on develop help sign in
Expanding primary usage documentation
commiteb15a71cd96660726e7e73b9c0426aa9427e3795 authorBrendan Hay <brendan.g.hay@gmail.com> dateThu 16 Jun 2016, 11:15 (10 years ago) parentca8d11a
13 files changed+336−144
core/src/Network/Google/Types.hs+21−3 examples/src/Example/Storage.hs+10−8 gogol-storage/gen/Network/Google/Resource/Storage/Objects/Insert.hs+2−2 gogol/gogol.cabal−1 gogol/src/Network/Google.hs+201−35 gogol/src/Network/Google/Auth/ApplicationDefault.hs+19−12 gogol/src/Network/Google/Auth/Device.hs−11 gogol/src/Network/Google/Auth/InstalledApplication.hs+8 gogol/src/Network/Google/Auth/Scope.hs+2−3 gogol/src/Network/Google/Auth/ServiceAccount.hs+7 gogol/src/Network/Google/Env.hs+40−40 gogol/src/Network/Google/Internal/Auth.hs+20−26 gogol/src/Network/Google/Internal/Body.hs+6−3
core/src/Network/Google/Types.hs+21−3
@@ -248,24 +248,42 @@ defaultService i h = ServiceConfig
248248 , _svcTimeout = Just 70
249249 }
250250
251+-- | The remote host name, used for both the IP address to connect to and the
252+-- host request header.
251253serviceHost :: Lens' ServiceConfig ByteString
252254serviceHost = lens _svcHost (\s a -> s { _svcHost = a })
253255
254−servicePath :: Lens' ServiceConfig Builder
255−servicePath = lens _svcPath (\s a -> s { _svcPath = a })
256−
256+-- | The remote port to connect to.
257+--
258+-- Defaults to @443@.
257259servicePort :: Lens' ServiceConfig Int
258260servicePort = lens _svcPort (\s a -> s { _svcPort = a })
259261
262+-- | A path prefix that is prepended to any sent HTTP request.
263+--
264+-- Defaults to @mempty@.
265+servicePath :: Lens' ServiceConfig Builder
266+servicePath = lens _svcPath (\s a -> s { _svcPath = a })
267+
268+-- | Whether to use HTTPS/SSL.
269+--
270+-- Defaults to @True@.
260271serviceSecure :: Lens' ServiceConfig Bool
261272serviceSecure = lens _svcSecure (\s a -> s { _svcSecure = a })
262273
274+-- | Number of seconds to wait for a response.
263275serviceTimeout :: Lens' ServiceConfig (Maybe Seconds)
264276serviceTimeout = lens _svcTimeout (\s a -> s { _svcTimeout = a })
265277
266278-- | A single part of a (potentially multipart) request body.
279+--
280+-- /Note:/ The 'IsString' instance defaults to a @text/plain@ MIME type.
267281data Body = Body !MediaType !RequestBody
268282
283+instance IsString Body where
284+ fromString = Body ("text" // "plain") . fromString
285+
286+-- | A lens into the 'MediaType' of a request 'Body'.
269287bodyContentType :: Lens' Body MediaType
270288bodyContentType = lens (\(Body m _) -> m) (\(Body _ b) m -> Body m b)
271289
examples/src/Example/Storage.hs+10−8
@@ -1,4 +1,5 @@
1−{-# LANGUAGE OverloadedStrings #-}
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
23
34-- |
45-- Module : Example.Storage
@@ -10,12 +11,13 @@
1011--
1112module Example.Storage where
1213
13−import Control.Lens
14−import Data.Text (Text)
15−import qualified Data.Text as Text
16−import Network.Google
17−import Network.Google.Storage
18−import System.IO
14+import Control.Lens ((&), (.~), (<&>), (?~))
15+import Data.Text (Text)
16+import Network.Google
17+import Network.Google.Storage
18+import System.IO (stdout)
19+
20+import qualified Data.Text as Text
1921
2022-- This will calculate the MIME type (and therefore Content-Type) of
2123-- the stored object based on the file extension.
@@ -31,7 +33,7 @@ import System.IO
3133example :: Text -> FilePath -> IO ()
3234example bkt f = do
3335 l <- newLogger Debug stdout
34− e <- newEnv <&> (envLogger .~ l) . allow storageReadWriteScope
36+ e <- newEnv <&> (envLogger .~ l) . (envScopes .~ storageReadWriteScope)
3537 b <- sourceBody f
3638
3739 let key = Text.pack f
gogol-storage/gen/Network/Google/Resource/Storage/Objects/Insert.hs+2−2
@@ -45,8 +45,8 @@ module Network.Google.Resource.Storage.Objects.Insert
4545 , oiProjection
4646 ) where
4747
48−import Network.Google.Prelude
49−import Network.Google.Storage.Types
48+import Network.Google.Prelude
49+import Network.Google.Storage.Types
5050
5151-- | A resource alias for @storage.objects.insert@ method which the
5252-- 'ObjectsInsert' request conforms to.
gogol/gogol.cabal−1
@@ -43,7 +43,6 @@ library
4343 , Network.Google.Auth
4444 , Network.Google.Auth.Scope
4545 , Network.Google.Auth.ApplicationDefault
46− , Network.Google.Auth.Device
4746 , Network.Google.Auth.InstalledApplication
4847 , Network.Google.Auth.ServiceAccount
4948 , Network.Google.Compute.Metadata
gogol/src/Network/Google.hs+201−35
@@ -20,39 +20,38 @@
2020-- Stability : provisional
2121-- Portability : non-portable (GHC extensions)
2222--
23−-- This module provides a simple 'Google' monad and a set of operations which
24−-- can be performed against remote Google Service APIs, for use with the types
25−-- supplied by the various @gogol-*@ libraries.
26−--
27−-- A 'MonadGoogle' typeclass is used as a function constraint to provide automatic
28−-- lifting of functions when embedding 'Google' as a layer inside your own
29−-- application stack.
23+-- This module provides a 'Google' monad and common set of operations which
24+-- can be performed against the remote Google Service APIs. Typically you will
25+-- import this module along with modules from various @gogol-*@ libraries
26+-- for the services you wish to communicate with.
3027module Network.Google
3128 (
3229 -- * Usage
3330 -- $usage
3431
35− -- * Running Google Actions
32+ -- * The Google Monad
3633 Google (..)
3734 , MonadGoogle (..)
3835 , runGoogle
3936 , runResourceT
4037
41− -- * Authentication and Environment
38+ -- * Environment
4239 , Env
4340 , HasEnv (..)
44− , newEnv
4541
46− -- ** Customized Environment
42+ , newEnv
4743 , newEnvWith
44+
45+ -- ** Credentials
46+ -- $credentials
47+
4848 , getApplicationDefault
4949
5050 -- ** Authorization
51− -- $auth
51+ -- $authorization
5252
53− , allow
54− , forbid
5553 , (!)
54+ , allow
5655
5756 , AllowScopes
5857 , type HasScope
@@ -60,27 +59,30 @@ module Network.Google
6059 -- * Sending Requests
6160 , send
6261
63− -- ** Media Downloads
62+ -- ** Streaming Media
6463 , download
65−
66− -- ** Media Uploads
6764 , upload
6865
69− , ToBody (..)
70− , RequestBody
66+ , Body (..)
67+ , bodyContentType
68+
7169 , sourceBody
70+ , getMIMEType
71+
72+ -- * Service Configuration
73+ -- $configuration
7274
73− -- ** Service Configuration
7475 -- ** Overriding Defaults
7576 , configure
7677 , override
77−
78− -- *** Scoped Actions
79− , reconfigure
8078 , timeout
8179
82− -- * Running Asynchronous Actions
83− -- $async
80+ -- ** Lenses
81+ , serviceHost
82+ , servicePort
83+ , servicePath
84+ , serviceSecure
85+ , serviceTimeout
8486
8587 -- * Handling Errors
8688 , AsError (..)
@@ -102,10 +104,13 @@ module Network.Google
102104 , newManager
103105 , tlsManagerSettings
104106
107+ -- * Running Asynchronous Actions
108+ -- $async
109+
110+ -- * Compute Metadata
111+ -- $metadata
112+
105113 -- * Re-exported Types
106− , OctetStream
107− , PlainText
108− , JSON
109114 , module Network.Google.Types
110115 ) where
111116
@@ -137,6 +142,9 @@ import Network.Google.Types
137142import Network.HTTP.Conduit (newManager, tlsManagerSettings)
138143import Network.HTTP.Media.MediaType (MediaType)
139144
145+-- | The 'Google' monad containing configuration environment and tracks
146+-- resource allocation via 'ResourceT'.
147+-- The functions in "Network.Google" are generalised
140148newtype Google s a = Google { unGoogle :: ReaderT (Env s) (ResourceT IO) a }
141149 deriving
142150 ( Functor
@@ -153,12 +161,16 @@ newtype Google s a = Google { unGoogle :: ReaderT (Env s) (ResourceT IO) a }
153161 , MonadResource
154162 )
155163
156−-- | Run a 'Google' actoin using the specified environment containing
157−-- credentials with sufficient authorization scopes.
164+-- | Run a 'Google' action using the specified environment and
165+-- credentials annotated with sufficient authorization scopes.
158166runGoogle :: (MonadResource m, HasEnv s r) => r -> Google s a -> m a
159167runGoogle e m = liftResourceT $ runReaderT (unGoogle m) (e ^. environment)
160168
161169-- | Monads in which 'Google' actions may be embedded.
170+--
171+-- The functions in "Network.Google" have 'MonadGoogle' constraints to provide
172+-- automatic lifting when embedding 'Google' as a layer inside your own
173+-- application stack.
162174class ( Functor m
163175 , Applicative m
164176 , Monad m
@@ -226,6 +238,12 @@ send x = liftGoogle $ do
226238-- a streaming media response. Use 'send' to retrieve the metadata and 'download'
227239-- to retrieve the streaming media.
228240--
241+-- Equivalent to:
242+--
243+-- @
244+-- 'send' . 'MediaDownload'
245+-- @
246+--
229247-- Throws 'Error'.
230248download :: ( MonadGoogle s m
231249 , HasScope s (MediaDownload a)
@@ -235,7 +253,13 @@ download :: ( MonadGoogle s m
235253 -> m (Rs (MediaDownload a))
236254download = send . MediaDownload
237255
238−-- | Send a request with an attached multipart/related media upload.
256+-- | Send a request with an attached <https://tools.ietf.org/html/rfc2387 multipart/related media> upload.
257+--
258+-- Equivalent to:
259+--
260+-- @
261+-- 'send' . 'MediaUpload'
262+-- @
239263--
240264-- Throws 'Error'.
241265upload :: ( MonadGoogle s m
@@ -251,11 +275,146 @@ hoistError :: MonadThrow m => Either Error a -> m a
251275hoistError = either (throwingM _Error) return
252276
253277{- $usage
254−Usage explanation and example placeholder.
278+
279+The request and response types provided by the various @gogol-*@ libraries can
280+be used with either 'send', 'upload', or 'download', depending upon the
281+request's purpose. Namely, 'send' is the function you will most commonly use to
282+send requests, with 'upload' and 'download' as convenience when dealing with
283+streaming requests and responses respectively.
284+
285+To get started we will need to specify our Google Service credentials and
286+create an 'Env' environment containing configuration which will be used by
287+'runGoogle' to perform any actions. Your Google 'Credentials' can be supplied
288+in a number of ways, by having Gogol retrieve
289+<https://developers.google.com/identity/protocols/application-default-credentials Application Default Credentials>
290+for use on Google App Engine and Google Compute Engine, or by explicitly
291+supplying your credentials. See the <#credentials Credentials> section for
292+information about supported credential mechanisms.
293+
294+The following example demonstrates uploading a file to Google
295+<https://cloud.google.com/storage/ Cloud Storage> using 'ObjectsInsert' from
296+<http://hackage.haskell.org/package/gogol-storage gogol-storage>:
297+
298+> import Control.Lens ((&), (.~), (<&>), (?~))
299+> import Data.Text (Text)
300+> import Network.Google
301+> import Network.Google.Storage
302+> import System.IO (stdout)
303+>
304+> import qualified Data.Text as Text
305+>
306+> example :: IO Object
307+> example = do
308+> lgr <- newLogger Debug stdout -- (1)
309+> env <- newEnv <&> (envLogger .~ lgr) . (envScopes .~ storageReadWriteScope) -- (2) (3)
310+> body <- sourceBody "/path/to/image.jpg" -- (4)
311+>
312+> let key = "image.jpg"
313+> bkt = "my-storage-bucket"
314+>
315+> runResourceT . runGoogle env $ -- (5)
316+> upload (objectsInsert bkt object' & oiName ?~ key) bkt
317+
318+Breaking down the above example, we have the following points of interest:
319+
320+1. A new 'Logger' to replace the default noop logger is created, set to print
321+ debug information and errors to 'stdout'.
322+
323+2. The 'Env' is created using 'newEnv'. This creates a new HTTP 'Manager' and
324+ retrieves the application default 'Credentials'.
325+
326+3. The lenses 'envLogger' and 'envScopes' are used to set the newly created
327+ 'Logger' and authorised OAuth2 scopes, respectively. Explicitly annotating the
328+ 'Env' with the scopes ensures that any mismatch between the remote
329+ operations performed in 'runGoogle' and the credential scopes are raised as
330+ errors at compile time.
331+ See the <#authorization Authorization> section for more information.
332+
333+4. The streaming 'body' for the object is retrieved from a 'FilePath', and the
334+ MIME type is calculated from the file extension.
335+ The MIME type is used as the object's @Content-Type@ in Cloud Storage, and
336+ can be overriden using the 'bodyContentType' lens as follows:
337+
338+ > import Network.HTTP.Media ((//))
339+ >
340+ > body <- sourceBody f <&> bodyContentType .~ "application" // "json"
341+
342+5. Finally, we run the 'Google' computation using @'runResourceT' . 'runGoogle'@
343+ which serialises the 'ObjectsInsert' type to a HTTP request and sets the streaming 'Body'.
344+ The resulting 'Object' metadata is then parsed from a successful HTTP response.
345+1
346+Additional examples can be found can be found in the
347+<https://github.com/brendanhay/gogol/tree/develop/examples Gogol> project's
348+source control.
349+
255350-}
256351
257−{- $auth
258−Authorization explanation and example placeholder.
352+{- $authorization #authorization#
353+Each request within a particular 'runGoogle' context requires specific
354+OAuth2 scopes to be have been authorized for the given credentials.
355+
356+For example, the Google Storage 'ObjectsInsert' has the associated scopes of:
357+
358+> type Scopes ObjectsInsert =
359+> '["https://www.googleapis.com/auth/cloud-platform",
360+> "https://www.googleapis.com/auth/devstorage.full_control",
361+> "https://www.googleapis.com/auth/devstorage.read_write"]
362+
363+Multiple differing requests within a given 'runGoogle' context will then require
364+the credentials to have a minimal set of these associated request scopes.
365+This authorization information is represented as a type-level set,
366+the 's' type parameter of 'Google' and 'MonadGoogle'. A mismatch
367+of the sent request scopes and the 'Env' credential scopes results in a informative
368+compile error.
369+
370+You can use 'allow' or the 'envScopes' lens to specify the 'Env's set of scopes.
371+The various @gogol-*@ libraries export their individual scopes from @Network.Google.*"
372+and you can use the '(!)' combinator to combine these into a larger set.
373+
374+For example:
375+
376+> import Control.Lens ((<&>), (.~))
377+> import Network.Google
378+> import Network.Google.Monitoring
379+>
380+> main :: IO ()
381+> main = do
382+> env <- newEnv <&> envScopes .~ (monitoringReadScope ! monitoringWriteScope ! computeReadOnlyScope)
383+> ...
384+
385+>>> :type env
386+Env '["https://www.googleapis.com/auth/monitoring.read", "https://www.googleapis.com/auth/monitoring.write", "https://www.googleapis.com/auth/compute.readonly"]
387+
388+-}
389+
390+{- $configuration
391+Each service has its own configuration such as host, port, path prefix, and timeout
392+which can be customized independent of other services.
393+It can be desirable to customize this when mocking service endpoints or adjusting
394+HTTP response timeouts for a specific request.
395+
396+For example, to point all calls to Google Compute to @https://localhost@ instead
397+of the actual remote endpoint, we can use @Control.Monad.Reader.local@ in conjunction
398+with 'override':
399+
400+> import Control.Lens ((&), (.~))
401+> import Control.Monad.Reader (local)
402+> import Network.Google
403+> import Network.Google.Compute
404+>
405+> local (override (computeService & serviceHost .~ "localhost")) $ do
406+> _ <- send $ instancesGet "project" "zone" "instance-id"
407+> ...
408+
409+-}
410+
411+{- $credentials #credentials#
412+If you wish to manually specify 'Credentials' via 'newEnvWith' you can use one
413+of the following supported credential mechanisms:
414+
415+* "Network.Google.Auth.InstalledApplication" - Applications installed on devices.
416+* "Network.Google.Auth.ServiceAccount" - Applications deployed to custom environments.
417+* "Network.Google.Auth.ApplicationDefault" - Applications deployed to App Engine (GAE) or Compute Engine (GCE).
259418-}
260419
261420{- $async
@@ -263,10 +422,17 @@ Requests can be sent asynchronously, but due to guarantees about resource closur
263422require the use of <http://hackage.haskell.org/package/lifted-async lifted-async>.
264423-}
265424
425+{- $metadata
426+
427+Google Compute metadata can be retrieve when running on GCE instances.
428+See the documentation in "Network.Google.Compute.Metadata" for the available
429+functions.
430+-}
431+
266432{- $logging
267433The exposed logging interface is a primitive 'Logger' function which gets
268434threaded through service calls and serialisation routines. This allows the
269−library to output useful information and diagnostics.
435+consuming library to output useful information and diagnostics.
270436
271437The 'newLogger' function can be used to construct a simple logger which writes
272438output to a 'Handle', but in most production code you should probably consider
gogol/src/Network/Google/Auth/ApplicationDefault.hs+19−12
@@ -9,6 +9,13 @@
99-- Stability : provisional
1010-- Portability : non-portable (GHC extensions)
1111--
12+-- Application Default Credentials are suited for cases when access to a Google service
13+-- needs to have the same identity and authorization level for the application
14+-- independent of the user. This is Google\'s the recommended approach to authorize
15+-- calls to Google Cloud APIs, particularly when you're building an application
16+-- that is deployed to Google App Engine or Google Compute Engine virtual machines.
17+--
18+-- /See:/ <https://developers.google.com/identity/protocols/application-default-credentials Application Default Documentation>.
1219module Network.Google.Auth.ApplicationDefault where
1320
1421import Control.Applicative
@@ -23,13 +30,11 @@ import qualified Data.Text as Text
2330import Network.Google.Compute.Metadata (isGCE)
2431import Network.Google.Internal.Auth
2532
26−
27−import Network.HTTP.Conduit (Manager)
28−import System.Directory (doesFileExist,
29− getHomeDirectory)
30−import System.Environment (lookupEnv)
31−import System.FilePath ((</>))
32−import System.Info (os)
33+import Network.HTTP.Conduit (Manager)
34+import System.Directory (doesFileExist, getHomeDirectory)
35+import System.Environment (lookupEnv)
36+import System.FilePath ((</>))
37+import System.Info (os)
3338
3439-- | The environment variable name which is used to specify the directory
3540-- containing the @application_default_credentials.json@ generated by @gcloud init@.
@@ -57,23 +62,25 @@ defaultCredentialsFile = "GOOGLE_APPLICATION_CREDENTIALS"
5762defaultCredentialsPath :: MonadIO m => m (Maybe FilePath)
5863defaultCredentialsPath = liftIO (lookupEnv defaultCredentialsFile)
5964
60−-- | Attempt credentials discovery via the following steps:
65+-- | Performs credentials discovery in the following order:
6166--
62−-- * Read the default credentials from a file specified by
67+-- 1. Read the default credentials from a file specified by
6368-- the environment variable @GOOGLE_APPLICATION_CREDENTIALS@ if it exists.
6469--
65−-- * Read the platform equivalent of @~\/.config\/gcloud\/application_default_credentials.json@ if it exists.
70+-- 2. Read the platform equivalent of @~\/.config\/gcloud\/application_default_credentials.json@ if it exists.
6671-- The @~/.config@ component of the path can be overriden by the environment
6772-- variable @CLOUDSDK_CONFIG@ if it exists.
6873--
69−-- * Retrieve the default service account application credentials if
74+-- 3. Retrieve the default service account application credentials if
7075-- running on GCE. The environment variable @NO_GCE_CHECK@ can be used to
7176-- skip this check if set to a truthy value such as @1@ or @true@.
7277--
7378-- The specified 'Scope's are used to authorize any @service_account@ that is
74−-- found with the appropriate scopes, otherwise they are not used. See the
79+-- found with the appropriate OAuth2 scopes, otherwise they are not used. See the
7580-- top-level module of each individual @gogol-*@ library for a list of available
7681-- scopes, such as @Network.Google.Compute.computeScope@.
82+--
83+-- /See:/ <https://developers.google.com/identity/protocols/application-default-credentials Application Default Credentials>
7784getApplicationDefault :: (MonadIO m, MonadCatch m)
7885 => Manager
7986 -> m (Credentials s)
gogol/src/Network/Google/Auth/Device.hsdeleted−11
@@ -1,11 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−
3−-- |
4−-- Module : Network.Google.Auth.Device
5−-- Copyright : (c) 2015-2016 Brendan Hay
6−-- License : Mozilla Public License, v. 2.0.
7−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
8−-- Stability : provisional
9−-- Portability : non-portable (GHC extensions)
10−--
11−module Network.Google.Auth.Device where
gogol/src/Network/Google/Auth/InstalledApplication.hs+8
@@ -8,6 +8,14 @@
88-- Stability : provisional
99-- Portability : non-portable (GHC extensions)
1010--
11+-- Credentials for applications that are installed on devices such as
12+-- computers, cell phones, or a tablet. Installed apps are distributed to
13+-- individual machines, and it is assumed that these apps securely store secrets.
14+--
15+-- These apps might access a Google service while the user is present at the
16+-- application, or when the application is running in the background.
17+--
18+-- /See:/ <https://developers.google.com/identity/protocols/OAuth2InstalledApp Installed Application Documentation>.
1119module Network.Google.Auth.InstalledApplication where
1220
1321import Control.Monad.Catch (MonadCatch)
gogol/src/Network/Google/Auth/Scope.hs+2−3
@@ -36,12 +36,11 @@ import Network.Google.Prelude (GoogleRequest (..),
3636import Network.HTTP.Types (urlEncode)
3737
3838-- | Annotate credentials with the specified scopes.
39−--
4039-- This exists to allow users to choose between using 'getCredentials'
41−-- with a 'Proxy' type constructed via use of '(!)', or explicitly
40+-- with a 'Proxy' type constructed via use of '!', or explicitly
4241-- specifying the scopes in a type annotation.
4342--
44−-- /See:/ '!' and the related scopes available for each service.
43+-- /See:/ '!' and the scopes available for each service.
4544allow :: proxy s -> k s -> k s
4645allow _ = id
4746
gogol/src/Network/Google/Auth/ServiceAccount.hs+7
@@ -8,6 +8,13 @@
88-- Stability : provisional
99-- Portability : non-portable (GHC extensions)
1010--
11+-- Credentials for Server to Server communication, such as between a deployed web
12+-- application and a Google service.
13+--
14+-- Typically, an application uses a service account when the application uses
15+-- Google services to work with its own data rather than a specific user's data.
16+--
17+-- /See:/ <https://developers.google.com/identity/protocols/OAuth2ServiceAccount Service Account Documentation>.
1118module Network.Google.Auth.ServiceAccount where
1219
1320import Control.Monad.Catch
gogol/src/Network/Google/Env.hs+40−40
@@ -3,6 +3,7 @@
33{-# LANGUAGE FunctionalDependencies #-}
44{-# LANGUAGE KindSignatures #-}
55{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
67
78-- |
89-- Module : Network.Google.Env
@@ -15,17 +16,18 @@
1516-- Environment and Google specific configuration for the 'Network.Google' monad.
1617module Network.Google.Env where
1718
18−import Control.Lens (Lens', lens, (<>~), (?~))
19−import Control.Monad.Catch (MonadCatch)
20−import Control.Monad.IO.Class (MonadIO (..))
21−import Control.Monad.Reader (MonadReader (local))
22−import Data.Function (on)
23−import Data.Monoid (Dual (..), Endo (..))
24−import GHC.TypeLits (Symbol)
25−import Network.Google.Auth
26−import Network.Google.Internal.Logger (Logger)
27−import Network.Google.Types
28−import Network.HTTP.Conduit
19+import Control.Lens (Lens', lens, (<>~), (?~))
20+import Control.Monad.Catch (MonadCatch)
21+import Control.Monad.IO.Class (MonadIO (..))
22+import Control.Monad.Reader (MonadReader (local))
23+import Data.Function (on)
24+import Data.Monoid (Dual (..), Endo (..))
25+import Data.Proxy (Proxy (..))
26+import GHC.TypeLits (Symbol)
27+import Network.Google.Auth
28+import Network.Google.Internal.Logger (Logger)
29+import Network.Google.Types
30+import Network.HTTP.Conduit (Manager, newManager, tlsManagerSettings)
2931
3032-- | The environment containing the parameters required to make Google requests.
3133data Env (s :: [Symbol]) = Env
@@ -54,62 +56,59 @@ class HasEnv s a | a -> s where
5456 -- | The credential store used to sign requests for authentication with Google.
5557 envStore :: Lens' a (Store s)
5658
59+ -- | The authorised OAuth2 scopes.
60+ --
61+ -- /See:/ 'allow', '!', and the related scopes available for each service.
62+ envScopes :: Lens' a (Proxy s)
63+
5764 envOverride = environment . lens _envOverride (\s a -> s { _envOverride = a })
5865 envLogger = environment . lens _envLogger (\s a -> s { _envLogger = a })
5966 envManager = environment . lens _envManager (\s a -> s { _envManager = a })
6067 envStore = environment . lens _envStore (\s a -> s { _envStore = a })
68+ envScopes = environment . lens (\_ -> Proxy :: Proxy s) (flip allow)
6169
6270instance HasEnv s (Env s) where
6371 environment = id
6472
6573-- | Provide a function which will be added to the existing stack
66−-- of overrides applied to all service configuration.
74+-- of overrides which are applied to all service configurations.
6775--
68−-- To override a specific service, it's suggested you use
69−-- either 'configure' or 'reconfigure' with a modified version of the default
70−-- service, such as @Network.Google.Gmail.gmailService@.
71−override :: HasEnv s a => (ServiceConfig -> ServiceConfig) -> a -> a
72−override f = envOverride <>~ Dual (Endo f)
76+-- /See:/ 'override'.
77+configure :: HasEnv s a => (ServiceConfig -> ServiceConfig) -> a -> a
78+configure f = envOverride <>~ Dual (Endo f)
7379
74−-- | Configure a specific service. All requests belonging to the
80+-- | Configure a specific 'Service'. All requests belonging to the
7581-- supplied service will use this configuration instead of the default.
7682--
77−-- It's suggested you use a modified version of the default service, such
78−-- as @Network.Google.Gmail.gmailService@.
83+-- It's suggested you use a modified version of the default 'Service', such as:
84+--
85+-- @
86+-- override (Network.Google.Gmail.gmailService & serviceHost .~ "localhost") env
87+-- @
7988--
80−-- /See:/ 'reconfigure'.
81−configure :: HasEnv s a => ServiceConfig -> a -> a
82−configure s = override f
89+-- /See:/ 'configure'.
90+override :: HasEnv s a => ServiceConfig -> a -> a
91+override s = configure f
8392 where
8493 f x | on (==) _svcId s x = s
8594 | otherwise = x
8695
87−-- | Scope an action such that all requests belonging to the supplied service
88−-- will use this configuration instead of the default.
89−--
90−-- It's suggested you use a modified version of the default service, such
91−-- as @Network.Google.Gmail.gmailService@.
92−--
93−-- /See:/ 'configure'.
94−reconfigure :: (MonadReader r m, HasEnv s r) => ServiceConfig -> m a -> m a
95−reconfigure = local . configure
96−
9796-- | Scope an action such that any HTTP response will use this timeout value.
9897--
9998-- Default timeouts are chosen by considering:
10099--
101100-- * This 'timeout', if set.
102101--
103−-- * The related 'Service' timeout for the sent request if set. (Usually 70s)
102+-- * The related 'Service' timeout for the sent request if set. (Default 70s)
104103--
105−-- * The 'envManager' timeout if set.
104+-- * The 'envManager' timeout, if set.
106105--
107−-- * The default 'ClientRequest' timeout. (Approximately 30s)
106+-- * The 'ClientRequest' timeout. (Default 30s)
108107timeout :: (MonadReader r m, HasEnv s r) => Seconds -> m a -> m a
109−timeout s = local (override (serviceTimeout ?~ s))
108+timeout s = local (configure (serviceTimeout ?~ s))
110109
111−-- | Creates a new environment with a new 'Manager', without logging.
112−-- Credentials are determined by calling 'getApplicationDefault'.
110+-- | Creates a new environment with a newly initialized 'Manager', without logging.
111+-- and Credentials that are determined by calling 'getApplicationDefault'.
113112-- Use 'newEnvWith' to supply custom credentials such as an 'OAuthClient'
114113-- and 'OAuthCode'.
115114--
@@ -117,7 +116,6 @@ timeout s = local (override (serviceTimeout ?~ s))
117116-- found with the appropriate scopes. See the top-level module of each individual
118117-- @gogol-*@ library for a list of available scopes, such as
119118-- @Network.Google.Compute.authComputeScope@.
120−--
121119-- Lenses from 'HasEnv' can be used to further configure the resulting 'Env'.
122120--
123121-- /See:/ 'newEnvWith', 'getApplicationDefault'.
@@ -127,6 +125,8 @@ newEnv = do
127125 c <- getApplicationDefault m
128126 newEnvWith c (\_ _ -> pure ()) m
129127
128+-- | Create a new environment.
129+--
130130-- /See:/ 'newEnv'.
131131newEnvWith :: (MonadIO m, MonadCatch m, AllowScopes s)
132132 => Credentials s
gogol/src/Network/Google/Internal/Auth.hs+20−26
@@ -75,15 +75,13 @@ data Credentials (s :: [Symbol])
7575{-| Service Account credentials which are typically generated/download
7676from the Google Developer console of the following form:
7777
78−@
79−{
80− \"type\": \"service_account\",
81− \"private_key_id\": \"303ad77e5efdf2ce952DFa\",
82− \"private_key\": \"-----BEGIN PRIVATE KEY-----\n...\n\",
83− \"client_email\": \"email@serviceaccount.com\",
84− \"client_id\": \"035-2-310.useraccount.com\"
85−}
86−@
78+> {
79+> \"type\": \"service_account\",
80+> \"private_key_id\": \"303ad77e5efdf2ce952DFa\",
81+> \"private_key\": \"-----BEGIN PRIVATE KEY-----\n...\n\",
82+> \"client_email\": \"email@serviceaccount.com\",
83+> \"client_id\": \"035-2-310.useraccount.com\"
84+> }
8785
8886The private key is used to sign a JSON Web Token (JWT) of the
8987grant_type @urn:ietf:params:oauth:grant-type:jwt-bearer@, which is sent to
@@ -115,14 +113,12 @@ instance FromJSON ServiceAccount where
115113{-| Authorized User credentials which are typically generated by the Cloud SDK
116114Tools such as @gcloud init@, of the following form:
117115
118−@
119−{
120− \"type\": \"authorized_user\",
121− \"client_id\": \"32555940559.apps.googleusercontent.com\",
122− \"client_secret\": \"Zms2qjJy2998hD4CTg2ejr2\",
123− \"refresh_token\": \"1/B3gM1x35v.VtqffS1n5w-rSJ\"
124−}
125−@
116+> {
117+> \"type\": \"authorized_user\",
118+> \"client_id\": \"32555940559.apps.googleusercontent.com\",
119+> \"client_secret\": \"Zms2qjJy2998hD4CTg2ejr2\",
120+> \"refresh_token\": \"1/B3gM1x35v.VtqffS1n5w-rSJ\"
121+> }
126122
127123The secret and refresh token are used to obtain a valid 'OAuthToken' from
128124'accountsURL' using grant_type @refresh_token@.
@@ -147,15 +143,13 @@ data OAuthClient = OAuthClient
147143
148144{-| An OAuth bearer type token of the following form:
149145
150−@
151−{
152− \"token_type\": \"Bearer\",
153− \"access_token\": \"eyJhbGci...\",
154− \"refresh_token\": \"1/B3gq9K...\",
155− \"expires_in\": 3600,
156− ...
157−}
158−@
146+> {
147+> \"token_type\": \"Bearer\",
148+> \"access_token\": \"eyJhbGci...\",
149+> \"refresh_token\": \"1/B3gq9K...\",
150+> \"expires_in\": 3600,
151+> ...
152+> }
159153
160154The '_tokenAccess' field will be inserted verbatim into the
161155@Authorization: Bearer ...@ header for all HTTP requests.
gogol/src/Network/Google/Internal/Body.hs+6−3
@@ -15,7 +15,7 @@ import Data.Conduit.Binary (sourceFile)
1515import Data.Maybe (fromMaybe)
1616import qualified Data.Text as Text
1717import Network.Google.Types (Body (..))
18−import Network.HTTP.Conduit (RequestBody, requestBodySource)
18+import Network.HTTP.Conduit (requestBodySource)
1919import Network.HTTP.Media (MediaType, parseAccept, (//))
2020import qualified Network.Mime as MIME
2121import System.IO
@@ -24,6 +24,9 @@ import System.IO
2424getFileSize :: MonadIO m => FilePath -> m Integer
2525getFileSize f = liftIO (withBinaryFile f ReadMode hFileSize)
2626
27+-- | Attempt to calculate the MIME type based on file extension.
28+--
29+-- Defaults to @application/octet-stream@ if no file extension is recognised.
2730getMIMEType :: FilePath -> MediaType
2831getMIMEType =
2932 fromMaybe ("application" // "octet-stream")
@@ -34,8 +37,8 @@ getMIMEType =
3437
3538-- | Construct a 'Body' from a 'FilePath'.
3639--
37−-- This attempts to guess the MIME type from the file extension,
38−-- you can use "Network.Google.Types.bodyContentType" to set a MIME type explicitly.
40+-- This uses 'getMIMEType' to calculate the MIME type from the file extension,
41+-- you can use 'bodyContentType' to set a MIME type explicitly.
3942sourceBody :: MonadIO m => FilePath -> m Body
4043sourceBody f = do
4144 n <- getFileSize f