Skip to content

Instantly share code, notes, and snippets.

@Woody88
Last active August 3, 2018 05:10
Show Gist options
  • Select an option

  • Save Woody88/8dd5510c0fe3f937ce6ec92903f965b5 to your computer and use it in GitHub Desktop.

Select an option

Save Woody88/8dd5510c0fe3f937ce6ec92903f965b5 to your computer and use it in GitHub Desktop.
module Salesforce.SOQL.Query where
import Prelude
import Control.Monad.Except
import Control.Monad.Error.Class
import Data.Either (Either(..))
import Data.Function.Uncurried (Fn4, runFn4)
import Effect.Aff (Aff)
import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
import Foreign (Foreign, MultipleErrors)
import Foreign.Class (class Decode, decode)
import Foreign.Generic (genericDecode, decodeJSON, defaultOptions)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Salesforce.Types (Connection)
newtype SOQL result = SOQL String
data QueryError = QueryError String | SOQLParse MultipleErrors
type QueryResult r =
{ totalSize :: Int
, done :: Boolean
, records :: Array (Record r)
}
newtype SOQLResult r = SOQLResult
{ totalSize :: Int
, done :: Boolean
, records :: Array r
}
newtype SOQLResult' = SOQLResult'
{ totalSize :: Int
, done :: Boolean
, records :: Foreign
}
derive instance genericSOQLResult :: Generic (SOQLResult r) _
derive instance genericSOQLResult' :: Generic SOQLResult' _
instance decodeSOQLResult :: Decode r => Decode (SOQLResult r) where
decode = genericDecode $ defaultOptions {unwrapSingleConstructors = true}
instance decodeSOQLResult' :: Decode SOQLResult' where
decode = genericDecode $ defaultOptions {unwrapSingleConstructors = true}
queryString :: forall r. Connection -> String -> Aff (Either QueryError (QueryResult r))
queryString conn q = fromEffectFnAff $ runFn4 queryString_ conn q (Left <<< QueryError) Right
query :: forall result. Decode result => Connection -> SOQL result -> Aff (Either QueryError (Array result))
query conn soql = query' conn soql
>>= (\eitherSOQL ->
pure $ eitherSOQL
>>= (\(SOQLResult r) -> pure $ r.records))
query' :: forall result. Decode result => Connection -> SOQL result -> Aff (Either QueryError (SOQLResult result))
query' conn soql = querySOQLResult conn soql
>>= (\eitherSOQL ->
pure $ eitherSOQL
>>= (\(SOQLResult' q) -> decodeRecord q.records))
where
decodeRecord :: forall r. Decode r => Foreign -> Either QueryError r
decodeRecord rec = (parsingHandler <<< runExcept <<< decode $ rec)
querySOQLResult :: forall result. Decode result => Connection -> SOQL result -> Aff (Either QueryError (SOQLResult'))
querySOQLResult conn (SOQL q) = do
eitherF <- fromEffectFnAff $ runFn4 query_ conn q (Left <<< QueryError) Right
pure $ (eitherF >>= \x -> parsingHandler <<< runExcept <<< decode $ x)
-- where
-- decodeRecord :: forall r. Decode r => {totalSize :: Int, done :: Boolean , records :: Foreign} -> Either QueryError r
-- decodeRecord res = (parsingHandler <<< runExcept <<< decode $ res.records)
parsingHandler :: forall a. Decode a => Either MultipleErrors a -> Either QueryError a
parsingHandler (Left x) = throwError $ SOQLParse x
parsingHandler (Right x) = pure x
-- where decodeSOQL = runExcept <<< decodeJSON
-- decodeSOQL fv
-- where decodeOpts = defaultOptions { unwrapSingleConstructors = true }
-- decodeSOQL fail@(Left v) = fail
-- decodeSOQL (Right json) = runExcept <<< genericDecodeJSON decodeOpts $ json
{-- Type Related Issue
we the function below there is an assumption that assumption that JSForce provides valid data of type `sobject`.
And since it’s on the FFI boundary the compiler will trust you.
Based on Robert this is often referred as the `TypeScript Problem` :)
With generics, you replace the assumed type on the FFI declaration with `Foreign` and decode it in PureScript, giving it all the guarantees you would have if you had declared the data in PS (edited)
Generics adds compile-time static type reflection and typeclass-based code generation.
libraries needed `foreign-generic` + `generics-rep`
--}
foreign import queryString_ :: forall a b c sobject. Fn4 Connection a (String -> b) (QueryResult sobject -> b) (EffectFnAff b)
foreign import query_ :: forall a b. Fn4 Connection a (String -> b) (Foreign -> b) (EffectFnAff b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment