Skip to content

Instantly share code, notes, and snippets.

@the-frey
Last active October 23, 2020 13:35
Show Gist options
  • Select an option

  • Save the-frey/c766fc8996a164417e013e6516aaa643 to your computer and use it in GitHub Desktop.

Select an option

Save the-frey/c766fc8996a164417e013e6516aaa643 to your computer and use it in GitHub Desktop.
'use strict';
import * as R from 'ramda';
import * as responses from '../../common/responses';
import * as db from '../../common/db';
import * as uDb from '../../common/users/db';
import * as uXfms from '../../common/users/transformations';
// welcome to the party, pal
import { pipe } from 'fp-ts/lib/pipeable';
import * as T from 'fp-ts/lib/Task';
import * as E from 'fp-ts/lib/Either';
import * as TE from 'fp-ts/lib/TaskEither';
import { Do } from 'fp-ts-contrib/lib/Do';
const getIdFromPath = (event): E.Either<responses.Response, string> => {
try {
const userId = event.pathParameters.id;
return E.right(userId);
} catch (err) {
return E.left(responses.respond400('your_project/error', 'Could not parse ID'));
}
};
const getUser = (client, userId: string): TE.TaskEither<responses.Response, any> => {
return TE.tryCatch(
// the anon fn creates a thunk, making the type Lazy<Promise<any>>
// this pattern lifts us from regular promise ctx to TE ctx
() => uDb.getUser(client, userId),
() => responses.respond404('your_project/error', 'User does not exist')
);
};
const getUserFromRes = (userRes): E.Either<responses.Response, User> => {
const user = userRes.rows[0];
if (R.isNil(user)) {
return E.left(responses.respond404('your_project/error', 'User does not exist'));
} else {
return E.right(user);
}
};
const userToPubliclyVisibleUser = (dbUser): E.Either<responses.Response, CCSharableUser> => {
try {
return E.right(uXfms.dbUserToSharableUser(dbUser));
} catch (err) {
return E.left(responses.respond500('your_project/error', 'Error while serialising User'));
}
};
// this is an API endpoint
// it has to return something comprehensible to the AWS SDK
// to guard against runtime errors we use the outer try/catch/finally
export const usersPublicGet = async (event): Promise<responses.Response> => {
const client = await db.getDbConn();
try {
const pipeline = Do(TE.taskEither)
.bind('userId', TE.fromEither(getIdFromPath(event)))
.bindL('getUserRes', ({userId}) => getUser(client, userId))
.bindL('user', ({getUserRes}) => TE.fromEither(getUserFromRes(getUserRes)))
.bindL('userWithoutPII', ({user}) => TE.fromEither(userToPubliclyVisibleUser(user)))
.return(({userWithoutPII}) => responses.respond200('your_project/user', userWithoutPII));
const run = pipe(
pipeline,
TE.fold(
errRes => T.of(errRes),
userRes => T.of(userRes)
));
// TE API used to be TE.run()
// but now you have to invoke with ()
const response = await run();
return response;
} catch (err) {
console.log(err);
return responses.respond500('your_project/error', 'Something went wrong');
} finally {
await db.closeDbConn(client);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment