packages feed

factual-api 0.1.2 → 0.2.0

raw patch · 33 files changed

+870/−402 lines, 33 filesdep ~hoauth

Dependency ranges changed: hoauth

Files

− Data/Factual/Credentials.hs
@@ -1,18 +0,0 @@--- | This module contains a simple definition of the credentials type which is---   used to perform submit queries to the Factual API.--module Data.Factual.Credentials-  (-    -- * Credentials type-    Credentials(..)-  , Key-  , Secret-  ) where---- | The Key is the oauth key as a String.-type Key = String--- | The Secret is the oauth secret as a String.-type Secret = String---- | A Credentials value is used to generate a Token.-data Credentials = Credentials Key Secret
− Data/Factual/CrosswalkQuery.hs
@@ -1,52 +0,0 @@--- | This module exports the type used to create crosswalk queries.-module Data.Factual.CrosswalkQuery-  (-   -- * CrosswalkQuery type-   CrosswalkQuery(..)-  ) where--import Data.Factual.Query-import Data.Factual.Utils---- | A crosswalk query can be formed by specifying a factual id and---   (optionally) a list of namespaces to only include when attempting to find---   the ids for various namespaces, or by specifying a namespace and namespace---   id in order to find the factual id. An optional limit can be set as well.-data CrosswalkQuery = CrosswalkQuery { factualId :: Maybe String-                                     , limit :: Maybe Int-                                     , namespace :: Maybe String-                                     , namespaceId :: Maybe String-                                     , only :: [String]-                                     } deriving (Eq, Show)---- A crosswalk query is a member of the Query typeclass so it can generate a--- path to be queried.-instance Query CrosswalkQuery where-  toPath query = "/places/crosswalk?"-               ++ joinAndFilter [ factualIdString $ factualId query-                                , limitString $ limit query-                                , namespaceString $ namespace query-                                , namespaceIdString $ namespaceId query-                                , onlyString $ only query ]---- The following helper functions are used to generate the separate parts of--- the query path.-factualIdString :: Maybe String -> String-factualIdString (Just id) = "factual_id=" ++ id-factualIdString Nothing   = ""--limitString :: Maybe Int -> String-limitString (Just lim) = "limit=" ++ (show lim)-limitString Nothing    = ""--namespaceString :: Maybe String -> String-namespaceString (Just namespace) = "namespace=" ++ namespace-namespaceString Nothing          = ""--namespaceIdString :: Maybe String -> String-namespaceIdString (Just id) = "namespace_id=" ++ id-namespaceIdString Nothing   = ""--onlyString :: [String] -> String-onlyString []   = ""-onlyString strs = "only=" ++ (join "," strs)
+ Data/Factual/Query/CrosswalkQuery.hs view
@@ -0,0 +1,48 @@+-- | This module exports the type used to create crosswalk queries.+module Data.Factual.Query.CrosswalkQuery+  (+   -- * CrosswalkQuery type+   CrosswalkQuery(..)+  ) where++import Data.Factual.Query+import Data.Factual.Utils++-- | A crosswalk query can be formed by specifying a factual id and+--   (optionally) a list of namespaces to only include when attempting to find+--   the ids for various namespaces, or by specifying a namespace and namespace+--   id in order to find the factual id. An optional limit can be set as well.+data CrosswalkQuery = CrosswalkQuery { factualId :: Maybe String+                                     , limit :: Maybe Int+                                     , namespace :: Maybe String+                                     , namespaceId :: Maybe String+                                     , only :: [String]+                                     } deriving (Eq, Show)++-- A crosswalk query is a member of the Query typeclass so it can generate a+-- path to be queried.+instance Query CrosswalkQuery where+  toPath query = "/places/crosswalk?"+               ++ joinAndFilter [ factualIdString $ factualId query+                                , limitString $ limit query+                                , namespaceString $ namespace query+                                , namespaceIdString $ namespaceId query+                                , onlyString $ only query ]++-- The following helper functions are used to generate the separate parts of+-- the query path.+factualIdString :: Maybe String -> String+factualIdString (Just id) = "factual_id=" ++ id+factualIdString Nothing   = ""++namespaceString :: Maybe String -> String+namespaceString (Just namespace) = "namespace=" ++ namespace+namespaceString Nothing          = ""++namespaceIdString :: Maybe String -> String+namespaceIdString (Just id) = "namespace_id=" ++ id+namespaceIdString Nothing   = ""++onlyString :: [String] -> String+onlyString []   = ""+onlyString strs = "only=" ++ (join "," strs)
+ Data/Factual/Query/FacetsQuery.hs view
@@ -0,0 +1,49 @@+-- | This module exports the types used to create facets queries.+module Data.Factual.Query.FacetsQuery+  (+    -- * FacetsQuery type+    FacetsQuery(..)+    -- * Required modules+  , module Data.Factual.Shared.Table+  , module Data.Factual.Shared.Search+  , module Data.Factual.Shared.Filter+  , module Data.Factual.Shared.Geo+  ) where++import Data.Factual.Query+import Data.Factual.Shared.Table+import Data.Factual.Shared.Search+import Data.Factual.Shared.Filter+import Data.Factual.Shared.Geo+import Data.Factual.Utils++-- | The FacetsQuery type is used to construct facets queries. A table and search+--   should be specified, but the rest of the query options are essentially+--   optional.+data FacetsQuery = FacetsQuery { table        :: Table+                               , search       :: Search+                               , select       :: [String]+                               , filters      :: [Filter]+                               , geo          :: Maybe Geo+                               , limit        :: Maybe Int+                               , minCount     :: Maybe Int+                               , includeCount :: Bool+                               } deriving (Eq, Show)++-- The FacetsQuery type is a member of the Query typeclass so it can be used to+-- make a request.+instance Query FacetsQuery where+  toPath query = (show $ table query)+               ++ "facets?"+               ++ joinAndFilter [ searchString $ search query+                                , selectString $ select query+                                , filtersString $ filters query+                                , geoString $ geo query+                                , limitString $ limit query+                                , minCountString $ minCount query+                                , includeCountString $ includeCount query ]++-- Helper functions+minCountString :: Maybe Int -> String+minCountString (Just x) = "min_count=" ++ show x+minCountString Nothing  = ""
+ Data/Factual/Query/GeocodeQuery.hs view
@@ -0,0 +1,20 @@+-- | This module exports the type used to create geopulse queries.+module Data.Factual.Query.GeocodeQuery+  (+    -- * ResolveQuery type+    GeocodeQuery(..)+    -- * Required modules+  , module Data.Factual.Shared.Geo+  ) where++import Data.Factual.Query+import Data.Factual.Shared.Geo++-- | The GeocodeQuery type is used to construct geocode queries. A geo point+--   is required.+data GeocodeQuery = GeocodeQuery Geo deriving (Eq, Show)++-- The GeocodeQuery type is a member of the Query typeclass so it can be used+-- to make a request.+instance Query GeocodeQuery where+  toPath (GeocodeQuery geo) = "/places/geocode?" ++ (geoString $ Just geo)
+ Data/Factual/Query/GeopulseQuery.hs view
@@ -0,0 +1,26 @@+-- | This module exports the type used to create geopulse queries.+module Data.Factual.Query.GeopulseQuery+  (+    -- * ResolveQuery type+    GeopulseQuery(..)+    -- * Required modules+  , module Data.Factual.Shared.Geo+  ) where++import Data.Factual.Query+import Data.Factual.Utils+import Data.Factual.Shared.Geo++-- | The GeopulseQuery type is used to construct geopulse queries. A geo point+--   is required but select values are optional (just use an empty list to+--   denote selecting all pulses).+data GeopulseQuery = GeopulseQuery { geo    :: Geo+                                   , select :: [String]+                                   } deriving (Eq, Show)++-- The GeopulseQuery type is a member of the Query typeclass so it can be used+-- to make a request.+instance Query GeopulseQuery where+  toPath query = "/places/geopulse?"+               ++ joinAndFilter [ geoString $ Just $ geo query+                                , selectString $ select query ]
+ Data/Factual/Query/ReadQuery.hs view
@@ -0,0 +1,52 @@+-- | This module exports the types used to create read queries.+module Data.Factual.Query.ReadQuery+  (+    -- * ReadQuery type+    ReadQuery(..)+    -- * Required modules+  , module Data.Factual.Shared.Table+  , module Data.Factual.Shared.Search+  , module Data.Factual.Shared.Filter+  , module Data.Factual.Shared.Geo+  ) where++import Data.Factual.Query+import Data.Factual.Shared.Table+import Data.Factual.Shared.Search+import Data.Factual.Shared.Filter+import Data.Factual.Shared.Geo+import Data.Factual.Utils++-- | The ReadQuery type is used to construct read queries. A table should be+--   specified, but the rest of the query options are essentially optional+--   (you opt out using Nothing or an empty List for the value). The select is+--   a list of field names to include in the results. The limit and offset are+--   used to request a specific range of rows and includeCount will include the+--   count of returned rows if it is set to True.+data ReadQuery = ReadQuery { table        :: Table+                           , search       :: Search+                           , select       :: [String]+                           , limit        :: Maybe Int+                           , offset       :: Maybe Int+                           , filters      :: [Filter]+                           , geo          :: Maybe Geo+                           , includeCount :: Bool+                           } deriving (Eq, Show)++-- The ReadQuery type is a member of the Query typeclass so it can be used to+-- make a request.+instance Query ReadQuery where+  toPath query = (show $ table query)+               ++ "read?"+               ++ joinAndFilter [ searchString $ search query+                                , selectString $ select query+                                , limitString $ limit query+                                , offsetString $ offset query+                                , filtersString $ filters query+                                , geoString $ geo query+                                , includeCountString $ includeCount query ]++-- The following helper functions are used in generating query Strings.+offsetString :: Maybe Int -> String+offsetString (Just x) = "offset=" ++ show x+offsetString Nothing = ""
+ Data/Factual/Query/ResolveQuery.hs view
@@ -0,0 +1,35 @@+-- | This module exports the type used to create resolve queries.+module Data.Factual.Query.ResolveQuery+  (+    -- * ResolveQuery type+    ResolveQuery(..)+    -- * ResolveValue type+  , ResolveValue(..)+  ) where++import Data.Factual.Query+import Data.Factual.Utils++-- | A resolve value can either be a String or a Number (Double). The first+--   argument is the name of the field and the second argument is the input+--   value.+data ResolveValue = ResolveStr String String+                  | ResolveNum String Double+                  deriving Eq++-- ResolveValue is a member of the Show typeclass to help generate query Strings.+instance Show ResolveValue where+  show (ResolveStr name str) = (show name) ++ ":" ++ (show str)+  show (ResolveNum name num) = (show name) ++ ":" ++ (show num)++-- | A resolve query is formed as an array of resolve values. These values will+--   be compared with Factual records to return a cleaner, more canonical row+--   of data.+data ResolveQuery = ResolveQuery [ResolveValue] deriving Eq++-- ResolveQuery is a member of the Query typeclass so that it can be used to+-- make requests.+instance Query ResolveQuery where+  toPath (ResolveQuery values) = "/places/resolve?values={"+                               ++ (join "," $ map show values)+                               ++ "}"
+ Data/Factual/Query/SchemaQuery.hs view
@@ -0,0 +1,19 @@+-- | This module exports the type used to create schema queries.+module Data.Factual.Query.SchemaQuery+  (+    -- * SchemaQuery type+    SchemaQuery(..)+    -- * Required modules+  , module Data.Factual.Shared.Table+  ) where++import Data.Factual.Query+import Data.Factual.Shared.Table++-- | A schema query is formed by simply supplying a Table to the value+--   constructor.+data SchemaQuery = SchemaQuery Table deriving (Eq, Show)++-- SchemaQuery is a member of Query typeclass so that it can generate a response.+instance Query SchemaQuery where+  toPath (SchemaQuery table) = (show $ table) ++ "schema"
− Data/Factual/ReadQuery.hs
@@ -1,153 +0,0 @@--- | This module exports the types used to create read queries.-module Data.Factual.ReadQuery-  (-    -- * ReadQuery type-    ReadQuery(..)-    -- * Search type-  , Search(..)-    -- * Filter type-  , Field-  , Filter(..)-    -- * Geo type-  , Lat-  , Long-  , Radius-  , Geo(..)-  ) where--import Data.Factual.Query-import Data.Factual.Table-import Data.Factual.Utils---- | A Field is a String representation of the field name.-type Field = String--- | A Lat is the latitude represented as a Double.-type Lat = Double--- | A Long is the longitude represented as a Double.-type Long = Double--- | A Radius is the radius of the circle as a Double in meters.-type Radius = Double---- | This type is used to construct an ANDed or ORed search in a read query.-data Search = AndSearch [String] | OrSearch [String] deriving Eq---- When a Search is shown it displays the string representation that will go--- into the query string.-instance Show Search where-  show (AndSearch terms) = join " " terms-  show (OrSearch terms) = join "," terms---- | The Filter type is used to represent various filters in a read query.-data Filter = EqualNum Field Double -- ^ A numeric field has to match a number exactly.-            | EqualStr Field String -- ^ A string field has to match a string exactly.-            | NotEqualNum Field Double -- ^ A numeric field must equal a specific number.-            | NotEqualStr Field String -- ^ A string field must equal a specific string.-            | InNumList Field [Double] -- ^ A numeric field must be equal to any of the numbers in a list.-            | InStrList Field [String] -- ^ A string field must be equal to any of the strings in a list.-            | NotInNumList Field [Double] -- ^ A numeric field must not be equal to any of the numbers in a list.-            | NotInStrList Field [String] -- ^ A string field must not be equal to any of the strings in a list.-            | BeginsWith Field String -- ^ A string field must begin with a specific string.-            | NotBeginsWith Field String -- ^ A string field must not begin with a specific string.-            | BeginsWithAny Field [String] -- ^ A string field must begin with any of the strings in a list.-            | NotBeginsWithAny Field [String] -- ^ A string field must not begin with any of the strings in a list.-            | IsBlank Field -- ^ A field must be blank.-            | IsNotBlank Field -- ^ A field must not be blank.-            | And [Filter] -- ^ Form an AND condition with the filters in the list.-            | Or [Filter] -- ^ Form an OR condition with the filters in the list.-            deriving Eq---- Filter is a member of Show to help generate query strings.-instance Show Filter where-  show (EqualNum field num) = (show field) ++ ":" ++ (show num)-  show (EqualStr field str) = (show field) ++ ":" ++ (show str)-  show (NotEqualNum field num) = (show field) ++ ":{" ++ (show "$neq") ++ ":" ++ (show num) ++ "}"-  show (NotEqualStr field str) = (show field) ++ ":{" ++ (show "$neq") ++ ":" ++ (show str) ++ "}"-  show (InNumList field nums) = (show field) ++ ":{" ++ (show "$in") ++ ":[" ++ (join "," $ map show nums) ++ "]}"-  show (InStrList field strs) = (show field) ++ ":{" ++ (show "$in") ++ ":[" ++ (join "," $ map show strs) ++ "]}"-  show (NotInNumList field nums) = (show field) ++ ":{" ++ (show "$nin") ++ ":[" ++ (join "," $ map show nums) ++ "]}"-  show (NotInStrList field strs) = (show field) ++ ":{" ++ (show "$nin") ++ ":[" ++ (join "," $ map show strs) ++ "]}"-  show (BeginsWith field str) = (show field) ++ ":{" ++ (show "$bw") ++ ":" ++ (show str) ++ "}"-  show (NotBeginsWith field str) = (show field) ++ ":{" ++ (show "$nbw") ++ ":" ++ (show str) ++ "}"-  show (BeginsWithAny field strs) = (show field) ++ ":{" ++ (show "$bwin") ++ ":[" ++ (join "," $ map show strs) ++ "]}"-  show (NotBeginsWithAny field strs) = (show field) ++ ":{" ++ (show "$nbwin") ++ ":[" ++ (join "," $ map show strs) ++ "]}"-  show (IsBlank field) = (show field) ++ ":{\"$blank\":true}"-  show (IsNotBlank field) = (show field) ++ ":{\"$blank\":false}"-  show (And filters) = (show "$and") ++ ":[" ++ (join "," $ map showFilter filters) ++ "]"-  show (Or filters) = (show "$or") ++ ":[" ++ (join "," $ map showFilter filters) ++ "]"---- | The Geo type is used to limit the search to specific geograph location.---   Currently, only circles are supported. Supply a latitude, longitude and---   radius in meters for the circle.-data Geo = Circle Lat Long Radius deriving Eq---- Geo is a member of Show to help generate query strings.-instance Show Geo where-  show (Circle lat long radius) = "{\"$circle\":{\"$center\":[" -                                ++ (show lat) -                                ++ ", "-                                ++ (show long)-                                ++ "],\"$meters\":"-                                ++ (show radius)-                                ++ "}}"---- | The ReadQuery type is used to construct read queries. A table should be---   specified, but the rest of the query options are essentially optional---   (you opt out using Nothing or an empty List for the value). The select is---   a list of field names to include in the results. The limit and offset are---   used to request a specific range of rows and includeCount will include the---   count of returned rows if it is set to True.-data ReadQuery = ReadQuery { table        :: Table-                           , search       :: Search-                           , select       :: [String]-                           , limit        :: Maybe Int-                           , offset       :: Maybe Int-                           , filters      :: [Filter]-                           , geo          :: Maybe Geo-                           , includeCount :: Bool-                           } deriving (Eq, Show)---- The ReadQuery type is a member of the Query typeclass so it can be used to--- make a request.-instance Query ReadQuery where-  toPath query = (show $ table query)-               ++ "read?"-               ++ joinAndFilter [ searchString $ search query-                                , selectString $ select query-                                , limitString $ limit query-                                , offsetString $ offset query-                                , filtersString $ filters query-                                , geoString $ geo query-                                , includeCountString $ includeCount query ]---- The following helper functions are used in generating query Strings.-showFilter :: Filter -> String-showFilter filter = "{" ++ (show filter) ++ "}"--searchString :: Search -> String-searchString (AndSearch []) = ""-searchString (OrSearch []) = ""-searchString search = "q=" ++ show search--selectString :: [String] -> String-selectString []      = ""-selectString selects = "select=" ++ (join "," selects)--limitString :: Maybe Int -> String-limitString (Just x) = "limit=" ++ show x-limitString Nothing = ""--offsetString :: Maybe Int -> String-offsetString (Just x) = "offset=" ++ show x-offsetString Nothing = ""--filtersString :: [Filter] -> String-filtersString [] = ""-filtersString fs = "filters={" ++ (join "," $ map show fs) ++ "}"--geoString :: Maybe Geo -> String-geoString (Just c) = "geo=" ++ show c-geoString Nothing = ""--includeCountString :: Bool -> String-includeCountString True = "include_count=true"-includeCountString False = "include_count=false"
− Data/Factual/ResolveQuery.hs
@@ -1,35 +0,0 @@--- | This module exports the type used to create resolve queries.-module Data.Factual.ResolveQuery-  (-    -- * ResolveQuery type-    ResolveQuery(..)-    -- * ResolveValue type-  , ResolveValue(..)-  ) where--import Data.Factual.Query-import Data.Factual.Utils---- | A resolve value can either be a String or a Number (Double). The first---   argument is the name of the field and the second argument is the input---   value.-data ResolveValue = ResolveStr String String-                  | ResolveNum String Double-                  deriving Eq---- ResolveValue is a member of the Show typeclass to help generate query Strings.-instance Show ResolveValue where-  show (ResolveStr name str) = (show name) ++ ":" ++ (show str)-  show (ResolveNum name num) = (show name) ++ ":" ++ (show num)---- | A resolve query is formed as an array of resolve values. These values will---   be compared with Factual records to return a cleaner, more canonical row---   of data.-data ResolveQuery = ResolveQuery [ResolveValue] deriving Eq---- ResolveQuery is a member of the Query typeclass so that it can be used to--- make requests.-instance Query ResolveQuery where-  toPath (ResolveQuery values) = "/places/resolve?values={"-                               ++ (join "," $ map show values)-                               ++ "}"
− Data/Factual/SchemaQuery.hs
@@ -1,17 +0,0 @@--- | This module exports the type used to create schema queries.-module Data.Factual.SchemaQuery-  (-    -- * SchemaQuery type-    SchemaQuery(..)-  ) where--import Data.Factual.Query-import Data.Factual.Table---- | A schema query is formed by simply supplying a Table to the value---   constructor.-data SchemaQuery = SchemaQuery Table deriving (Eq, Show)---- SchemaQuery is a member of Query typeclass so that it can generate a response.-instance Query SchemaQuery where-  toPath (SchemaQuery table) = (show $ table) ++ "schema"
+ Data/Factual/Shared/Filter.hs view
@@ -0,0 +1,60 @@+-- | This module exports the Filter type used to create read and facet queries.+module Data.Factual.Shared.Filter+  (+    -- * Filter type+    Field+  , Filter(..)+    -- * Helper functions+  , filtersString+  ) where++import Data.Factual.Utils++-- | A Field is a String representation of the field name.+type Field = String++-- | The Filter type is used to represent various filters in a read or facets query.+data Filter = EqualNum Field Double -- ^ A numeric field has to match a number exactly.+            | EqualStr Field String -- ^ A string field has to match a string exactly.+            | NotEqualNum Field Double -- ^ A numeric field must equal a specific number.+            | NotEqualStr Field String -- ^ A string field must equal a specific string.+            | InNumList Field [Double] -- ^ A numeric field must be equal to any of the numbers in a list.+            | InStrList Field [String] -- ^ A string field must be equal to any of the strings in a list.+            | NotInNumList Field [Double] -- ^ A numeric field must not be equal to any of the numbers in a list.+            | NotInStrList Field [String] -- ^ A string field must not be equal to any of the strings in a list.+            | BeginsWith Field String -- ^ A string field must begin with a specific string.+            | NotBeginsWith Field String -- ^ A string field must not begin with a specific string.+            | BeginsWithAny Field [String] -- ^ A string field must begin with any of the strings in a list.+            | NotBeginsWithAny Field [String] -- ^ A string field must not begin with any of the strings in a list.+            | IsBlank Field -- ^ A field must be blank.+            | IsNotBlank Field -- ^ A field must not be blank.+            | And [Filter] -- ^ Form an AND condition with the filters in the list.+            | Or [Filter] -- ^ Form an OR condition with the filters in the list.+            deriving Eq++-- Filter is a member of Show to help generate query strings.+instance Show Filter where+  show (EqualNum field num) = (show field) ++ ":" ++ (show num)+  show (EqualStr field str) = (show field) ++ ":" ++ (show str)+  show (NotEqualNum field num) = (show field) ++ ":{" ++ (show "$neq") ++ ":" ++ (show num) ++ "}"+  show (NotEqualStr field str) = (show field) ++ ":{" ++ (show "$neq") ++ ":" ++ (show str) ++ "}"+  show (InNumList field nums) = (show field) ++ ":{" ++ (show "$in") ++ ":[" ++ (join "," $ map show nums) ++ "]}"+  show (InStrList field strs) = (show field) ++ ":{" ++ (show "$in") ++ ":[" ++ (join "," $ map show strs) ++ "]}"+  show (NotInNumList field nums) = (show field) ++ ":{" ++ (show "$nin") ++ ":[" ++ (join "," $ map show nums) ++ "]}"+  show (NotInStrList field strs) = (show field) ++ ":{" ++ (show "$nin") ++ ":[" ++ (join "," $ map show strs) ++ "]}"+  show (BeginsWith field str) = (show field) ++ ":{" ++ (show "$bw") ++ ":" ++ (show str) ++ "}"+  show (NotBeginsWith field str) = (show field) ++ ":{" ++ (show "$nbw") ++ ":" ++ (show str) ++ "}"+  show (BeginsWithAny field strs) = (show field) ++ ":{" ++ (show "$bwin") ++ ":[" ++ (join "," $ map show strs) ++ "]}"+  show (NotBeginsWithAny field strs) = (show field) ++ ":{" ++ (show "$nbwin") ++ ":[" ++ (join "," $ map show strs) ++ "]}"+  show (IsBlank field) = (show field) ++ ":{\"$blank\":true}"+  show (IsNotBlank field) = (show field) ++ ":{\"$blank\":false}"+  show (And filters) = (show "$and") ++ ":[" ++ (join "," $ map showFilter filters) ++ "]"+  show (Or filters) = (show "$or") ++ ":[" ++ (join "," $ map showFilter filters) ++ "]"++-- The following helper functions are used in generating query Strings.+showFilter :: Filter -> String+showFilter filter = "{" ++ (show filter) ++ "}"++filtersString :: [Filter] -> String+filtersString [] = ""+filtersString fs = "filters={" ++ (join "," $ map show fs) ++ "}"
+ Data/Factual/Shared/Geo.hs view
@@ -0,0 +1,45 @@+-- | This module exports the Geo type used to create read and facet queries.+module Data.Factual.Shared.Geo+  (+    -- * Geo type+    Lat+  , Long+  , Radius+  , Geo(..)+    -- * Helper functions+  , geoString+  ) where++-- | A Lat is the latitude represented as a Double.+type Lat = Double+-- | A Long is the longitude represented as a Double.+type Long = Double+-- | A Radius is the radius of the circle as a Double in meters.+type Radius = Double++-- | The Geo type is used to limit the search to specific geograph location.+--   Currently, only circles are supported. Supply a latitude, longitude and+--   radius in meters for the circle.+data Geo = Circle Lat Long Radius+         | Point Lat Long+         deriving Eq++-- Geo is a member of Show to help generate query strings.+instance Show Geo where+  show (Circle lat long radius) = "{\"$circle\":{\"$center\":[" +                                ++ (show lat) +                                ++ ", "+                                ++ (show long)+                                ++ "],\"$meters\":"+                                ++ (show radius)+                                ++ "}}"+  show (Point lat long) = "{\"$point\":["+                        ++ (show lat) +                        ++ ","+                        ++ (show long)+                        ++ "]}"++-- Helper functions+geoString :: Maybe Geo -> String+geoString (Just c) = "geo=" ++ show c+geoString Nothing = ""
+ Data/Factual/Shared/Search.hs view
@@ -0,0 +1,26 @@+-- | This module exports the Search type used to create read and facet queries.+module Data.Factual.Shared.Search+  (+    -- * Search type+    Search(..)+    -- * Helper functions+  , searchString+  ) where++import Data.Factual.Utils++-- | This type is used to construct an ANDed or ORed search in a query.+data Search = AndSearch [String] | OrSearch [String] | NoSearch deriving Eq++-- When a Search is shown it displays the string representation that will go+-- into the query string.+instance Show Search where+  show (AndSearch terms) = join " " terms+  show (OrSearch terms)  = join "," terms+  show NoSearch          = ""++-- Helper functions+searchString :: Search -> String+searchString (AndSearch []) = ""+searchString (OrSearch []) = ""+searchString search = "q=" ++ show search
+ Data/Factual/Shared/Table.hs view
@@ -0,0 +1,21 @@+-- | This module exports the type used to represent a table for the read or+--   schema query types.+module Data.Factual.Shared.Table+  (+     -- * Table type+     Table(..)+  ) where++-- | Three Table values are currently available for the Factual API.+data Table = Places+           | USRestaurants+           | Global+           | Custom String+           deriving Eq++-- Table is a member of the Show typeclass to generate the beginning of the path.+instance Show Table where+  show Places        = "/t/places/"+  show USRestaurants = "/t/restaurants-us/"+  show Global        = "/t/global/"+  show (Custom name) = "/t/" ++ name ++ "/"
− Data/Factual/Table.hs
@@ -1,16 +0,0 @@--- | This module exports the type used to represent a table for the read or---   schema query types.-module Data.Factual.Table-  (-     -- * Table type-     Table(..)-  ) where---- | Three Table values are currently available for the Factual API.-data Table = Places | USRestaurants | Global deriving Eq---- Table is a member of the Show typeclass to generate the beginning of the path.-instance Show Table where-  show Places = "/t/places/"-  show USRestaurants = "/t/restaurants-us/"-  show Global = "/t/global/"
Data/Factual/Utils.hs view
@@ -4,6 +4,9 @@     -- * Utility methods     join   , joinAndFilter+  , selectString+  , limitString+  , includeCountString   ) where  import Data.List (intersperse)@@ -18,3 +21,16 @@ --   Strings with an & character. The use case is forming query path Strings. joinAndFilter :: [String] -> String joinAndFilter strs = join "&" $ filter ("" /=) strs++-- The following helper functions are used in generating query Strings.+selectString :: [String] -> String+selectString []      = ""+selectString selects = "select=" ++ (join "," selects)++limitString :: Maybe Int -> String+limitString (Just x) = "limit=" ++ show x+limitString Nothing = ""++includeCountString :: Bool -> String+includeCountString True = "include_count=true"+includeCountString False = "include_count=false"
+ Data/Factual/Write.hs view
@@ -0,0 +1,12 @@+-- | This module exports the definition of the Write typeclass.+module Data.Factual.Write+  (+  -- * Query typeclass+  Write(..)) where++-- | A member of the Write typeclass must define a url method which returns+--   the write url as a string and a body method which returns of the body+--   of the write.+class Write w where+  path :: w -> String+  body :: w -> String
+ Data/Factual/Write/Flag.hs view
@@ -0,0 +1,64 @@+-- | This module exports the types used to create flag writes.+module Data.Factual.Write.Flag+  (+    -- * Flag type+    Flag(..)+    -- * Problem type+  , Problem(..)+    -- * Required modules+  , module Data.Factual.Shared.Table+  ) where++import Data.Factual.Write+import Data.Factual.Shared.Table+import Data.Maybe (fromJust)+import Data.Factual.Utils++-- | A Problem represents what is wrong with the row being flagged+data Problem = Duplicate+             | Nonexistent+             | Inaccurate+             | Inappropriate+             | Spam+             | Other+             deriving (Eq, Show)++-- | The Flag type represents a Write to be made to the API which flags a+--   row as having some kind of problem. The table and factualId identify the+--   problematic row, while the problem indicates the type of issue the row+--   has. The user is specified as a string. Other fields such as comment and+--   reference are optional. The debug flag is used to write in debug mode.+data Flag = Flag { table     :: Table+                 , factualId :: String+                 , problem   :: Problem+                 , user      :: String+                 , comment   :: Maybe String+                 , debug     :: Bool+                 , reference :: Maybe String+                 } deriving (Eq, Show)++-- The Flag type is a member of the Write typeclass so it can be sent as a post+-- request to the API.+instance Write Flag where+  path flag = (show $ table flag) ++ (factualId flag) ++ "/flag"+  body flag = "problem=" ++ (show $ problem flag) ++ "&" +++              "user=" ++ (user flag) ++ "&" +++              joinAndFilter [ commentString flag+                            , debugString flag+                            , referenceString flag ]++-- The following functions are helpers for the body function+commentString :: Flag -> String+commentString flag+  | comment flag == Nothing = ""+  | otherwise               = "comment=" ++ (fromJust $ comment flag)++debugString :: Flag -> String+debugString flag+  | debug flag == True = "debug=true"+  | otherwise          = "debug=false"++referenceString :: Flag -> String+referenceString flag+  | reference flag == Nothing = ""+  | otherwise                 = "reference=" ++ (fromJust $ reference flag)
+ Data/Factual/Write/Submit.hs view
@@ -0,0 +1,44 @@+-- | This module exports the types used to create contributions.+module Data.Factual.Write.Submit+  (+    -- * Submit type+    Submit(..)+    -- * Required modules+  , module Data.Factual.Shared.Table+  ) where++import Data.Factual.Write+import Data.Factual.Shared.Table+import Data.Maybe (fromJust)+import Data.Factual.Utils+import qualified Data.Map as M++-- | The Submit type represents a Write to the API which performs an upsert+--   (a row can be updated or a new row can be written). The table and user+--   must be specified, while the factual ID is optional (omitted for new+--   rows). Finally the values are specified in a String to String Map.+data Submit = Submit { table     :: Table+                     , user      :: String+                     , factualId :: Maybe String+                     , values    :: M.Map String String+                     } deriving (Eq, Show)++-- The Submit type is a member of the Write typeclass so that it can be+-- sent as a post request to the API.+instance Write Submit where+  path submit = pathString submit+  body submit = "user=" ++ (user submit) ++ "&" +++                "values=" ++ valuesString (values submit)++-- The following functions are helpers for the Write typeclass functions.+pathString :: Submit -> String+pathString submit+  | factualId submit == Nothing = (show $ table submit) ++ "submit"+  | otherwise = (show $ table submit) +++                (fromJust $ factualId submit) +++                "/submit"++valuesString :: M.Map String String -> String+valuesString values = "{" ++ join "," (map valueString $ M.keys values) ++ "}"+  where valueString key = "\"" ++ key ++ "\":\"" +++                          (fromJust $ M.lookup key values) ++ "\""
Network/Factual/API.hs view
@@ -2,42 +2,75 @@ --   the OAuth authentication process. module Network.Factual.API   (-    -- * API functions-    makeRequest-  , generateToken+    -- * Authentication+    generateToken+    -- * Read functions+  , makeRequest+  , makeRawRequest+    -- * Write functions+  , sendWrite     -- * The hoauth Token type   , Token(..)   ) where  import Data.Maybe (fromJust) import Network.OAuth.Consumer-import Network.OAuth.Http.Request (Request(..), parseURL, fromList)+import Network.OAuth.Http.Request (Request(..), Method(..), parseURL, fromList) import Network.OAuth.Http.Response (Response(..)) import Network.OAuth.Http.CurlHttpClient (CurlClient(..)) import Data.Aeson (Value, decode) import Data.Factual.Query-import Data.Factual.Credentials+import Data.Factual.Write+import qualified Data.ByteString.Lazy.Char8 as B import qualified Data.Factual.Response as F +-- | The Key is the oauth key as a String.+type Key = String+-- | The Secret is the oauth secret as a String.+type Secret = String++-- | This function takes a set of credentials and returns an OAuth token that+--   can be used to make requests.+generateToken :: Key -> Secret -> Token+generateToken key secret = fromApplication $ Application key secret OOB+ -- | This function takes an OAuth token and a query (which is member of the---   Query typeclass and returns an IO action which will fetch a response from+--   Query typeclass) and returns an IO action which will fetch a response from --   the Factual API. makeRequest :: (Query query) => Token -> query -> IO F.Response-makeRequest token query = do-  let fullpath = "http://api.v3.factual.com" ++ toPath query+makeRequest token query = makeRawRequest token (toPath query)++-- | This function can be used to make raw read requests for any path. You pass+--   in your Token and the path of your request (e.g. \"\/t\/places?q=starbucks\")+makeRawRequest :: Token -> String -> IO F.Response+makeRawRequest token queryString = do+  let fullpath = basePath ++ queryString   let request = generateRequest fullpath-  response <- runOAuthM token $ setupOAuth request-  return $ F.fromValue $ extractJSON response+  makeRequest' token request --- | This function takes a set of credentials and returns an OAuth token that---   can be used to make requests.-generateToken :: Credentials -> Token-generateToken (Credentials key secret) = fromApplication $ Application key secret OOB+-- | This function takes an OAuth token and a Write and retunrs and IO action+--   which sends the Write to API and returns a Response.+sendWrite :: (Write write) => Token -> write -> IO F.Response+sendWrite token write = do+  let fullpath = basePath ++ path write+  let request = generatePostRequest fullpath (body write)+  makeRequest' token request  -- The following helper functions aid the exported API functions+makeRequest' :: Token -> Request -> IO F.Response+makeRequest' token request = do+  response <- runOAuthM token $ setupOAuth request+  return $ F.fromValue $ extractJSON response+ generateRequest :: String -> Request generateRequest url = (fromJust $ parseURL url) { reqHeaders = (fromList headersList) } +generatePostRequest :: String -> String -> Request+generatePostRequest url body = baseRequest { reqHeaders = (fromList headersList)+                                           , method = POST+                                           , reqPayload = B.pack body }+  where baseRequest = (fromJust $ parseURL url)+ setupOAuth :: Request -> OAuthMonadT IO Response setupOAuth request = do   oauthRequest <- signRq2 HMACSHA1 Nothing request@@ -47,4 +80,7 @@ extractJSON = fromJust . decode . rspPayload  headersList :: [(String, String)]-headersList = [("X-Factual-Lib", "factual-haskell-driver-0.1.2")]+headersList = [("X-Factual-Lib", "factual-haskell-driver-0.2.0")]++basePath :: String+basePath = "http://api.v3.factual.com"
README.md view
@@ -31,15 +31,15 @@      $ ghci test/Tests.hs -To run the response tests you'll need an API key and secret, but you-can always run the query tests:+To run the integration tests you'll need an API key and secret, but you+can always run the unit tests: -    *Main> runQueryTests-    Cases: 33  Tried: 33  Errors: 0  Failures: 0-    Counts {cases = 33, tried = 33, errors = 0, failures = 0}-    *Main> runResponseTests "mykey" "mysecret"-    Cases: 4  Tried: 4  Errors: 0  Failures: 0-    Counts {cases = 4, tried = 4, errors = 0, failures = 0}+    *Main> runUnitTests+    Cases: 41  Tried: 41  Errors: 0  Failures: 0+    Counts {cases = 41, tried = 41, errors = 0, failures = 0}+    *Main> runIntegrationTests "mykey" "mysecret"+    Cases: 8  Tried: 8  Errors: 0  Failures: 0+    Counts {cases = 8, tried = 8, errors = 0, failures = 0}  # Documentation 
examples/CrosswalkExample.hs view
@@ -2,9 +2,7 @@  import System (getArgs) import Network.Factual.API-import Data.Factual.Credentials-import Data.Factual.Table-import Data.Factual.CrosswalkQuery+import Data.Factual.Query.CrosswalkQuery import Data.Factual.Response  main :: IO()@@ -12,7 +10,7 @@   args <- getArgs   let oauthKey = head args   let oauthSecret = last args-  let token = generateToken (Credentials oauthKey oauthSecret)+  let token = generateToken oauthKey oauthSecret   let query = CrosswalkQuery { factualId = Just "97598010-433f-4946-8fd5-4a6dd1639d77"                              , limit = Nothing                              , namespace = Nothing
+ examples/FacetsExample.hs view
@@ -0,0 +1,25 @@+module Main where++import System (getArgs)+import Network.Factual.API+import Data.Factual.Query.FacetsQuery+import Data.Factual.Response++main :: IO()+main = do+  args <- getArgs+  let oauthKey = head args+  let oauthSecret = last args+  let token = generateToken oauthKey oauthSecret+  let query = FacetsQuery { table        = Places+                          , search       = AndSearch ["Starbucks"]+                          , select       = ["country"]+                          , filters      = []+                          , geo          = Nothing+                          , limit        = Nothing+                          , minCount     = Nothing+                          , includeCount = False }+  result <- makeRequest token query+  putStrLn $ "Status: " ++ status result+  putStrLn $ "Version: " ++ show (version result)+  putStrLn $ "Data: " ++ show (response result)
+ examples/GeocodeExample.hs view
@@ -0,0 +1,18 @@+module Main where++import System (getArgs)+import Network.Factual.API+import Data.Factual.Query.GeocodeQuery+import Data.Factual.Response++main :: IO()+main = do+  args <- getArgs+  let oauthKey = head args+  let oauthSecret = last args+  let token = generateToken oauthKey oauthSecret+  let query = GeocodeQuery $ Point 34.06021 (-118.41828)+  result <- makeRequest token query+  putStrLn $ "Status: " ++ status result+  putStrLn $ "Version: " ++ show (version result)+  putStrLn $ "Data: " ++ show (response result)
+ examples/GeopulseExample.hs view
@@ -0,0 +1,19 @@+module Main where++import System (getArgs)+import Network.Factual.API+import Data.Factual.Query.GeopulseQuery+import Data.Factual.Response++main :: IO()+main = do+  args <- getArgs+  let oauthKey = head args+  let oauthSecret = last args+  let token = generateToken oauthKey oauthSecret+  let query = GeopulseQuery { geo    = Point 34.06021 (-118.41828)+                            , select = ["commercial_density"] }+  result <- makeRequest token query+  putStrLn $ "Status: " ++ status result+  putStrLn $ "Version: " ++ show (version result)+  putStrLn $ "Data: " ++ show (response result)
+ examples/RawReadExample.hs view
@@ -0,0 +1,16 @@+module Main where++import System (getArgs)+import Network.Factual.API+import Data.Factual.Response++main :: IO()+main = do+  args <- getArgs+  let oauthKey = head args+  let oauthSecret = last args+  let token = generateToken oauthKey oauthSecret+  result <- makeRawRequest token "/t/places?q=starbucks"+  putStrLn $ "Status: " ++ status result+  putStrLn $ "Version: " ++ show (version result)+  putStrLn $ "Data: " ++ show (response result)
examples/ReadExample.hs view
@@ -2,9 +2,7 @@  import System (getArgs) import Network.Factual.API-import Data.Factual.Credentials-import Data.Factual.Table-import Data.Factual.ReadQuery+import Data.Factual.Query.ReadQuery import Data.Factual.Response  main :: IO()@@ -12,7 +10,7 @@   args <- getArgs   let oauthKey = head args   let oauthSecret = last args-  let token = generateToken (Credentials oauthKey oauthSecret)+  let token = generateToken oauthKey oauthSecret   let query = ReadQuery { table = Places                         , search = AndSearch []                         , select = ["name"]
examples/ResolveExample.hs view
@@ -2,9 +2,7 @@  import System (getArgs) import Network.Factual.API-import Data.Factual.Credentials-import Data.Factual.Table-import Data.Factual.ResolveQuery+import Data.Factual.Query.ResolveQuery import Data.Factual.Response  main :: IO()@@ -12,7 +10,7 @@   args <- getArgs   let oauthKey = head args   let oauthSecret = last args-  let token = generateToken (Credentials oauthKey oauthSecret)+  let token = generateToken oauthKey oauthSecret   let query = ResolveQuery [ ResolveStr "name" "McDonalds"                            , ResolveStr "address" "10451 Santa Monica Blvd" ]   result <- makeRequest token query
examples/SchemaExample.hs view
@@ -2,9 +2,7 @@  import System (getArgs) import Network.Factual.API-import Data.Factual.Credentials-import Data.Factual.Table-import Data.Factual.SchemaQuery+import Data.Factual.Query.SchemaQuery import Data.Factual.Response  main :: IO()@@ -12,7 +10,7 @@   args <- getArgs   let oauthKey = head args   let oauthSecret = last args-  let token = generateToken (Credentials oauthKey oauthSecret)+  let token = generateToken oauthKey oauthSecret   let query = SchemaQuery Places   result <- makeRequest token query   putStrLn $ "Status: " ++ status result
factual-api.cabal view
@@ -1,12 +1,12 @@ name:            factual-api-version:         0.1.2+version:         0.2.0 license:         BSD3 license-file:    LICENSE.txt category:        API, Web copyright:       (c) 2011 Rudiger Lippert author:          Rudiger Lippert <rudy@factual.com> maintainer:      Rudiger Lippert <rudy@factual.com>-stability:       experimental+stability:       stable tested-with:     GHC == 7.0.4 synopsis:        A driver for the Factual API cabal-version:   >= 1.8@@ -14,9 +14,8 @@ bug-reports:     https://github.com/rudyl313/factual-haskell-driver/issues build-type:      Simple description:-    This is a driver for the Factual API. It provides a typesafe way to-    generate queries, an easy way to setup OAuth authentication and a-    simple way to send queries to the API.+    This is a driver for the Factual API. It provides a type-safe, easy way to+    generate queries, setup OAuth authentication and send queries to the API.  extra-source-files:     README.md@@ -27,14 +26,22 @@ library   exposed-modules:     Network.Factual.API-    Data.Factual.Credentials-    Data.Factual.CrosswalkQuery     Data.Factual.Query-    Data.Factual.ReadQuery-    Data.Factual.ResolveQuery+    Data.Factual.Query.CrosswalkQuery+    Data.Factual.Query.FacetsQuery+    Data.Factual.Query.GeocodeQuery+    Data.Factual.Query.GeopulseQuery+    Data.Factual.Query.ReadQuery+    Data.Factual.Query.ResolveQuery+    Data.Factual.Query.SchemaQuery+    Data.Factual.Write+    Data.Factual.Write.Flag+    Data.Factual.Write.Submit     Data.Factual.Response-    Data.Factual.SchemaQuery-    Data.Factual.Table+    Data.Factual.Shared.Filter+    Data.Factual.Shared.Geo+    Data.Factual.Shared.Search+    Data.Factual.Shared.Table    other-modules:     Data.Factual.Utils@@ -43,7 +50,7 @@     aeson >= 0.6.0.0,     attoparsec >= 0.10.1.0,     base == 4.*,-    hoauth >= 0.3.3,+    hoauth >= 0.3.0,     text >= 0.11.1.5,     unordered-containers >= 0.1.4.6,     vector >= 0.9.1
test/Tests.hs view
@@ -1,57 +1,74 @@ import Test.HUnit import Network.Factual.API import Data.Factual.Query-import Data.Factual.Table-import Data.Factual.ReadQuery-import Data.Factual.SchemaQuery-import Data.Factual.ResolveQuery+import Data.Factual.Query.ReadQuery+import Data.Factual.Query.SchemaQuery+import Data.Factual.Query.ResolveQuery+import Data.Factual.Query.GeocodeQuery import Data.Factual.Response-import Data.Factual.Credentials-import qualified Data.Factual.CrosswalkQuery as C+import qualified Data.Map as M+import qualified Data.Factual.Write as W+import qualified Data.Factual.Query.CrosswalkQuery as C+import qualified Data.Factual.Query.FacetsQuery as F+import qualified Data.Factual.Query.GeopulseQuery as G+import qualified Data.Factual.Write.Submit as S+import qualified Data.Factual.Write.Flag as L -runQueryTests = runTestTT queryTests+runUnitTests = runTestTT unitTests -runResponseTests key secret = runTestTT $ responseTests (Credentials key secret)+runIntegrationTests key secret = runTestTT $ integrationTests key secret -queryTests = TestList [ TestLabel "Place table test" placeTablePathTest-                 , TestLabel "Restaurants table test" restaurantsTablePathTest-                 , TestLabel "Global table test" globalTablePathTest-                 , TestLabel "And search test" andSearchPathTest-                 , TestLabel "Or search test" orSearchPathTest-                 , TestLabel "Select test" selectPathTest-                 , TestLabel "Limit test" limitPathTest-                 , TestLabel "Offset test" offsetPathTest-                 , TestLabel "Equal number filter test" equalNumFilterTest-                 , TestLabel "Equal string filter test" equalStrFilterTest-                 , TestLabel "Not equal number filter test" notEqualNumFilterTest-                 , TestLabel "Not equal string filter test" notEqualStrFilterTest-                 , TestLabel "In number list filter test" inNumListFilterTest-                 , TestLabel "In string list filter test" inStrListFilterTest-                 , TestLabel "Not in number list filter test" notInNumListFilterTest-                 , TestLabel "Not in string list filter test" notInStrListFilterTest-                 , TestLabel "Begins with filter test" beginsWithFilterTest-                 , TestLabel "Not begins with filter test" notBeginsWithFilterTest-                 , TestLabel "Begins with any filter test" beginsWithAnyFilterTest-                 , TestLabel "Not begins with any filter test" notBeginsWithAnyFilterTest-                 , TestLabel "Is blank filter test" isBlankFilterTest-                 , TestLabel "Is not blank filter test" isNotBlankFilterTest-                 , TestLabel "And filter test" andFilterTest-                 , TestLabel "Or filter test" andFilterTest-                 , TestLabel "Geo test" geoTest-                 , TestLabel "Include count test" includeCountTest-                 , TestLabel "Schema query test" schemaQueryTest-                 , TestLabel "Resolve query test" resolveQueryTest-                 , TestLabel "Factual ID test" factualIdTest-                 , TestLabel "Crosswalk limit test" limitCWPathTest-                 , TestLabel "Namespace test" namespaceTest-                 , TestLabel "Namespace ID test" namespaceIdTest-                 , TestLabel "Only test" onlyTest ]+unitTests = TestList [ TestLabel "Place table test" placeTablePathTest+                     , TestLabel "Restaurants table test" restaurantsTablePathTest+                     , TestLabel "Global table test" globalTablePathTest+                     , TestLabel "Custom table test" customTablePathTest+                     , TestLabel "Geopulse test" geopulsePathTest+                     , TestLabel "Geocode test" geocodePathTest+                     , TestLabel "And search test" andSearchPathTest+                     , TestLabel "Or search test" orSearchPathTest+                     , TestLabel "Select test" selectPathTest+                     , TestLabel "Limit test" limitPathTest+                     , TestLabel "Offset test" offsetPathTest+                     , TestLabel "Equal number filter test" equalNumFilterTest+                     , TestLabel "Equal string filter test" equalStrFilterTest+                     , TestLabel "Not equal number filter test" notEqualNumFilterTest+                     , TestLabel "Not equal string filter test" notEqualStrFilterTest+                     , TestLabel "In number list filter test" inNumListFilterTest+                     , TestLabel "In string list filter test" inStrListFilterTest+                     , TestLabel "Not in number list filter test" notInNumListFilterTest+                     , TestLabel "Not in string list filter test" notInStrListFilterTest+                     , TestLabel "Begins with filter test" beginsWithFilterTest+                     , TestLabel "Not begins with filter test" notBeginsWithFilterTest+                     , TestLabel "Begins with any filter test" beginsWithAnyFilterTest+                     , TestLabel "Not begins with any filter test" notBeginsWithAnyFilterTest+                     , TestLabel "Is blank filter test" isBlankFilterTest+                     , TestLabel "Is not blank filter test" isNotBlankFilterTest+                     , TestLabel "And filter test" andFilterTest+                     , TestLabel "Or filter test" andFilterTest+                     , TestLabel "Geo test" geoTest+                     , TestLabel "Include count test" includeCountTest+                     , TestLabel "Schema query test" schemaQueryTest+                     , TestLabel "Resolve query test" resolveQueryTest+                     , TestLabel "Factual ID test" factualIdTest+                     , TestLabel "Crosswalk limit test" limitCWPathTest+                     , TestLabel "Namespace test" namespaceTest+                     , TestLabel "Namespace ID test" namespaceIdTest+                     , TestLabel "Only test" onlyTest+                     , TestLabel "Facets test" facetsTest+                     , TestLabel "Submit path test" submitPathTest+                     , TestLabel "Submit body test" submitBodyTest+                     , TestLabel "Flag path test" flagPathTest+                     , TestLabel "Flag body test" flagBodyTest ] -responseTests creds = TestList [ TestLabel "Read test" (readResponseTest token)-                               , TestLabel "Schema test" (schemaResponseTest token)-                               , TestLabel "Resolve test" (resolveResponseTest token)-                               , TestLabel "Crosswalk test" (crosswalkResponseTest token) ]-                    where token = generateToken creds+integrationTests key secret = TestList [ TestLabel "Read test" (readIntegrationTest token)+                                       , TestLabel "Schema test" (schemaIntegrationTest token)+                                       , TestLabel "Resolve test" (resolveIntegrationTest token)+                                       , TestLabel "Crosswalk test" (crosswalkIntegrationTest token)+                                       , TestLabel "Raw read test" (rawIntegrationTest token)+                                       , TestLabel "Facets test" (facetsIntegrationTest token)+                                       , TestLabel "Geopulse test" (geopulseIntegrationTest token)+                                       , TestLabel "Geocode test" (geocodeIntegrationTest token) ]+                            where token = generateToken key secret  placeTablePathTest = TestCase (do   let expected = "/t/places/read?include_count=false"@@ -68,6 +85,21 @@   let path = toPath $ blankReadQuery { table = Global }   assertEqual "Correct path for global table" expected path) +customTablePathTest = TestCase (do+  let expected = "/t/foo/read?include_count=false"+  let path = toPath $ blankReadQuery { table = Custom "foo" }+  assertEqual "Correct path for custom table" expected path)++geopulsePathTest = TestCase (do+  let expected = "/places/geopulse?geo={\"$point\":[34.06021,-118.41828]}&select=commercial_density"+  let path = toPath $ G.GeopulseQuery { G.geo = Point 34.06021 (-118.41828) , G.select = ["commercial_density"] }+  assertEqual "Correct path for geopulse" expected path)++geocodePathTest = TestCase (do+  let expected = "/places/geocode?geo={\"$point\":[34.06021,-118.41828]}"+  let path = toPath $ GeocodeQuery $ Point 34.06021 (-118.41828)+  assertEqual "Correct path for geocode" expected path)+ andSearchPathTest = TestCase (do   let expected = "/t/places/read?q=foo bar&include_count=false"   let path = toPath $ blankReadQuery { search = AndSearch ["foo", "bar"] }@@ -218,8 +250,42 @@   let path = toPath $ blankCrosswalkQuery { C.only = ["yelp", "loopd"] }   assertEqual "Correct path for a only" expected path) -readResponseTest :: Token -> Test-readResponseTest token = TestCase (do+facetsTest = TestCase (do+  let expected = "/t/places/facets?q=starbucks&select=locality,region&"+               ++ "filters={\"country\":\"US\"}&limit=10&min_count=2&"+               ++ "include_count=false"+  let path = toPath $ F.FacetsQuery { F.table        = Places+                                    , F.search       = AndSearch ["starbucks"]+                                    , F.select       = ["locality", "region"]+                                    , F.filters      = [EqualStr "country" "US"]+                                    , F.geo          = Nothing+                                    , F.limit        = Just 10+                                    , F.minCount     = Just 2+                                    , F.includeCount = False }+  assertEqual "Correct path for a facets query" expected path)++submitPathTest = TestCase (do+  let expected = "/t/places/foobar/submit"+  let path = W.path submitWrite+  assertEqual "Correct path for submit" expected path)++submitBodyTest = TestCase (do+  let expected = "user=user123&values={\"key\":\"val\"}"+  let body = W.body submitWrite+  assertEqual "Correct body for submit" expected body)++flagPathTest = TestCase (do+  let expected = "/t/places/foobar/flag"+  let path = W.path flagWrite+  assertEqual "Correct path for flag" expected path)++flagBodyTest = TestCase (do+  let expected = "problem=Duplicate&user=user123&comment=There was a problem&debug=false"+  let body = W.body flagWrite+  assertEqual "Correct body for flag" expected body)++readIntegrationTest :: Token -> Test+readIntegrationTest token = TestCase (do   let query = ReadQuery { table = Places                         , search = AndSearch ["McDonalds", "Burger King"]                         , select = ["name"]@@ -231,28 +297,56 @@   result <- makeRequest token query   assertEqual "Valid read query" "ok" (status result)) -schemaResponseTest :: Token -> Test-schemaResponseTest token = TestCase (do+schemaIntegrationTest :: Token -> Test+schemaIntegrationTest token = TestCase (do   let query = SchemaQuery Places   result <- makeRequest token query-  assertEqual "Valid read query" "ok" (status result))+  assertEqual "Valid schema query" "ok" (status result)) -resolveResponseTest :: Token -> Test-resolveResponseTest token = TestCase (do+resolveIntegrationTest :: Token -> Test+resolveIntegrationTest token = TestCase (do   let query = ResolveQuery [ResolveStr "name" "McDonalds"]   result <- makeRequest token query-  assertEqual "Valid read query" "ok" (status result))+  assertEqual "Valid resolve query" "ok" (status result)) -crosswalkResponseTest :: Token -> Test-crosswalkResponseTest token = TestCase (do+crosswalkIntegrationTest :: Token -> Test+crosswalkIntegrationTest token = TestCase (do   let query = C.CrosswalkQuery { C.factualId = Just "97598010-433f-4946-8fd5-4a6dd1639d77"-                                   , C.limit = Nothing-                                   , C.namespace = Nothing-                                   , C.namespaceId = Nothing-                                   , C.only = ["loopt"] }+                               , C.limit = Nothing+                               , C.namespace = Nothing+                               , C.namespaceId = Nothing+                               , C.only = ["loopt"] }   result <- makeRequest token query+  assertEqual "Valid crosswalk query" "ok" (status result))++rawIntegrationTest :: Token -> Test+rawIntegrationTest token = TestCase (do+  result <- makeRawRequest token "/t/places?q=starbucks"   assertEqual "Valid read query" "ok" (status result)) +facetsIntegrationTest token = TestCase (do+  let query = F.FacetsQuery { F.table        = Places+                            , F.search       = AndSearch ["Starbucks"]+                            , F.select       = ["country"]+                            , F.filters      = []+                            , F.geo          = Nothing+                            , F.limit        = Just 100+                            , F.minCount     = Just 1+                            , F.includeCount = False }+  result <- makeRequest token query+  assertEqual "Valid facets query" "ok" (status result))++geopulseIntegrationTest token = TestCase (do+  let query = G.GeopulseQuery { G.geo    = Point 34.06021 (-118.41828)+                              , G.select = [] }+  result <- makeRequest token query+  assertEqual "Valid geopulse query" "ok" (status result))++geocodeIntegrationTest token = TestCase (do+  let query = GeocodeQuery $ Point 34.06021 (-118.41828)+  result <- makeRequest token query+  assertEqual "Valid geopulse query" "ok" (status result))+ blankReadQuery :: ReadQuery blankReadQuery = ReadQuery { table = Places                            , search = AndSearch []@@ -269,3 +363,18 @@                                        , C.namespace = Nothing                                        , C.namespaceId = Nothing                                        , C.only = [] }++submitWrite :: S.Submit+submitWrite = S.Submit { S.table     = Places+                       , S.user      = "user123"+                       , S.factualId = Just "foobar"+                       , S.values    = M.fromList [("key", "val")] }++flagWrite :: L.Flag+flagWrite = L.Flag { L.table     = Places+                   , L.factualId = "foobar"+                   , L.problem   = L.Duplicate+                   , L.user      = "user123"+                   , L.comment   = Just "There was a problem"+                   , L.debug     = False+                   , L.reference = Nothing }