Skip to content

Commit 5b8789e

Browse files
committed
Implement graphQL error handling
1 parent bf5434a commit 5b8789e

File tree

6 files changed

+53
-159
lines changed

6 files changed

+53
-159
lines changed

app/src/Dashboard/ProjectContainer.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ import Project from './Project.tsx';
99
// 2) useQuery hook will update the data stored in Apollo Client's cache and automatically trigger child components rendering
1010

1111
const ProjectContainer = () => {
12-
<<<<<<< HEAD
13-
=======
14-
15-
>>>>>>> typescript
1612
let myVar = {};
1713
// Need this for the individual user dasboard, for now, dashboard shows all projects from all users
1814
const userSSID = window.localStorage.getItem('ssid') || 'unavailable';

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
"@types/enzyme": "^3.10.5",
171171
"@types/enzyme-adapter-react-16": "^1.0.6",
172172
"@types/jest": "^25.1.5",
173+
"apollo": "^2.32.5",
173174
"babel-eslint": "^8.2.6",
174175
"babel-jest": "^25.2.4",
175176
"babel-loader": "^8.1.0",

server/graphQL/resolvers/mutation.js

Lines changed: 36 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,74 @@
1-
const { Tests, Projects } = require('../../models/reactypeModels');
2-
1+
const { UserInputError } = require('apollo-server-express');
2+
const { Projects, Users } = require('../../models/reactypeModels');
33
/*
44
* resolvers are functions that handles graphQL requests. This file defines the logic for graphQL mutation requests
55
* Link to Apollo Mutations:
66
* https://www.apollographql.com/docs/apollo-server/data/resolvers/#defining-a-resolver
77
*/
88

9-
const Test = {
10-
addTest: async (parent, args) => {
11-
const resp = await Tests.create({ name: args.name, likes: args.likes });
12-
console.log('Added test', resp);
13-
if (resp) return { description: resp.name, id: resp._id, likes: args.likes };
14-
return { description: 'Error creating test', id: 'unknown', likes: 0 };
15-
},
16-
17-
updateTest: async (parent, args) => {
18-
// console.log('args >>> ', args);
19-
const filter = { _id: args.id };
20-
const update = { name: args.name, likes: args.likes };
21-
const options = { new: true };
22-
const resp = await Tests.findOneAndUpdate(filter, update, options);
23-
24-
console.log('Updated database with', resp);
25-
26-
if (resp) return { description: resp.name, id: resp._id, likes: resp.likes };
27-
return { description: 'Error updating', id: resp._id, likes: 0 };
28-
},
29-
30-
deleteTest: async (parent, args) => {
31-
const filter = { _id: args.id };
32-
const resp = await Tests.deleteOne(filter);
33-
if (resp) return { description: resp.name, id: resp._id, likes: 0 };
34-
return { description: 'Error updating', likes: 0 };
35-
},
36-
};
37-
38-
399
const Project = {
4010
addLike: async (parent, { projId, likes }) => {
4111
const filter = { _id: projId };
4212
const update = { likes };
4313
const options = { new: true };
4414
const resp = await Projects.findOneAndUpdate(filter, update, options);
45-
15+
4616
if (resp) {
4717
return ({
4818
name: resp.name,
4919
id: resp._id,
5020
userId: resp.userId,
51-
username: resp.username,
5221
likes: resp.likes,
5322
});
5423
}
5524

56-
// TODO: Go back to this to see how to handle error later
57-
return {
58-
name: 'Error',
59-
id: 'Error',
60-
userId: 'Error',
61-
username: 'Error',
62-
likes: -1,
63-
};
25+
throw new UserInputError('Project is not found. Please try another project ID', {
26+
argumentName: 'projId',
27+
});
6428
},
6529

6630
makeCopy: async (parent, { projId, userId, username }) => {
6731
const filter = { _id: projId };
6832
const target = await Projects.findOne(filter);
69-
70-
// make a copy with the passed in userId
71-
// IMPORTANT: DO NOT CHANGE copy.name, it will create a nother copy of the document with the origional project name.
72-
const copy = {
73-
name: target.name,
74-
project: target.project,
75-
userId,
76-
username,
77-
};
78-
79-
// IMPORTANT: MUST MAKE A DEEP COPY OF target.project, otherwise, field 'style' is lost during the copy process. See 'minimize' option in projectSchema
80-
81-
const resp = await Projects.create(copy);
8233

83-
if (resp) {
84-
return ({
85-
name: resp.name,
86-
id: resp._id,
87-
userId: resp.userId,
88-
username: resp.username,
89-
likes: resp.likes,
34+
if (!target) {
35+
throw new UserInputError('Project is not found. Please try another project ID', {
36+
argumentName: 'projId',
9037
});
9138
}
9239

93-
// TODO: Go back to this to see how to handle error later
94-
return {
95-
name: 'Error',
96-
id: 'Error',
97-
userId: 'Error',
98-
username: 'Error',
99-
likes: -1,
100-
};
101-
},
40+
// check if user account exists
41+
const user = await Users.findOne({ _id: userId });
42+
console.log ('User >>> ', user);
43+
if (user) {
44+
// make a copy with the passed in userId
45+
const copy = {
46+
name: target.name,
47+
project: target.project,
48+
userId,
49+
username,
50+
};
10251

103-
deleteProject: async (parent, { projId }) => {
104-
const filter = { _id: projId };
105-
const options = { strict: true };
106-
const resp = await Projects.findOneAndDelete(filter, options);
107-
// console.log("resp", resp);
108-
if (resp) {
109-
return ({
110-
name: resp.name,
111-
likes: resp.likes,
112-
id: resp._id,
113-
userId: resp.userId,
114-
username: resp.username,
115-
});
52+
// Make a copy of the requested project
53+
const resp = await Projects.create(copy);
54+
if (resp) {
55+
return ({
56+
name: resp.name,
57+
id: resp._id,
58+
userId: resp.userId,
59+
username: resp.username,
60+
likes: resp.likes,
61+
})}
62+
63+
throw new UserInputError('Internal Server Error');
11664
}
117-
return {
118-
name: 'Error',
119-
id: 'Error',
120-
userId: 'Error',
121-
username: 'Error',
122-
likes: -1,
123-
};
65+
66+
throw new UserInputError('User is not found. Please try another user ID', {
67+
argumentName: 'userId',
68+
});
12469
},
12570
};
12671

12772
module.exports = {
128-
TestMutation: Test,
12973
ProjectMutation: Project,
13074
};

server/graphQL/resolvers/query.js

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,13 @@
1-
const { Tests, Projects, Users } = require('../../models/reactypeModels');
2-
1+
const { UserInputError } = require('apollo-server-express');
2+
const { Projects } = require('../../models/reactypeModels');
33
// Link to Apollo Query Types:
44
// https://www.apollographql.com/docs/apollo-server/data/resolvers/#defining-a-resolver
55

6-
const Test = {
7-
8-
readTest: async (parent, args) => {
9-
const resp = await Tests.findOne({ _id: args.id });
10-
if (resp) return { description: resp.name, id: resp._id, likes: resp.likes };
11-
return { description: 'Error reading', id: '0', likes: '0' };
12-
},
13-
14-
readAllTests: async () => {
15-
const resp = await Tests.find({});
16-
// console.log('resp', resp);
17-
if (resp) {
18-
return resp.map(elem => ({ description: elem.name, id: elem._id, likes: elem.likes }));
19-
}
20-
// TODO: Go back to this to see how to handle error later
21-
return [{ description: 'Error reading all tests', id: '0', likes: '0' }];
22-
},
23-
24-
};
25-
266

277
const Project = {
288
getProject: async (parent, { projId }) => {
299
const resp = await Projects.findOne({ _id: projId });
10+
// console.log('getProject return >>> ', resp);
3011
if (resp) {
3112
return ({
3213
name: resp.name,
@@ -37,14 +18,10 @@ const Project = {
3718
});
3819
}
3920

40-
// TODO: Go back to this to see how to handle error later
41-
return {
42-
name: 'Error',
43-
id: 'Error',
44-
userId: 'Error',
45-
username: 'Error',
46-
likes: -1,
47-
};
21+
// resp is null if nothing is found based on the project ID
22+
throw new UserInputError('Project is not found. Please try another project ID', {
23+
argumentName: 'projId',
24+
});
4825
},
4926

5027
getAllProjects: async (parent, { userId }) => {
@@ -54,6 +31,10 @@ const Project = {
5431
if (userId) {
5532
// use loosely equal for the callback because there are some discrepancy between the type of userId from the db vs from the mutation query
5633
resp = resp.filter(proj => proj.userId == userId);
34+
// if resp = [] after the filtering, this means the userId doesnt exisit in the database, throw error as follow
35+
throw new UserInputError(`Project for userId: "${userId}". Please try another id`, {
36+
argumentName: 'userId',
37+
});
5738
}
5839

5940
if (resp) {
@@ -66,20 +47,13 @@ const Project = {
6647
}));
6748
}
6849

69-
// TODO: Go back to this to see how to handle error later
70-
return [{
71-
name: 'Error',
72-
id: 'Error',
73-
userId: 'Error',
74-
username: 'Error',
75-
likes: -1,
76-
}];
50+
// resp is null, return error message
51+
throw new UserInputError('Internal Server Error');
7752
},
7853

7954

8055
};
8156

8257
module.exports = {
83-
TestQuery: Test,
8458
ProjectQuery: Project,
8559
};

server/graphQL/typeDefs.js

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
1-
21
const { gql } = require('apollo-server-express');
32

43
// Link to defining a schema in Apollo:
54
// https://www.apollographql.com/docs/apollo-server/schema/schema/
65
// The schema specifies which queries and mutations are available for clients
76
// to execute against your data graph.
87

9-
const Test = gql`
10-
11-
type Mutation {
12-
addTest(name: String, likes: Int): Test
13-
updateTest(id: ID, name: String, likes: Int): Test
14-
deleteTest(id: ID): Test
15-
}
16-
type Test {
17-
description: String
18-
likes: Int
19-
id: ID
20-
}
21-
type Query {
22-
readTest(id: ID): Test
23-
readAllTests: [Test]
24-
}
25-
`;
26-
278
//NOTE: Project type does not return the detail of the project's components, but info needed for the dashboard
289
const Project = gql`
2910
@@ -48,6 +29,5 @@ const Project = gql`
4829
`;
4930

5031
module.exports = {
51-
typeDefsTest: Test,
5232
typeDefsProject: Project,
5333
};

server/server.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ GraphQl Router
5959
const { ApolloServer } = require('apollo-server-express');
6060

6161
// Query resolvers
62-
const { TestQuery, ProjectQuery } = require('./graphQL/resolvers/query');
62+
const { ProjectQuery } = require('./graphQL/resolvers/query');
6363
// Mutation resolvers
64-
const { TestMutation, ProjectMutation } = require('./graphQL/resolvers/mutation');
64+
const { ProjectMutation } = require('./graphQL/resolvers/mutation');
6565

6666
// package resolvers into one variable to pass to Apollo Server
6767
const resolvers = {
@@ -70,15 +70,14 @@ const resolvers = {
7070
};
7171

7272
// schemas used for graphQL
73-
const { typeDefsTest, typeDefsProject } = require('./graphQL/typeDefs');
73+
const { typeDefsProject } = require('./graphQL/typeDefs');
7474

7575
// instantiate Apollo server and attach to Express server, mounted at 'http://localhost:PORT/graphql'
7676
const server = new ApolloServer({ typeDefs: typeDefsProject, resolvers });
7777
server.applyMiddleware({ app });
7878
/** ****************************************************************** */
7979

8080

81-
8281
app.post(
8382
'/signup',
8483
userController.createUser,

0 commit comments

Comments
 (0)