Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exercise 4 #11

Merged
merged 13 commits into from Dec 14, 2020
Merged

Exercise 4 #11

merged 13 commits into from Dec 14, 2020

Conversation

MaykAkifovski
Copy link
Collaborator

@MaykAkifovski MaykAkifovski commented Dec 4, 2020

We have reviewed this PR

@MaykAkifovski MaykAkifovski requested review from a team, roschaefer and medizinmensch and removed request for a team December 4, 2020 16:18
@MaykAkifovski
Copy link
Collaborator Author

@Systems-Development-and-Frameworks/countryroads, there is a bug in our PR and we are not able to add you as a Code Reviewer. I have to manually go to our PR and make a code review.

@yiimnta
Copy link

yiimnta commented Dec 5, 2020

@Systems-Development-and-Frameworks/countryroads, there is a bug in our PR and we are not able to add you as a Code Reviewer. I have to manually go to our PR and make a code review.

Hi...
when i can review your code, please add me.
if possible, can i review a bit while waiting for fixing your bug?

Copy link

@yiimnta yiimnta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review on 12.06.2020 at commit "tests fixed(1f1eb90)"

The idea of using graph-shield to check the login (passwordIsValid) and to check the user has voted or not (isPostUpvoted) is quite good 👍 . And you can create 2 classes Post and User to give the code a more cool look. 😉

✔️ ⭐ For an implemented signup feature.

❌ ⭐ For a software-tested signup feature.

✔️ ⭐ For password validation.

✔️ ⭐ For unique email address validation.

✔️ ⭐ For not having security issues according to instructions.

✔️ ⭐ For an implemented login feature using JWT.

❌ ⭐ For a software-tested login feature.

✔️ ⭐ For assigning the authenticated user to a post in write and upvote mutations.

✔️ ⭐ For requesting a review and reviewing another team's PR according to the instructions.

❌ ⭐ For a successful rebase on homework/main.

Comment on lines +18 to 21
const userById = await dataSources.usersApi.getUserById(parent.author);
return {
name: parent.author
name: userById.name
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you return a name like this then it's not possible to nest your queries 'indefinitely':

{
   posts {
     title
     votes
     author {
       name
       posts {
         title
       }
     }
   }
}

as result, the author's posts will be an empty list.
Here you should return to a User instead of the author's name.

Suggested change
const userById = await dataSources.usersApi.getUserById(parent.author);
return {
name: parent.author
name: userById.name
};
return await dataSources.usersApi.getUserById(parent.author);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's 100% correct!

Comment on lines 177 to 193
it("creates new post - not authenticated", async () => {
const {
data: { write }
} = await mutate({ mutation: WRITE_POST, variables: { post: { title: "Title1", author: { name: "Max" } } } });
errors:[ error ]
} = await mutate({ mutation: WRITE_POST, variables: { post: { title: "Title1"}}});

expect(write).toEqual({title: "Title1", author: {name: "Max"}});
expect(error.message).toEqual('Not Authorised!');
});

it("throws error if author creates post with already existent title", async () => {
it("creates new post - authenticated", async () => {
reqMock.headers = {authorization: 'Bearer ' + atanas_auth_token};

const {
data: {write}
} = await mutate({ mutation: WRITE_POST, variables: { post: { title: "Title1"} } });

expect(write).toEqual({title: "Title1", author: {name: "Atanas"}});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1/2⭐ For assigning the authenticated user to a post in write mutations.

Comment on lines +231 to 247
it("upvotes a post - not authorized", async () => {
const {
errors:[ error ]
} = await mutate({ mutation: UPVOTE_POST, variables: { title: "Atanas's Post 1" } });

expect(error.message).toEqual('Not Authorised!');
});

it("upvotes a post - authorized", async () => {
reqMock.headers = {authorization: 'Bearer ' + atanas_auth_token};

const {
data: {upvote}
} = await mutate({ mutation: UPVOTE_POST, variables: { title: "The Nothing", voter: { name: "Max" } } });
expect(upvote).toMatchObject({ title: "The Nothing", votes: 1 });
data:{upvote},
} = await mutate({ mutation: UPVOTE_POST, variables: { title: "Atanas's Post 1" } });

expect(upvote).toMatchObject({ title: "Atanas's Post 1", votes: 1 });
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1/2⭐ For assigning the authenticated user to a post in upvote mutations.

Comment on lines +40 to 44
async login(parent, args, { dataSources }) {
const user = await dataSources.usersApi.getUserByEmail(args.email);
const token = await dataSources.authApi.createToken(user.id);
return token;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐ For an implemented login feature using JWT.

Comment on lines +35 to +39
async signup(parent, args, { dataSources }) {
const createdUser = await dataSources.usersApi.createUser(args.name, args.email, args.password);
const token = await dataSources.authApi.createToken(createdUser.id);
return token;
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐ For an implemented signup feature.

Mutation: {
"*": deny,
signup: and(
not(emailIsTaken, new UserInputError("A user with this email already exists.")),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐ For unique email address validation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, didn't know that not has a second error argument.

"*": deny,
signup: and(
not(emailIsTaken, new UserInputError("A user with this email already exists.")),
not(passwordIsTooShort, new UserInputError("The password must be at least 8 characters long."))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐ For password validation.

Comment on lines +29 to +30
const context = require('../context');
const contextPipeline = () => context({ req: reqMock, res: resMock });
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could easily use:

const contextPipeline = () => {}

when you want to authenticate a user, just use:

server.context = () => ({token: {uId: user_id}});

instead of creating these variables:

reqMock = { headers: {} };
resMock = {};

let userIds = [uuidv4(), uuidv4()];

mike_auth_token = await authApi.createToken(userIds[0]);
atanas_auth_token = await authApi.createToken(userIds[1]);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, mocking the context is fine and it's how I implemented the tests in my reference solution. However, this is copied from my example code for people who really want to test the requests headers for whatever reason.

Comment on lines 86 to 87
const GET_POSTS = gql`
query {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try testing the following query and you will see that the author's posts list will be empty.

const GET_POSTS = gql`
            query {
                    posts {
                        title
                        author {
                            name
                            posts {
                                title
                                author {
                                    name
                                }
                            }
                        }
                    }
                }`;

Copy link
Contributor

@roschaefer roschaefer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not bad

Yes, I agree with @yiimnta here. You achieved 7/10 🌠 well done!

⭐ For an implemented signup feature.

❌ For a software-tested signup feature.

⭐ For password validation.

⭐ For unique email address validation.

⭐ For not having security issues according to instructions.

❌ For an implemented login feature using JWT.

⭐ For a software-tested login feature.

⭐ For assigning the authenticated user to a post in write and upvote mutations.

⭐ For requesting a review and reviewing another team's PR according to the instructions.

❌ For a successful rebase on homework/main.

What got me the most is that you have put so much logic (also data-validations) into the permissions layer.

Regarding the rebase look here:

2020-12-10_22-59

See my suggestions below.

let token = req.headers.authorization || '';
token = token.replace('Bearer ', '');
try {
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to import a file here wich require('dotenv') and exports a constant JWT_SECRET. You would avoid a process.env.JWT_SECRET being undefined here, regardless of the order how files are loaded.

}

async createToken(userId) {
return jwt.sign({ uId: userId }, process.env.JWT_SECRET, { algorithm: 'HS256', expiresIn: '1h' });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expiresIn 👍

async getUser(name) {
return this.users.find(user => user.name === name);
async getUserById(id) {
return this.users.find(user => user.id == id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return this.users.find(user => user.id == id);
return this.users.find(user => user.id === id);

backend/src/datasources/userApi.js Outdated Show resolved Hide resolved
backend/src/datasources/userApi.js Show resolved Hide resolved
Comment on lines +29 to +30
const context = require('../context');
const contextPipeline = () => context({ req: reqMock, res: resMock });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, mocking the context is fine and it's how I implemented the tests in my reference solution. However, this is copied from my example code for people who really want to test the requests headers for whatever reason.

backend/src/tests/tests.test.js Show resolved Hide resolved
backend/src/tests/tests.test.js Show resolved Hide resolved
backend/src/tests/tests.test.js Show resolved Hide resolved
backend/src/tests/tests.test.js Outdated Show resolved Hide resolved
MaykAkifovski and others added 2 commits December 14, 2020 15:50
Co-authored-by: Robert Schäfer <git@roschaefer.de>
Co-authored-by: Robert Schäfer <git@roschaefer.de>
@MaykAkifovski MaykAkifovski merged commit f01584a into main Dec 14, 2020
@MaykAkifovski MaykAkifovski deleted the exercise_4 branch December 14, 2020 14:53
@MaykAkifovski MaykAkifovski restored the exercise_4 branch December 14, 2020 15:16
@MaykAkifovski MaykAkifovski deleted the exercise_4 branch January 29, 2021 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants