Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
08415cb
Install @nuxtjs/apollo and connect with backend
yiimnta Jan 11, 2021
75a73c2
Adding css and component NavBar
yiimnta Jan 12, 2021
89ba70c
Adding Login and Signup (Not decode yet)
yiimnta Jan 12, 2021
ad0efcc
add jwt-decode, fix login-signup, show button based on authentication…
yiimnta Jan 13, 2021
88af2df
add getters, mutations, action of store. Refractoring. edit login, si…
yiimnta Jan 16, 2021
7cbdf0b
delete plugin get-token
yiimnta Jan 16, 2021
a78e105
add store file and install apollo
yiimnta Jan 18, 2021
e087ade
add graphql files and components, pages for NavBar, Login and Signup
yiimnta Jan 18, 2021
7e332fd
Add middleware to redirect from /login to / when already logged in
yiimnta Jan 18, 2021
964df68
button that behaves according to the authentication state of user
yiimnta Jan 18, 2021
8c920a7
using property authored of post
yiimnta Jan 18, 2021
aa9a4de
fix lint backend
yiimnta Jan 19, 2021
168af79
add test LoginForm and SignupForm.
yiimnta Jan 19, 2021
33fac0f
adding README.md of ex-6 and 7
yiimnta Jan 19, 2021
dee07c6
Using apolloProvider.defaultClient to replace the variable "apollo"
yiimnta Jan 22, 2021
8f1d157
Merge branch 'exercise-7' of https://github.com/Systems-Development-a…
ilonae Jan 22, 2021
bc972ea
add wpa config manifest for service worker
Jan 23, 2021
c39973d
edit webapp readme
Jan 23, 2021
b5bdbb0
WIP: voting and creating posts (almost) working
ilonae Jan 23, 2021
e616ca6
Merge branch 'exercise-7' of https://github.com/Systems-Development-a…
ilonae Jan 23, 2021
a121656
Update mutations.js
yiimnta Jan 23, 2021
2500072
WIP: adding intermediate tests
ilonae Jan 24, 2021
fa8a36c
WIP:fixing CreateNews tests and progressing with ListNews
ilonae Jan 25, 2021
7de180a
WIP: update on News tests
ilonae Jan 25, 2021
cf45f18
update Login- ListNews-Tests
yiimnta Jan 25, 2021
5a8b169
Update ci_lint_test.yml
ilonae Jan 25, 2021
b5fd300
fix name
yiimnta Jan 26, 2021
eb8556c
adding 404 Page, fix middleware, fix errors and editing News and its …
yiimnta Jan 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci_lint_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Testing frontend with Node.js ${{ matrix.node-version }}
run: |
yarn install
yarn test:unit
yarn test

lint-backend:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion backend/src/Test/posts.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ describe("mutations", () => {
it('upvote a post only once', async () => {
await expect(upvote_action())
.resolves.toMatchObject({
errors: [expect.objectContaining({ message: "This user voted on that post already." })],
errors: [expect.objectContaining({ message: "This user upvoted on that post already." })],
data: {
upvote: null,
},
Expand Down
41 changes: 9 additions & 32 deletions backend/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const bcrypt = require('bcrypt');

const NEW_VOTE = 0
const VOTE_AGAIN = 1
const NOT_ALLOWED_VOTE = -1
const NOT_ALLOWED_UPVOTE = -1
const NOT_ALLOWED_DOWNVOTE = -2

const login = async(args, executor, context) => {
const document = gql`
Expand Down Expand Up @@ -145,7 +146,7 @@ const mayVote = async(userId, postId, val, executor) => {
if(voters) {
if(voters.length > 0) { // User already has voted (max length = 1)
if(voters[0].value === val) {
voteType = NOT_ALLOWED_VOTE;
voteType = val === -1 ? NOT_ALLOWED_DOWNVOTE : NOT_ALLOWED_UPVOTE;
} else {
voteType = VOTE_AGAIN; //user is allowed vote again.
voterId = voters[0].id
Expand Down Expand Up @@ -176,8 +177,12 @@ const votePost = async(userId, postId, val, schema, executor, context, info) =>

const { voteType, voterId } = await mayVote(userId, postId, val, executor)

if(voteType === NOT_ALLOWED_VOTE) {
throw new UserInputError("This user voted on that post already.");
if(voteType === NOT_ALLOWED_UPVOTE) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the voteType is unnecessary state and your nested if-clause is very complex and hard to read. Try to use guard-clauses and throw an error in case. The execution will stop if an error occurs.

throw new UserInputError("This user upvoted on that post already.");
}

if(voteType === NOT_ALLOWED_DOWNVOTE) {
throw new UserInputError("This user downvoted on that post already.");
}

let variables = {}
Expand Down Expand Up @@ -246,34 +251,6 @@ const votePost = async(userId, postId, val, schema, executor, context, info) =>
return null;
}

const checkForExistingPost= async(userId, postId,value, executor ) => {

const param = {
data:{
person:{
connect: {id:userId}
},
post:{
connect:{id:postId}
},
value
}
}

let document = gql`
mutation ($data: VoterCreateInput!) {
createVoter(data: $data) {
id
}
}
`;
const { data, errors } = await executor({ document, variables : {data: param.data} });
if (errors) throw new UserInputError(errors.map((e) => e.message).join('\n'));
const { createVoter } = data;
return createVoter != null && createVoter.length == 0;

}

const writePost = async(userId, args, schema, executor, context, info) => {
if(!await checkUserExist(userId, executor)) { //user is not exist
throw new AuthenticationError("Sorry, your credentials are wrong!");
Expand Down
65 changes: 65 additions & 0 deletions exercises/6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Exercise #6

| Deadline | Date |
| -------------------- | ------------------- |
| **Due date** | **All optional :)** |

## Goal

In this Christmas exercise, it is time to plug frontend, backend and database
together and finally deploy your application to show it to your friends
and families!

## Instructions

1. Install `vue-apollo` in your frontend and call the backend from there.
Refactor your frontend code so that all data comes from your backend. Certain
groups have already [implemented this](https://github.com/Systems-Development-and-Frameworks/lichtow/tree/origin/main/webapp)
in previous exercises.

2. Build your frontend and upload the files to a static webhoster. E.g. you
could use [Netlify](https://www.netlify.com/) or [Surge](https://surge.sh/).

3. Build your backend for production. You could e.g. use [Heroku](https://dashboard.heroku.com/apps)
or deploy your backend as a [Serverless](https://www.serverless.com/) function.
There is documentation how to setup `apollo-server` to run on [Heroku](https://www.apollographql.com/docs/apollo-server/deployment/heroku/)
or on a [lambda function](https://www.apollographql.com/docs/apollo-server/deployment/lambda/).

4. Use a managed [Neo4J](https://neo4j.com/cloud/) database or a remote GraphQL
API for persistency. If you deploy your backend as a lambda function, I suggest
to use [serverless-dotenv-plugin](https://github.com/colynb/serverless-dotenv-plugin)
to manage credentials. Other helpful plugins are [serverless-offline](https://github.com/dherault/serverless-offline)
for local development and [serverless-bundle](https://github.com/AnomalyInnovations/serverless-bundle)
for ES6 and typescript support.

5. Add automatic deployments to your CI/CD pipeline.

6. Show-off to your friends and your family!

**Merry christmas!**

* ,
_/^\_
< >
* /.-.\ *
* `/&\` *
,@.*;@,
/_o.I %_\ *
* (`'--:o(_@;
/`;--.,__ `') *
;@`o % O,*`'`&\
* (`'--)_@ ;o %'()\ *
/`;--._`''--._O'@;
/&*,()~o`;-.,_ `""`)
* /`,@ ;+& () o*`;-';\
(`""--.,_0 +% @' &()\
/-.,_ ``''--....-'`) *
* /@%;o`:;'--,.__ __.'\
;*,&(); @ % &^;~`"`o;@(); *
/(); o^~; & ().o@*&`;&%O\
jgs `"="==""==,,,.,="=="==="`
__.----.(\-''#####---...___...-----._
'` \)_`"""""`
.--' ')
o( )_-\
`"""` `

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why are the exercise descriptions in the README.md 😢 ? Please rebase your changes or merge homework/main.

141 changes: 141 additions & 0 deletions exercises/7/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Exercise #7

| Deadline | Date |
| -------------------------- | ---------------------- |
| Review due date (optional) | 20.01.2021 - 14:00 |
| **Final Due date** | **27.01.2021 - 14:00** |

## Goal

Extend exercise [#1](../1), [#2](../2), [#3](../3), [#4](../4), [#5](../5) and
[#6](../6) with the new [objectives](#objectives).

In this exercise, we will connect our Webapp with the backend. During that, we cover the following topics:
* [Isomorphic JavaScript](https://en.wikipedia.org/wiki/Isomorphic_JavaScript)
* [Client-Side-Rendering(CSR) vs. Server-Side-Rendering(SSR)](https://developers.google.com/web/updates/2019/02/rendering-on-the-web)
* [Vuex](https://vuex.vuejs.org/)
* [Vue-Apollo](https://apollo.vuejs.org/)
* [Progressive-Web-App](https://web.dev/progressive-web-apps/)

## Instructions

0. Discuss and decide your deployment target with your team:
* Static (JAMstack) or
* Server (Node.js)

1. Setup a Nuxt app that replaces your vue-cli `webapp/`.
* Use [create-nuxt-app](https://nuxtjs.org/docs/2.x/get-started/installation#using-create-nuxt-app)
for setup. Make sure to select
* "SSR/SSG" as Rendering mode
* The deployment target you decided on in Instruction #0.
* [@nuxtjs/pwa](https://pwa.nuxtjs.org/) for the progressive web app
* Copy all relevant code (most importantly your components including your specs and stories) from your old `webapp/` folder to your new one.
* Delete the old `webapp/` folder. Make sure the new one is in the same location.
* Commit this refactoring in a *separate* PR and merge it into your `main` branch. This will keep the content of "Files Changed" tab
small and help mentors to review your code.

2. Use [@nuxtjs/storybook](https://storybook.nuxtjs.org/) to
setup storybook.

3. Use [@nuxtjs/apollo](https://github.com/nuxt-community/apollo-module) to
setup [vue-apollo](https://github.com/vuejs/vue-apollo) in your Nuxt app. An
alternative to `nuxtjs/apollo` is
[nuxt-graphql-request](https://github.com/Gomah/nuxt-graphql-request). Both
of these libraries have
[authentication helpers](https://github.com/nuxt-community/apollo-module#authentication)
or [similar features](https://github.com/Gomah/nuxt-graphql-request#authentication-via-http-header)
to make sure that a valid JWT is sent on every authenticated request.

4. Create a `login.vue` page component and a `LoginForm.vue` component. The
login form is responsible to call a `LOGIN` mutation and save the JWT token
returned by the backend.

5. Create a menu component with a `<nuxt-link>` to `/login` if the user is
not logged in. If the user is logged in, it shows a logout button. You might
want to put this this menu component in your `layouts/default.vue`.
Furthermore, you might want to use [Vuex](https://vuex.vuejs.org/) for a
globally accessible `isAuthenticated` getter method. There is a [nuxt integration](https://nuxtjs.org/docs/2.x/directory-structure/store).

* Hint: Due to a bug, `nuxt-apollo` does not properly read the cookie containing `apollo-token` in SSR. See this [PR](https://github.com/nuxt-community/apollo-module/pull/358). If you need `this.$apolloHelpers.getToken` in SSR you could either follow the PR or parse the cookie like this:
```js
// in store/index.js
import cookie from 'cookie'

export const actions = {
nuxtServerInit(store, context) {
const { req } = context.ssrContext
if (!req) return // static site generation
const parsedCookies = cookie.parse(req.headers.cookie)
const token = parsedCookies['apollo-token']
if (!token) return
store.commit('auth/setToken', token)
},
}
```
* Hint2: You might want to decode the id of the current user from the JWT with [jwt-decode](https://github.com/auth0/jwt-decode).

6. Make sure that your `upvote` and `write` mutations hit
your backend. If you use `vue-apollo`, it will update your cache
automatically if you request the `ID` field in your mutations.


7. Your buttons should behave according to the authentication state. E.g. you
could only display `upvote` when the user is logged in. Alternatively,
you could redirect to `/login` if the user is not logged in.
Add a `delete` and `edit` button to your news-entries which only shows for authors.
Connecting them to your backend is optional though.

8. PR Review:
* Review a pull request of another team.
* Find at least 6 things (:star: from [Objectives](#objectives)) the other
team did or didn't do.
* Either "Request Changes" or "Approve" *do not just "Comment"*.
* Suggest changes line-by-line in "Files Changed".
* Link to your code review in the description of your own pull request.
* Request a review from another team

## Objectives

:star: For instructions in the `README.md` on how to build your webapp for production.

:star: For no changes in "Files Changed" tab of the refactoring from `vue-cli` to `create-nuxt-app`. (See #1 in instructions)

:star: :star: For the API connection between your front- and backend.

:star: For your previous frontend tests still passing. Requests to the backend are mocked.

:star: :star: For a login feature in your webapp including a Vue component and its software tests.

:star: :star: For a menu component which shows a login or logout button and its software tests.

:star: For an upvote button that behaves according to the authentication state of your user

:star: For a delete and edit button that is only visible to the author of the post.

:star: For [Lighthouse](https://developers.google.com/web/tools/lighthouse) reporting that your production website is installable as PWA (except HTTPS).

:star: For requesting a review and reviewing another team's PR.

All objectives must be implemented according to the [instructions](#instructions).

## Optional Objectives

:rocket: Create a storybook story for `LoginForm.vue`.

:rocket: Create storybook stories to show the appearance of the post component to the author and to another users.

:rocket: Use different [layouts](https://nuxtjs.org/docs/2.x/directory-structure/layouts). E.g. add a logout button in `layouts/default.vue`. Use a different layout for `pages/login.vue`.

:rocket: Add a [middleware](https://nuxtjs.org/docs/2.x/directory-structure/middleware) to redirect from `/login` to `/` when already logged in.

:rocket: Add [dynamic page components](https://nuxtjs.org/examples/routing-dynamic-pages/). E.g. every post gets a separate page component, e.g. `/post/_id.vue` or `/post/_slug.vue`.

:rocket: Navigating to a non-existing post route responds with a 404 HTTP status code.

:rocket: Your menu component shows the name of the current user when logged in. You could e.g. call a another grapqhl query to get the name of the user after a successful login. Alternatively, you could encode the name of the user in the JWT.

:rocket: The form to submit a new post has another text input for the URL of a link.

:rocket: The URL of the post appears on the post component or post page as an external link.

:rocket: On every post page you can see the list of voters. It's up to you if the result of the vote (up or down) is made public.
4 changes: 2 additions & 2 deletions webapp/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ module.exports = {
sourceType: 'module',
},
extends: [
'@nuxtjs',
'eslint:recommended',
'plugin:vue/essential',
'plugin:prettier/recommended',
'plugin:nuxt/recommended',
],
plugins: ['vue', 'jest'],
plugins: ['vue'],
rules: {
'prettier/prettier': [
'error',
Expand All @@ -38,6 +37,7 @@ module.exports = {
math: 'always',
},
],
'vue/this-in-template': 'off',
'no-console': 'off',
'vue/singleline-html-element-content-newline': 'off',
},
Expand Down
5 changes: 2 additions & 3 deletions webapp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
$ yarn install

# serve with hot reload at localhost:3000
$ yarn dev
$ yarn dev (all PWA functions are not supported)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤔 ?


# build for production and launch server
$ yarn generate
$ yarn build
Comment on lines +13 to 14

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Depending on your deployment target you have either yarn run build or yarn run generate.

$ yarn start
Comment on lines 12 to 15

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⭐ for extending the Readme


# generate static project
$ yarn generate
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ For instructions in the README.md on how to build your webapp for production.

The build is broken, when I follow the instructions.


For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org).
Binary file added webapp/assets/img/404-Page.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/assets/img/loader.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 0 additions & 27 deletions webapp/components/CreatNews/CreateNews.spec.js

This file was deleted.

Loading