Edgar
Edgar

Reputation: 927

How to rewrite this function in FP (Ramda.js)?

I have this function and I wonder, how should one write it in FP style (with Ramda.js).

  1. registerUser(username, password) -> client_id, client_secret;
  2. getUserAccessToken(username, password, client_id, client_secret) -> access_token, refresh_token, refreshTokenExpiresAt;
  3. save to storage: client_id, client_secret, access_token, refresh_token, refreshTokenExpiresAt.
async function registerUser(username: string, password: string) {
  try {
    const { client_id, client_secret } = await authAPI.registerUser(username, password);

    // get tokens
    const {
      access_token,
      refresh_token,
      refreshTokenExpiresAt,
    } = await authAPI.getUserAccessToken(username, password, client_id, client_secret);

    // save to async storage
    store.update('user', {
      client_id,
      client_secret,
      access_token,
      refresh_token,
      refreshTokenExpiresAt,
    });
  } catch (err) {
    throw Error(err);
  }
}

Upvotes: 0

Views: 141

Answers (1)

Jared Smith
Jared Smith

Reputation: 21983

This has nothing to do with Ramda or FP. You've got one function doing three different things (auth, token request, and persistence) and that's your problem.

const registered = authAPI.registerUser(username, password)
  .catch(err => showUserRegisterErr(err));

const token = registered.then(({client_id, client_secret}) => {
  return Promise.all([
    Promise.resolve({client_id, client_secret}), 
    authAPI.getUserAccessToken(
      username,
      password, 
      client_id, 
      client_secret
    )
  ]);
});

const persisted = token.then((
  {client_id, client_secret},
  {access_token, refresh_token, refreshTokenExpiresAt}
) => {
  return store.update('user', {
    client_id,
    client_secret,
    access_token,
    refresh_token,
    refreshTokenExpiresAt,
  });
});

persisted.catch(err => { throw err; });

Now these are all constant values, not functions, so lets fix that:

// No .catch, caller can catch errors if it wants. Also
// no more registerUser function, it was a thin layer over
// the auth API with error handling and we'll delegate to
// the caller for that.
const getToken = (username, password, registration) => {
  return registration.then(({client_id, client_secret}) => {
    return Promise.all([
      username,
      password,
      client_id,
      client_secret,
      authAPI.getUserAccessToken(
        username,
        password, 
        client_id, 
        client_secret
      )
    ]);
  });
};

const persist = datastore => ([
  client_id,
  client_secret,
  {
    access_token,
    refresh_token,
    refreshTokenExpiresAt
  }
]) => {
  return store.update('user', {
    client_id,
    client_secret,
    access_token,
    refresh_token,
    refreshTokenExpiresAt,
  });
});

const persistToDB = persist(store);
const signUp = (username, password) => {
  return getToken(username, password, authAPI.registerUser(username, password))
    .then(persistToDB)
    .catch(showUserRegistrationErr);
};

Now all the different pieces are independently testable. Since the registration is passed as a parameter to getToken you can test it with a mock promise. Likewise with the persistence function. You can also mockout the authAPI.registerUser function.

No state is mutated except in the persistence function, functions use only their parameters, etc. I also parameterized the datastore so that you can swap out persistence (or stub it for tests).

Upvotes: 2

Related Questions