0
votes

I'm new to Purescript. I'm searching for an OAuth client and found this. I'm not sure how to build it, but this is what I've tried.

I've copied the source under the first project I've created following the getting started guide. The first error when I pulp build was

Error 1 of 3:

  at src\Network\OAuth.purs:138:1 - 138:1 (line 138, column 1 - line 138, column 1)

    Unable to parse module:
    expecting indentation past column 1

which I fixed by commenting out the unfinished tokenByAuthorizationToken, right?

But then I'm getting

Error 1 of 3:

  at src\Network\OAuth.purs:228:3 - 228:3 (line 228, column 3 - line 228, column 3)

    Unable to parse module:
    unexpected {
    expecting data constructor name

How am I supposed to fix the following code (assuming all I've done up till now is fine... which I don't know either)?

data TokenEndpointSuccessResponse a =
  { access_token :: a -- See 7.1: Access Token Types
  , token_type :: AccessTokenType
  , expires_in :: Maybe Seconds -- recommended
  , refresh_token :: Maybe RefreshToken
  , scope :: Maybe AccessScope
  }

I was looking at the documentation for types and syntax and I couldn't immediately spot the error (except what I'm writing next). In particular it seems a correct record definition, as per the language rule

PureScript records correspond to JavaScript objects. They may have zero or more named fields, each with their own types. For example: {name :: String, greet :: String -> String } corresponds to a JavaScript object with precisely two fields: name, which is a String, and greet, which is a function that takes a String and returns a String.

So I've done an experiment by adding a couple of lines before the error which are an extrapolation of the documentation for the data keyword and from another question about the record type

data Foo a = Foo | Bar a

type ThreeStringProps = {prop1:: string, prop2:: string, prop3:: string}

and both the above lines compile fine and also the following

type Foo a = {foo :: Foo | bar :: Bar a}

so I guess I have to replace data with type, but does it make sense? The following fix seems to work (but I have other erros in other files of the code from github...)

type TokenEndpointSuccessResponse a = { access_token :: a -- See 7.1: Access Token Types
  , token_type :: AccessTokenType
  , expires_in :: Maybe Seconds -- recommended
  , refresh_token :: Maybe RefreshToken
  , scope :: Maybe AccessScope
  }

Am I on right path anyway (to implement an OAuth client in Purescript)?

2

2 Answers

0
votes

As a practical matter, I would get in touch with chexxor and see what his thoughts are. This library was written before PureScript 0.12 and so will require numerous changes to make compatible.

type and data are two different things. type introduces a type synonym (another name for an existing type) whereas data introduces an algebraic data type (discriminated sums and products of other types).

Records are also something else. { a ∷ A } is definitionally equivalent to Record (a ∷ A).

Line 228 does not parse because a constructor name is missing. data X = { a ∷ A } is invalid syntax and data X = Y { a ∷ A } is valid syntax.

0
votes

I'm searching for an OAuth client and found this.

You've found an unfinished project, while the best approach would be writing the binding for the npm oauth module.

First of all, if you plan to integrate with Twitter, consider retrieving the access token by node run twitter_oauth_getter.js (notice both comments there).

Then replace the 2+2 secrets (app and user keys) in the oauth.get and finally export it as a module of uncurried functions.

For example, let's say you simply want to output some tweets, you can pass the log function as an effectful callback.

"use strict";
module.exports = {
sayHelloInEnglish: function() {
return "HELLO";
},

logMyTweets: function(callback) {
var OAuth = require('oauth');

    var oauth = new OAuth.OAuth(
      'https://api.twitter.com/oauth/request_token',
      'https://api.twitter.com/oauth/access_token',
      'app key',
      'app token',
      '1.0A',
      null,
      'HMAC-SHA1'
    );
    oauth.get(
      'https://api.twitter.com/1.1/lists/statuses.json?slug=develop&owner_screen_name=giuliohome_2017&count=2',
      //'https://api.twitter.com/1.1/trends/place.json?id=23424977',
      'user token', 
      'user secret', 
      function (e, data, res){
        if (e) console.error(e);        
        callback (data);
      }); 

}
};

Purescript binding

Now, following the above example, you'll write a Purescript binding (same name with .purs extension instead of .js)

module Twitting where
import Data.Unit
import Data.Function.Uncurried (Fn0, Fn1)
import Effect (Effect)
import Effect.Uncurried
foreign import sayHelloInEnglish ::  Fn0 String
foreign import logMyTweets ::  EffectFn1 (EffectFn1 String Unit) Unit

And the use case could be

module Main where

import Prelude
import Effect (Effect)
import Effect.Console (log)
import Twitting
import Data.Function.Uncurried (Fn0, runFn0, Fn1, runFn1)
import Effect.Uncurried (runEffectFn1, mkEffectFn1)

callback :: String -> Effect Unit
callback = log

main :: Effect Unit
main = do
  log "Hello sailor!"
  log (runFn0 sayHelloInEnglish)
  runEffectFn1 logMyTweets (mkEffectFn1 callback)

Next steps

Now, of course, we can proceed with Argonauts

Since our program is receiving JSON data as a string, we probably want the jsonParser function in Data.Argonaut.Parser, which is a very simple wrapper around JavaScript's JSON.parse. Otherwise, Json values can be introduced into our program via the FFI

concat :: forall a. Foldable a => a String -> String
concat tt =
  foldl (\a x -> x <> "\n" <> a) "" tt

showText :: Object A.Json -> String
showText tweet =
   maybe "no text" A.stringify (lookup "text" tweet)

toJsonObject :: A.Json -> String
toJsonObject j =
  A.caseJsonObject "not a json object " showText j

transform :: Array A.Json -> Array String
transform e = map toJsonObject e

showTweets :: A.Json -> Array String
showTweets tweets =
  A.caseJsonArray ["not a json array"] transform tweets

parse :: String -> String
parse j = 
  either (\x -> x <> "\n" <> j) (\x -> concat( showTweets x)) (jsonParser j)

callback :: String -> Effect Unit
callback j = do
  log (parse j)

Finally, assuming we are on server side, we will likely publish a web app, using a HTTP server like hyper