summaryrefslogtreecommitdiffstats
path: root/finished-application/backend/src/resolvers
diff options
context:
space:
mode:
authorRandy Ridge <randyridge@gmail.com>2018-09-14 20:01:25 -0400
committerGitHub <noreply@github.com>2018-09-14 20:01:25 -0400
commit908180c77cd38011eeedcae949613da2a3391e5e (patch)
treeb59bf1aae819a04d453cde72f6e601f5561a740f /finished-application/backend/src/resolvers
parent1f6667d233a4a2e9df977204d83a8f749c050c75 (diff)
parentb3bebda57ba187b7fa10398054b54c05d3fd3555 (diff)
Merge branch 'master' into randyridge/our
Diffstat (limited to 'finished-application/backend/src/resolvers')
-rw-r--r--finished-application/backend/src/resolvers/Mutation.js380
-rw-r--r--finished-application/backend/src/resolvers/Query.js65
2 files changed, 217 insertions, 228 deletions
diff --git a/finished-application/backend/src/resolvers/Mutation.js b/finished-application/backend/src/resolvers/Mutation.js
index 80ff3f2..f0ea6a8 100644
--- a/finished-application/backend/src/resolvers/Mutation.js
+++ b/finished-application/backend/src/resolvers/Mutation.js
@@ -1,16 +1,75 @@
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
-const { hasPermission } = require('../utils');
const { randomBytes } = require('crypto');
const { promisify } = require('util');
-const mail = require('../mail');
+const { transport, makeANiceEmail } = require('../mail');
+const { hasPermission } = require('../utils');
const stripe = require('../stripe');
-const mutations = {
- // Signup Mutations
+const Mutations = {
+ async createItem(parent, args, ctx, info) {
+ if (!ctx.request.userId) {
+ throw new Error('You must be logged in to do that!');
+ }
+
+ const item = await ctx.db.mutation.createItem(
+ {
+ data: {
+ // This is how to create a relationship between the Item and the User
+ user: {
+ connect: {
+ id: ctx.request.userId,
+ },
+ },
+ ...args,
+ },
+ },
+ info
+ );
+
+ console.log(item);
+
+ return item;
+ },
+ updateItem(parent, args, ctx, info) {
+ // first take a copy of the updates
+ const updates = { ...args };
+ // remove the ID from the updates
+ delete updates.id;
+ // run the update method
+ return ctx.db.mutation.updateItem(
+ {
+ data: updates,
+ where: {
+ id: args.id,
+ },
+ },
+ info
+ );
+ },
+ async deleteItem(parent, args, ctx, info) {
+ const where = { id: args.id };
+ // 1. find the item
+ const item = await ctx.db.query.item({ where }, `{ id title user { id }}`);
+ // 2. Check if they own that item, or have the permissions
+ const ownsItem = item.user.id === ctx.request.userId;
+ const hasPermissions = ctx.request.user.permissions.some(permission =>
+ ['ADMIN', 'ITEMDELETE'].includes(permission)
+ );
+
+ if (!ownsItem && hasPermissions) {
+ throw new Error("You don't have permission to do that!");
+ }
+
+ // 3. Delete it!
+ return ctx.db.mutation.deleteItem({ where }, info);
+ },
async signup(parent, args, ctx, info) {
+ // lowercase their email
args.email = args.email.toLowerCase();
+ // hash their password
const password = await bcrypt.hash(args.password, 10);
+ // create the user in the database
const user = await ctx.db.mutation.createUser(
{
data: {
@@ -21,153 +80,88 @@ const mutations = {
},
info
);
+ // create the JWT token for them
const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
+ // We set the jwt as a cookie on the response
ctx.response.cookie('token', token, {
- maxAge: 1000 * 60 * 60 * 24 * 365,
httpOnly: true,
+ maxAge: 1000 * 60 * 60 * 24 * 365, // 1 year cookie
});
+ // Finalllllly we return the user to the browser
return user;
},
-
- async signout(parent, args, ctx, info) {
- ctx.response.clearCookie('token');
- return { message: 'goodbye!' };
- },
-
async signin(parent, { email, password }, ctx, info) {
+ // 1. check if there is a user with that email
const user = await ctx.db.query.user({ where: { email } });
if (!user) {
- throw new Error(`No such user found for email: ${email}`);
+ throw new Error(`No such user found for email ${email}`);
}
-
+ // 2. Check if their password is correct
const valid = await bcrypt.compare(password, user.password);
if (!valid) {
- throw new Error('Invalid password');
+ throw new Error('Invalid Password!');
}
- // set the cookie
+ // 3. generate the JWT Token
const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
+ // 4. Set the cookie with the token
ctx.response.cookie('token', token, {
- maxAge: 1000 * 60 * 60 * 24 * 365,
httpOnly: true,
+ maxAge: 1000 * 60 * 60 * 24 * 365,
});
+ // 5. Return the user
return user;
},
-
- // Create An Item
- async createItem(parent, args, ctx, info) {
- if (!ctx.request.userId) {
- throw new Error('You must be logged in to create an item');
- }
-
- const item = await ctx.db.mutation.createItem(
- {
- data: {
- user: {
- connect: {
- id: ctx.request.userId,
- },
- },
- ...args,
- },
- },
- info
- );
- return item;
- },
-
- async deleteItem(parent, args, ctx, info) {
- const where = {
- id: args.id,
- };
- // 1. find the item
- const item = await ctx.db.query.item({ where }, `{ user {id}, title, id, description }`);
- // 2. if they 1. Don't own it AND 2. aren't an admin
- if (item.user.id !== ctx.request.user.id && !ctx.request.user.permissions.includes('ADMIN')) {
- throw new Error("You aren't allowed to delete that item!");
- }
-
- // 3. remove any orderItems this item is in
-
- return ctx.db.mutation.deleteItem({ where }, info);
- },
-
- async updateItem(parent, args, ctx, info) {
- const user = ctx.request.user;
- const item = await ctx.db.query.item({ where: { id: args.id } }, `{ user { id } }`);
-
- if (item.user.id !== user.id || !hasPermission(user, ['ADMIN'])) {
- throw new Error('You are not allowed to update that item!');
- }
-
- const updates = { ...args };
- // remove the ID because you can't update that
- delete updates.id;
- return ctx.db.mutation.updateItem(
- {
- where: { id: args.id },
- data: {
- ...updates,
- },
- },
- info
- );
+ signout(parent, args, ctx, info) {
+ ctx.response.clearCookie('token');
+ return { message: 'Goodbye!' };
},
-
- // Send password request
async requestReset(parent, args, ctx, info) {
- // 1. find if there is a user with that email
+ // 1. Check if this is a real user
const user = await ctx.db.query.user({ where: { email: args.email } });
-
if (!user) {
- throw new Error(`No user with the email ${args.email}`);
+ throw new Error(`No such user found for email ${args.email}`);
}
- // 2. Set a reset token, and a reset date
- const resetToken = (await promisify(randomBytes)(20)).toString('hex');
+ // 2. Set a reset token and expiry on that user
+ const randomBytesPromiseified = promisify(randomBytes);
+ const resetToken = (await randomBytesPromiseified(20)).toString('hex');
const resetTokenExpiry = Date.now() + 3600000; // 1 hour from now
- console.log({ resetToken, resetTokenExpiry });
const res = await ctx.db.mutation.updateUser({
where: { email: args.email },
data: { resetToken, resetTokenExpiry },
});
-
- // 3. Send them their token via email
- const mailRes = await mail.transport.sendMail({
- from: 'wesbos@gmail.com',
+ // 3. Email them that reset token
+ const mailRes = await transport.sendMail({
+ from: 'wes@wesbos.com',
to: user.email,
- subject: 'Your password reset token',
- html: mail.makeANiceEmail(
- `Your password reset link is here! \n\n<a href="${process.env
- .FRONTEND_URL}/reset?resetToken=${resetToken}">Click Here to reset</a>`
- ),
+ subject: 'Your Password Reset Token',
+ html: makeANiceEmail(`Your Password Reset Token is here!
+ \n\n
+ <a href="${process.env
+ .FRONTEND_URL}/reset?resetToken=${resetToken}">Click Here to Reset</a>`),
});
- return res.updateUser;
- },
+ // 4. Return the message
+ return { message: 'Thanks!' };
+ },
async resetPassword(parent, args, ctx, info) {
- // 1. Check that the passwords match
+ // 1. check if the passwords match
if (args.password !== args.confirmPassword) {
- throw new Error('Passwords do not match');
+ throw new Error("Yo Passwords don't match!");
}
-
- // 2. Check that this is a legit resetToken
- // 3. Check that it's not expired
- // Note: If we didn't need the user here, we could also use db.exists()
+ // 2. check if its a legit reset token
+ // 3. Check if its expired
const [user] = await ctx.db.query.users({
where: {
resetToken: args.resetToken,
- resetTokenExpiry_gte: Date.now() - 3600000, // within the last hour
+ resetTokenExpiry_gte: Date.now() - 3600000,
},
});
-
if (!user) {
- throw new Error('This token is either invalid or expired.');
+ throw new Error('This token is either invalid or expired!');
}
-
- // 4. Hash the password
+ // 4. Hash their new password
const password = await bcrypt.hash(args.password, 10);
-
- // 5. Update the users password
- // clean up the resetToken fields at the same time
+ // 5. Save the new password to the user and remove old resetToken fields
const updatedUser = await ctx.db.mutation.updateUser({
where: { email: user.email },
data: {
@@ -176,34 +170,63 @@ const mutations = {
resetTokenExpiry: null,
},
});
+ // 6. Generate JWT
const token = jwt.sign({ userId: updatedUser.id }, process.env.APP_SECRET);
+ // 7. Set the JWT cookie
ctx.response.cookie('token', token, {
- maxAge: 1000 * 60 * 60 * 24 * 365,
httpOnly: true,
+ maxAge: 1000 * 60 * 60 * 24 * 365,
});
-
- // 6. send back the User for the GraphQL request on the client
+ // 8. return the new user
return updatedUser;
},
- /*
- Add to cart
- */
+ async updatePermissions(parent, args, ctx, info) {
+ // 1. Check if they are logged in
+ if (!ctx.request.userId) {
+ throw new Error('You must be logged in!');
+ }
+ // 2. Query the current user
+ const currentUser = await ctx.db.query.user(
+ {
+ where: {
+ id: ctx.request.userId,
+ },
+ },
+ info
+ );
+ // 3. Check if they have permissions to do this
+ hasPermission(currentUser, ['ADMIN', 'PERMISSIONUPDATE']);
+ // 4. Update the permissions
+ return ctx.db.mutation.updateUser(
+ {
+ data: {
+ permissions: {
+ set: args.permissions,
+ },
+ },
+ where: {
+ id: args.userId,
+ },
+ },
+ info
+ );
+ },
async addToCart(parent, args, ctx, info) {
- const userId = ctx.request.userId;
-
+ // 1. Make sure they are signed in
+ const { userId } = ctx.request;
if (!userId) {
- throw new Error('You must be signed in to add to cart!');
+ throw new Error('You must be signed in soooon');
}
-
- // 1. Check if there is a CartItem for this user and item already
+ // 2. Query the users current cart
const [existingCartItem] = await ctx.db.query.cartItems({
where: {
user: { id: userId },
item: { id: args.id },
},
});
-
+ // 3. Check if that item is already in their cart and increment by 1 if it is
if (existingCartItem) {
+ console.log('This item is already in their cart');
return ctx.db.mutation.updateCartItem(
{
where: { id: existingCartItem.id },
@@ -212,15 +235,12 @@ const mutations = {
info
);
}
-
- // Otherwise create a new cartItem
+ // 4. If its not, create a fresh CartItem for that user!
return ctx.db.mutation.createCartItem(
{
data: {
user: {
- connect: {
- id: userId,
- },
+ connect: { id: userId },
},
item: {
connect: { id: args.id },
@@ -230,126 +250,88 @@ const mutations = {
info
);
},
-
- // delete that cart item
async removeFromCart(parent, args, ctx, info) {
- console.log(args.id);
- // 1. Find the CartItem
+ // 1. Find the cart item
const cartItem = await ctx.db.query.cartItem(
{
- where: { id: args.id },
+ where: {
+ id: args.id,
+ },
},
- `{ id, user { id, permissions }}`
+ `{ id, user { id }}`
);
- // 2. Check they own it
+ // 1.5 Make sure we found an item
+ if (!cartItem) throw new Error('No CartItem Found!');
+ // 2. Make sure they own that cart item
if (cartItem.user.id !== ctx.request.userId) {
- throw new Error("Cheatin' huh");
+ throw new Error('Cheatin huhhhh');
}
- // 3. Delete it
+ // 3. Delete that cart item
return ctx.db.mutation.deleteCartItem(
{
- where: {
- id: args.id,
- },
+ where: { id: args.id },
},
info
);
},
-
async createOrder(parent, args, ctx, info) {
- const userId = ctx.request.userId;
+ // 1. Query the current user and make sure they are signed in
+ const { userId } = ctx.request;
+ if (!userId) throw new Error('You must be signed in to complete this order.');
const user = await ctx.db.query.user(
{ where: { id: userId } },
- '{ id, name, email, cart { id, quantity, item { title, price, id, description, image } }}'
+ `{
+ id
+ name
+ email
+ cart {
+ id
+ quantity
+ item { title price id description image largeImage }
+ }}`
);
- // 1. Recalculate the total for the price
+ // 2. recalculate the total for the price
const amount = user.cart.reduce(
(tally, cartItem) => tally + cartItem.item.price * cartItem.quantity,
0
);
- // 2. Create a stripe charge
+ console.log(`Going to charge for a total of ${amount}`);
+ // 3. Create the stripe charge (turn token into $$$)
const charge = await stripe.charges.create({
amount,
- currency: 'usd',
+ currency: 'USD',
source: args.token,
});
-
- // 3. convert the items they want to OrderItems
+ // 4. Convert the CartItems to OrderItems
const orderItems = user.cart.map(cartItem => {
- console.log(cartItem);
const orderItem = {
- quantity: cartItem.quantity,
- // copy all the item details so it's there forever
...cartItem.item,
- // item: {
- // // relationship to the Item incase we need it
- // connect: { id: cartItem.item.id },
- // },
- user: { connect: { id: user.id } },
+ quantity: cartItem.quantity,
+ user: { connect: { id: userId } },
};
- // scrub the ID from it because the orderItem will have it's own ID
delete orderItem.id;
return orderItem;
});
- // 4. Create the Order
+ // 5. create the Order
const order = await ctx.db.mutation.createOrder({
data: {
total: charge.amount,
charge: charge.id,
- items: {
- create: orderItems,
- },
- user: {
- connect: {
- id: user.id,
- },
- },
+ items: { create: orderItems },
+ user: { connect: { id: userId } },
},
});
-
- // 5. Clean up, clear the users cart and send back { user, order }
- // Delete the users current cart items
+ // 6. Clean up - clear the users cart, delete cartItems
const cartItemIds = user.cart.map(cartItem => cartItem.id);
await ctx.db.mutation.deleteManyCartItems({
where: {
id_in: cartItemIds,
},
});
-
- // 6. Send the order back to the client
+ // 7. Return the Order to the client
return order;
},
-
- async updateUser(parent, args, ctx, info) {
- const userId = ctx.request.userId;
- const updatedUser = await ctx.db.mutation.updateUser(
- {
- data: args,
- where: { id: userId },
- },
- info
- );
- return updatedUser;
- },
-
- async updatePermissions(parent, args, ctx, info) {
- const userId = ctx.request.userId;
- const currentUser = await ctx.db.query.user({ where: { id: userId } }, info);
- if (!currentUser) throw new Error('You Must be logged in to updat permissions!');
- hasPermission(currentUser, ['ADMIN', 'PERMISSIONUPDATE']);
- return ctx.db.mutation.updateUser(
- {
- data: {
- permissions: {
- set: args.permissions,
- },
- },
- where: { id: args.userId },
- },
- info
- );
- },
};
-module.exports = mutations;
+module.exports = Mutations;
diff --git a/finished-application/backend/src/resolvers/Query.js b/finished-application/backend/src/resolvers/Query.js
index 51c2c42..22a6414 100644
--- a/finished-application/backend/src/resolvers/Query.js
+++ b/finished-application/backend/src/resolvers/Query.js
@@ -1,52 +1,59 @@
-const { hasPermission } = require('../utils');
-
const { forwardTo } = require('prisma-binding');
+const { hasPermission } = require('../utils');
const Query = {
items: forwardTo('db'),
+ item: forwardTo('db'),
itemsConnection: forwardTo('db'),
-
- async order(parent, args, ctx, info) {
- // 1. make sure they are signed in
+ me(parent, args, ctx, info) {
+ // check if there is a current user ID
if (!ctx.request.userId) {
- throw new Error('You Must be signed in to view an order');
+ return null;
}
-
- // 2. Create the query
- const where = {
- id: args.id,
- user: {
- id: ctx.request.userId,
+ return ctx.db.query.user(
+ {
+ where: { id: ctx.request.userId },
},
- };
- // 3. Fire off the query
- const [order] = await ctx.db.query.orders({ where }, info);
-
- // 4. Check that they are allowed to view the order
- if (order.user.id !== ctx.request.userId || hasPermission(ctx.request.user, ['ADMIN'])) {
- throw new Error("You don't have permission");
- }
- // 5. If everything checks out, return the order
- return order;
+ info
+ );
},
-
- me(parent, args, ctx, info) {
+ async users(parent, args, ctx, info) {
+ // 1. Check if they are logged in
if (!ctx.request.userId) {
- return null; // don't error out, just return nothing
+ throw new Error('You must be logged in!');
}
+ console.log(ctx.request.userId);
+ // 2. Check if the user has the permissions to query all the users
+ hasPermission(ctx.request.user, ['ADMIN', 'PERMISSIONUPDATE']);
- return ctx.db.query.user(
+ // 2. if they do, query all the users!
+ return ctx.db.query.users({}, info);
+ },
+ async order(parent, args, ctx, info) {
+ // 1. Make sure they are logged in
+ if (!ctx.request.userId) {
+ throw new Error('You arent logged in!');
+ }
+ // 2. Query the current order
+ const order = await ctx.db.query.order(
{
- where: { id: ctx.request.userId },
+ where: { id: args.id },
},
info
);
+ // 3. Check if the have the permissions to see this order
+ const ownsOrder = order.user.id === ctx.request.userId;
+ const hasPermissionToSeeOrder = ctx.request.user.permissions.includes('ADMIN');
+ if (!ownsOrder || !hasPermission) {
+ throw new Error('You cant see this buddd');
+ }
+ // 4. Return the order
+ return order;
},
-
async orders(parent, args, ctx, info) {
const { userId } = ctx.request;
if (!userId) {
- throw new Error('You must be signed in to see your orders');
+ throw new Error('you must be signed in!');
}
return ctx.db.query.orders(
{