@@ -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