haste-gapi (empty) → 0.1.0.0
raw patch · 13 files changed
+912/−0 lines, 13 filesdep +basedep +data-defaultdep +haste-libsetup-changed
Dependencies added: base, data-default, haste-lib, transformers
Files
- Haste/GAPI.hs +206/−0
- Haste/GAPI/GPlus.hs +119/−0
- Haste/GAPI/GPlus/Person.hs +17/−0
- Haste/GAPI/Internals/Promise.hs +46/−0
- Haste/GAPI/Request.hs +76/−0
- Haste/GAPI/Request/Raw.hs +34/−0
- Haste/GAPI/Request/RequestM.hs +59/−0
- Haste/GAPI/Request/Types.hs +97/−0
- Haste/GAPI/Result.hs +132/−0
- Haste/GAPI/Types.hs +43/−0
- LICENSE +20/−0
- Setup.hs +2/−0
- haste-gapi.cabal +61/−0
+ Haste/GAPI.hs view
@@ -0,0 +1,206 @@+{-# LANGUAGE OverloadedStrings #-}++{-|+Module : Haste.GAPI+Description : Entry Module for haste-gapi+Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Example of a login to the Google API. Note that @config@ is defined+elsewhere.++@++main :: IO ()+main = withGAPI config $ \token -> case token of+ OA2Success {} ->+ putStrLn $ show token++ OA2Error {errorMsg = e} ->+ putStrLn $ "Your success token is invalid (" ++ e ++ ")"++@++The login starts with the @withGAPI@, which will load the Google API+environment. If the script is compiled with @--onexec@ flag, the load will be+significally faster as we don't have to wait until the script has fully loaded+until we download the Google API hooks.++When loading is done, a @OAuth2Token@ is generated, and we may inspect it+to see wether the login is successful or not. On success, we continue with+showing the token. If not, we print an error instead. Note that the+@withGAPI@ function ought to only be invoked once.+++Example of a request:++@++greet :: IO ()+greet = runR $ do+ response <- request "plus\/v1\/people\/me" def+ Just [name, pic] \<- sequence <$\> mapM (lookupVal response) [+ "result.displayName",+ "result.image.url"]+ liftIO . putStrLn $ "Hello " ++ name ++ "! \<br /\>"+ ++ "You look like this: \<br \/\>\<img src='"++ pic ++ "' \/\>"++@++The reqeuest above will greet the user by name, fetched from the Google+ API.+A response is generated by taking a Path and some Params to the @request@+function, which will execute it and return a response.++After that we look up some fields in the result, namely /result.displayName/+and /result.image.url/. The first will be bound to name, and the second to+pic. These two elements are both strings, by Haste.Foreign magic.++After that we use @RequestM@s @liftIO@ (as it's an instance of the MonadIO+class) and present a pretty HTML string!+-}+module Haste.GAPI (+ -- | = Connecting to the Google API+ withGAPI,+ oa2success,+ getToken,+ Config(..),+ OAuth2Token(..),+ -- | = Creating Requests+ module Haste.GAPI.Request,+ -- | = Handling Results + module Haste.GAPI.Result,+ -- | = Common types when working with Google API libraries+ module Haste.GAPI.Types++ ) where ++import Haste.Foreign hiding (get, has, hasAll)+import qualified Haste.Foreign as FFI++-- GHC 7.8 compatibility+import Data.Functor ((<$>))++import Haste.GAPI.Request +import Haste.GAPI.Result+import Haste.GAPI.Types+import Data.Default+-- import Control.Monad+import Control.Applicative++-- Datatypes -----------------------------------------------------------------++-- | Google API configuration. For an in-detail description of what each field+-- does, please see the <https://developers.google.com/api-client-library/javascript/reference/referencedocs Google API Reference>,+-- especially the methods @gapi.auth.authorize@ and @gapi.client.setApiKey@.+data Config = Config {+ -- | Client ID to generate an authentification token from. + clientID :: String,+ -- | The API key for your application+ apiKey :: String,+ -- | Here you enter the availiable scopes for your application.+ scopes :: String,+ -- | If true, the token an attempt will be made to refresh it behind the+ -- cenes+ immediate :: Bool + }+ +instance Show Config where+ show (Config cid key scopes' imm)+ = "\nConfig: " ++ concatMap (++ "\n\t") [cid, key, scopes', show imm]++instance ToAny Config where+ toAny cfg = toObject [("clientID", toAny $ clientID cfg),+ ("apiKey", toAny $ apiKey cfg),+ ("scopes", toAny $ scopes cfg),+ ("immediate", toAny $ immediate cfg)]++-- | OAuth2Token, the authentication tokens used by the Google API.+data OAuth2Token = OA2Success {+ -- | Authenticated access token + accessToken :: String,+ -- | Expiration of the token + expiresIn :: String,+ -- | Google API Scopes related to this token.+ state :: String+ }+ | OA2Error {+ errorMsg :: String,+ state :: String+ }++instance Show OAuth2Token where+ show t = if oa2success t+ then "Success Token: '" ++ shorten (accessToken t)+ ++ "'\n\t for scopes: '" ++ state t+ ++ "'\n\t expires in: "++ expiresIn t ++ "s"+ else "Failure Token: " ++ errorMsg t+ where shorten :: String -> String+ shorten str | length str < 16 = str+ | otherwise = take 32 str ++ "..."+++instance FromAny OAuth2Token where+ fromAny oa2 = do+ success <- FFI.has oa2 "access_token"+ if success+ then OA2Success <$> FFI.get oa2 "access_token"+ <*> FFI.get oa2 "expires_in"+ <*> FFI.get oa2 "state"+ else OA2Error <$> FFI.get oa2 "error"+ <*> FFI.get oa2 "state"+ +-- Exported functions --------------------------------------------------------+-- | Returns true if the token represents a successful authentication+oa2success :: OAuth2Token -> Bool+oa2success OA2Success {} = True+oa2success _ = False++-- | Loads the Google API, inserts Google API headers and then executes+-- an action. +withGAPI :: Config -> (OAuth2Token -> IO ()) -> IO ()+withGAPI cfg handler = do+ loadGAPI cfg handler+ loadGAPIExternals "GAPILoader"++-- | Exports and coordinate loading of the Google API.+loadGAPI :: Config -> (OAuth2Token -> IO ()) -> IO ()+loadGAPI = loadGAPI' "GAPILoader"++-- | Loads the Google API with a custom loader name+loadGAPI' :: String -> Config -> (OAuth2Token -> IO ()) -> IO ()+loadGAPI' symbol cfg handler+ = exportLoaderSymbol symbol $ loadClient cfg $ auth cfg handler++-- | Returns the token from the current Google API state+getToken :: IO OAuth2Token+getToken = ffi "(function() {return gapi.auth.getToken();})"++-- | Loads the GAPI Client +loadClient :: Config -> IO () -> IO ()+loadClient = ffi "(function(cfg, auth){\+\gapi.client.setApiKey(cfg.apiKey); \+\window.setTimeout(auth, 1);})"++-- | Authenticates the user. Should be invoked by loadClient+auth :: Config -> (OAuth2Token -> IO ()) -> IO ()+auth = ffi "(function(cfg, ah)\+\{gapi.auth.authorize({\+ \'client_id': cfg.clientID, \+ \'scope': cfg.scopes, \+ \'immediate': cfg.immediate}, \+\ah);})"++-- | Export the loader symbol +exportLoaderSymbol :: String -> IO () -> IO ()+exportLoaderSymbol = ffi "(function(s, f) {window[s] = f;})"++-- | Loads the external GAPI scripts+loadGAPIExternals :: String -> IO ()+loadGAPIExternals = ffi "(function(sym) {\+\var s = document.createElement('script');\+\s.setAttribute('src', 'https://apis.google.com/js/client.js?onload=' + sym);\+\s.setAttribute('type', 'text/javascript');\+\document.head.appendChild(s);})"
+ Haste/GAPI/GPlus.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Haste.GAPI.GPlus+Description : Google+ API bindings for haste-gapi +Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Contains default function and result mappings for the Google+ API.+-}+module Haste.GAPI.GPlus (+ peopleGet,+ peopleSearch,+ peopleListByActivity,+ peopleList,+ -- | = Google+ datatypes:+ + -- | == Google+ Person+ module Haste.GAPI.GPlus.Person+ + ) where+++import Haste.GAPI.Types+import Haste.GAPI.GPlus.Person+import Haste.GAPI.Request+import Haste.JSString as J +import Haste (JSString)++-- | Fetches a user by ID+peopleGet :: UserID -> RequestM (Result Person)+peopleGet uid = request' $ "plus/v1/people/" `append` uid++-- | Search after users given a query string+--+-- Optional parameters:+-- +-- [@language@] @language@ to search with. See Google API documentation+-- for valid language codes +-- +-- [@maxResult@] Maximum number of people to include in the response.+-- Acceptable values of @maxResult@ ranges between 1-50,+-- default is 25.+--+-- [@pageToken@] Token used for pagination in large result sets. +peopleSearch :: JSString -> Params -> RequestM [Result Person]+peopleSearch query ps = do+ r <- request "plus/v1/people" $ ("query", query) `pcons` ps+ r' <- get r "items"+ childs <- children r'+ case childs of+ Just childs' -> return childs'+ Nothing -> fail "peopleSearch: Child was not found!"+ +-- | List users by activity+--+-- Valid collections are "@plusoners@" and "@resharers@".+--+-- Optional parameters:+-- +-- [@maxResult@] Maximum number of people to include in the response.+-- Acceptable values of @maxResult@ ranges between 1-100,+-- default is 20.+--+-- [@pageToken@] Token used for pagination in large result sets. +peopleListByActivity :: ActivityID -> Collection -> Params+ -> RequestM [Result Person]+peopleListByActivity actId col ps = do+ r <- request (J.concat ["plus/v1/activities/", actId, "/people/", col]) ps+ r' <- get r "items"+ childs <- children r'+ case childs of+ Just childs' -> return childs'+ Nothing -> fail "peopleSearch: Child was not found!"++++-- | List people in a specific collection+--+-- Accepted collections are:+--+-- [@connected@] The list of visible people in the authenticated user's+-- circles who also use the requesting app. This list+-- is limited to users who made their app activities visible+-- to the authenticated user.+--+-- [@visible@] The list of people who this user has added to one or more+-- circles, limited to the circles visible to the requesting+-- application.+-- +-- Optional parameters:+-- +-- [@maxResult@] Maximum number of people to include in the response.+-- Acceptable values of @maxResult@ ranges between 1-100,+-- default is 100.+--+-- [@orderBy@] The order to return people in. Valid inputs are+-- "@alphabetical@" and "@best@"+--+-- [@pageToken@] Token used for pagination in large result sets. +peopleList :: UserID -> Collection -> Params -> RequestM [Result Person]+peopleList uid c ps = do+ r <- request (J.concat ["plus/v1/people/", uid, "/people/", c]) ps+ r' <- get r "items"+ childs <- children r'+ case childs of+ Just childs' -> return childs'+ Nothing -> fail "peopleSearch: Child was not found!"+ ++++++++
+ Haste/GAPI/GPlus/Person.hs view
@@ -0,0 +1,17 @@+{-|+Module : Haste.GAPI.GPlus.Person+Description : Functions for working on persons +Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Functions which maps to the Google API +-}+module Haste.GAPI.GPlus.Person where++-- | Haste.GAPI.Result type parameter+data Person ++
+ Haste/GAPI/Internals/Promise.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Haste.GAPI.Internals.Promise+Description : Mapping to Google API promises+Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Wraps Promises as implemented in Google API with a haskell representation. +-}+module Haste.GAPI.Internals.Promise where++import Haste+import Haste.Foreign++-- | An argument to a fulfilled promise+type Response = JSAny+-- | An argument to a rejected promise +type Reason = JSAny++-- | A promise is either a true promise or a callback+-- (a default error handler will be provided)+data Promise = Promise (Response -> IO ()) (Reason -> IO ())+ | Callback (Response -> IO ())++-- | Transform a promise to an any (for application)+instance ToAny Promise where+ toAny (Promise thn err) = toObject [("then", toAny thn),+ ("error", toAny err)]+ toAny (Callback cbk) = toObject [("then", toAny cbk),+ ("error", toAny gapiError)]++-- | Apply a promise to a JSAny +applyPromise :: JSAny -> Promise -> IO ()+applyPromise = ffi "(function(action, p) {action.then(p.then, p.error);})"++-- | Default error handler in promises+gapiError :: (JSString -> IO ()) -> Reason -> IO ()+gapiError action reason+ = do msg <- lookupAny reason "result.error.message"+ case msg of+ Just m -> do str <- fromAny m+ action str+ Nothing -> putStrLn "Malformed response!"
+ Haste/GAPI/Request.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Haste.GAPI.Request+Description : Contains everything Request-related +Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Contains RequestM and run functions.++-}+module Haste.GAPI.Request (+ RequestM (..),+ Request (..),+ Result(..),+ Path (..),+ runR, runRConc,+ request, request', + has, get, hasAll, val, valOf, children,+ lookupResult, lookupVal,+ -- | Perform an IO Action inside RequestM + liftIO,+ -- | Perform a CIO Action inside RequestM + liftConc, + -- | === Constructing parameters+ Params(),+ parp, merge, pcons, params+ ) where++import Haste.GAPI.Internals.Promise+import Haste.GAPI.Request.RequestM+import Haste.GAPI.Request.Types (+ Path,+ Params(..), Request(..),+ merge, pcons, params,+ rawRequest)+import Haste.GAPI.Request.Raw+import Haste.GAPI.Result+import qualified Haste.JSString as J++import Haste.Foreign hiding (has, get, hasAll)+import Haste.Concurrent ++import Data.Default +import Control.Monad+import Control.Applicative++-- | Runs the request eDSL +runR :: RequestM () -> IO ()+runR = concurrent . void . unR++-- | Runs the request eDSL from a concurrent context+runRConc :: RequestM () -> CIO ()+runRConc = void . unR++-- | Creates a request from an API path and a series of parameters.+request :: Path -> Params -> RequestM (Result a)+request p ps = customRequest (rawRequest p ps)++-- | Performs a request from an API path but without parameters+request' :: Path -> RequestM (Result a)+request' p = customRequest (rawRequest p def)++-- | Creates a request using a custom request+customRequest :: Request -> RequestM (Result a)+customRequest req = do+ v <- newEmptyMVar+ liftConc . fork . liftIO $ do+ resp <- jsCreateRequest (path req) (toAny $ rparams req)+ applyPromise resp $ Promise (concurrent . putMVar v . Right . toResult)+ (\_r -> concurrent . putMVar v . Left . J.pack $ + "Request error: " ++ show req)+ Req $ takeMVar v+
+ Haste/GAPI/Request/Raw.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Haste.GAPI.Request.Raw+Description : JavaScript functions needed by Request handling. +Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Contains JavaScript foreign functions for use with the request handler.+-}++module Haste.GAPI.Request.Raw where++import Haste+import Haste.Foreign+import Haste.GAPI.Internals.Promise++-- | Creates a request object +jsCreateRequest :: JSString -> JSAny -> IO JSAny+jsCreateRequest = ffi "(function(p, ps) {\+\return gapi.client.request({'path': p, 'params': ps})\+\})"++-- | Executes a request and performs the given action as a continuation+jsExecuteRequestThen :: JSString -> JSAny -> (Response -> IO ()) -> IO ()+jsExecuteRequestThen = ffi "(function(p, ps, callback) {\+\ gapi.client.request({'path': p, 'params': ps}).then({\+\'then': function(resp) {callback(resp);}, \+\'error': function(err) {console.debug(err);}\+\});\+\})"+
+ Haste/GAPI/Request/RequestM.hs view
@@ -0,0 +1,59 @@+{-|+Module : Haste.GAPI.Request.RequestM+Description : Monad for use in Requests+Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Contains the Request Monad with all its instances +-}+module Haste.GAPI.Request.RequestM (+ Error, + RequestM(..),+ liftIO+ ) where++import Haste+import Control.Monad+import Data.Functor+import Haste.Concurrent+import Control.Monad.IO.Class +import Control.Applicative+import qualified Haste.JSString as J++-- | A default error type for RequestM, will be customisable in the future. +type Error = JSString++-- | An eDSL for requests. +newtype RequestM a = Req {unR :: CIO (Either Error a)}++-- | RequestM can perform CIO actions+instance MonadConc RequestM where+ liftConc a = Req $ a >>= return . Right+ fork (Req c) = Req $ (fork $ void c) >>= return . Right++-- | RequestM can perform IO actions+instance MonadIO RequestM where+ liftIO a = Req $ liftIO a >>= return . Right++-- | RequestM is a monad+instance Monad RequestM where+ fail err = Req . return . Left $ J.pack err+ return a = Req . return $ Right a+ (Req a) >>= f = Req $ do+ a' <- a+ case a' of+ Right good -> unR $ f good+ Left bad -> do+ return $ Left bad++-- | Applicative is defined so that it builds under GHC 7.8 +instance Applicative RequestM where+ (<*>) = ap+ pure = return+ +-- | Functor is defined so that it builds under GHC 7.8 +instance Functor RequestM where+ fmap f m = m >>= return . f
+ Haste/GAPI/Request/Types.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Haste.GAPI.Request.Types+Description : Types used in requests. +Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Contains default types and functions to work on them. +-}+++module Haste.GAPI.Request.Types where++-- GHC 7.8 compatibility+import Data.Functor ((<$>))++import Haste+import Haste.Foreign+import Data.Default+import qualified Haste.JSString as J++-- | Returns an Anys children as a Key-value array+getKV :: JSAny -> IO [(JSString, JSString)]+getKV = ffi "(function(obj) {\+\var out = [];\+\for(i in obj) { out.push([i, obj[i] ]); }\+\return out;\+\})"++-- | Path for requests+type Path = JSString++-- | Parameters for a GAPI request+data Params = Params [(JSString, JSString)]+ deriving Show++-- | Params can be constructed from a JSAny +instance FromAny Params where+ fromAny a = Params <$> getKV a++-- | Params can be converted to a JSAny +instance ToAny Params where+ toAny (Params ps) = let objField (k,v) = (k, toAny $ v)+ in toObject $ map objField ps ++-- | Empty Parameters as a default instance+instance Default Params where+ def = Params []++-- | Merge two parameters+merge :: Params -> Params -> Params+merge (Params xs) (Params ys) = Params $ xs ++ ys ++-- | Cons a param with a base value+pcons :: (JSString, JSString) -> Params -> Params+pcons x (Params xs) = Params $ x:xs++-- | Create a set of parameters+params :: [(JSString, JSString)] -> Params+params = Params++-- | Request with parameters and everything+data Request = Request { path :: Path,+ method :: JSString,+ rparams :: Params,+ headers :: JSString,+ body :: JSString}++-- | The requests can be shown as a debug feature+instance Show Request where+ show (Request p m pms _hs _body)+ = let showDict :: (String, String) -> String+ showDict (a,b) = "\n\t" ++ a ++ ": " ++ b+ showParams (Params p')+ = show $ map (\(a,b) -> (J.unpack a, J.unpack b)) p'+ in "Request: " ++ concatMap showDict [+ ("Path", J.unpack p),+ ("Method", J.unpack m),+ ("Params", showParams pms)+ ]++-- | A default request without any path given +instance Default Request where+ def = rawRequest "" def +++-- | Creates a request by manually entering request path and parameters+rawRequest :: JSString -> Params -> Request+rawRequest p ps = Request { path = p,+ method = "GET",+ rparams = ps,+ headers = "",+ body = "" }+
+ Haste/GAPI/Result.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Haste.GAPI.Result+Description : Managing Results within the Google API+Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++This module contains functions to work on results from requests to the Google+API. +-}+++module Haste.GAPI.Result (+ Result(),+ Raw(..), + get, has, hasAll, + lookupVal, lookupResult,+ val, valOf, valMaybe,+ parp, children,+ toResult + ) where++import Haste+import qualified Haste.Foreign as F +import qualified Haste.JSString as J++-- GHC 7.8 compatibility+import Data.Functor ((<$>))++import Haste.GAPI.Request.Types+import Haste.GAPI.Request.RequestM++-- | Result mapping. The Result takes a parameter to act as a phantom type+-- on which kind of object you're currently working on.+newtype Result a = Result {rawR :: F.JSAny}+++-- | Transforms a JSAny to a Result+toResult :: JSAny -> Result a+toResult = Result ++-- | Default result parameter.+data Raw ++-- | Fetches a data field from a GAPI Result +get :: Result a -> JSString -> RequestM (Result Raw)+get (Result r) key = liftIO $ do+ h <- F.has r key+ if h then Result <$> F.get r key+ else fail $ "Child '" ++ J.unpack key ++ "' was not found."++-- | Looks up a value by a deep index+-- Example:+-- +-- @+-- rLookup res "a.b.c"+-- @+--+-- This will look up`(a.b.c)` where `a` is a field of+-- `res`, `b` is a field of `a`, and `c` is a field of `b`.+lookupVal :: F.FromAny any => Result a -> JSString -> RequestM (Maybe any)+lookupVal res keys = liftIO $ do+ v <- F.lookupAny (rawR res) keys+ case v of+ Just a -> Just <$> F.fromAny a+ Nothing -> return Nothing ++-- | Fetches a result from a deep index+lookupResult :: Result a -> JSString -> RequestM (Maybe (Result Raw))+lookupResult res keys+ = liftIO $ fmap Result <$> F.lookupAny (rawR res) keys++-- | Checks if a Result has a certain field+has :: Result a -> JSString -> RequestM Bool+has (Result res) key = liftIO $ F.has res key++-- | Checks if a Result has several different fields. Will return true only+-- if all these fields exists. +hasAll :: Result a -> [JSString] -> RequestM Bool+hasAll res keys = and <$> mapM (has res) keys++-- | Fetches a value from a field, in contrast to a result.+-- Field must contain a value compatible with fromAny+-- Terminates with an error if the field cannot be found.+val :: F.FromAny any => Result a -> JSString -> RequestM any +val r key = liftIO $ F.get (rawR r) key >>= F.fromAny++-- | Fetches the value of the result directly.+valOf :: F.FromAny any => Result a -> RequestM any+valOf = liftIO . F.fromAny . rawR++-- | Same as `val`, but will return Nothing if there is no such field.+valMaybe :: F.FromAny any => Result a -> JSString -> RequestM (Maybe any)+valMaybe (Result res) key = liftIO $ do+ exists <- F.has res key+ if exists then Just <$> (F.get res key >>= F.fromAny)+ else return Nothing++-- | Takes a result and a list of keys and maps these values to Params.+parp :: Result a -> [JSString] -> RequestM (Maybe Params)+parp r keys = do+ exists <- hasAll r keys+ if not exists+ then return Nothing+ else do vals <- mapM (val r) keys+ return . Just . Params $ zip keys vals+++-- | Transforms a JSAny representing a list into a list of its children+children' :: JSAny -> IO [JSAny]+children' = F.fromAny++-- | Checks if a fromAny is an array +isArray :: JSAny -> IO Bool+isArray = F.ffi "(function(a) {\+\return a.prop \+\&& a.prop.constructor === Array;\+\})"++-- | Transforms a result which represents an array into a list of child+-- elements+children :: Result a -> RequestM (Maybe [Result b])+children (Result res) = do+ arr <- liftIO $ isArray res+ if arr + then do + childs <- liftIO $ children' res+ return . Just $ map Result childs+ else return Nothing
+ Haste/GAPI/Types.hs view
@@ -0,0 +1,43 @@+{-|+Module : Haste.GAPI.Types+Description : Types used in different subsection APIs, like Google++Copyright : (c) Jonathan Skårstedt, 2016+License : MIT+Maintainer : jonathan.skarstedt@gmail.com+Stability : experimental+Portability : Haste++Types for use by different APIs connecting to haste-gapi. +-}+module Haste.GAPI.Types where++import Haste (JSString)++-- | Resource versioning tag. Will be updated if the resource is updated+type ETag = JSString++-- | An e-mail address+type EMail = JSString++-- | Type of e-mail address. +type EMailType = JSString++-- | An URL+type URL = JSString++-- | User id+type UserID = JSString++-- | Activity id+type ActivityID = JSString++-- | Type of Collection+type Collection = JSString++-- | Type of URL, like contributor (user is contributing to this site) or+-- website.+type URLType = JSString++-- | An image wrapped URL +data Image = Image URL+ deriving Show
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Jonathan Skårstedt++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ haste-gapi.cabal view
@@ -0,0 +1,61 @@+name: haste-gapi+-- The package version. See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary: +-+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.1.0.0+synopsis: Google API bindings for the Haste compiler+-- A longer description of the package.+description: This is a library to make use of the Google API Client+ Library for JavasScript in a Haskell environment!++ The library works by wrapping login and giving you a+ fancy type to perform your requests in. This will ease+ chained requests that would give large amounts of clutter+ in JavaScript, while giving you a better ability to+ handle errors.+ + Your HTML doesn't even need to load the GAPI library+ itself, Haste-GAPI handles that for you!++license: MIT+license-file: LICENSE+author: Jonathan Skårstedt+maintainer: jonathan.skarstedt@gmail.com+-- copyright: +category: Web+build-type: Simple+cabal-version: >=1.10++source-repository head+ type: github+ location: https://github.com/nyson/haste-gapi+++ +library+ -- Modules exported by the library.+ exposed-modules: Haste.GAPI,+ Haste.GAPI.GPlus,+ Haste.GAPI.Types+ + -- Modules included in this library but not exported.+ other-modules: Haste.GAPI.Result,+ Haste.GAPI.Request, + Haste.GAPI.Request.RequestM,+ Haste.GAPI.Request.Types,+ Haste.GAPI.Request.Raw,+ Haste.GAPI.GPlus.Person,+ Haste.GAPI.Internals.Promise++ other-extensions: OverloadedStrings+ build-depends: base == 4.* , data-default >= 0.5, transformers >= 0.4 +-- if impl(haste)+ build-depends: haste-lib >=0.5.1+-- else+ -- build-depends: haste-compiler >=0.5.1+ + default-language: Haskell2010+