Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/webapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ name: Build, Test, Lint

on:
push:
branches: [ main, exercise3 ]
branches: [ main, exercise7 ]
pull_request:
branches: [ main, exercise3 ]
branches: [ main, exercise7 ]
Copy link
Contributor

Choose a reason for hiding this comment

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

What about testing all branches?


defaults:
run:
Expand All @@ -31,4 +31,4 @@ jobs:

- run: npm ci
- run: npm run lint
- run: npm run test:unit
- run: npm run test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ git-crypt-key
*.DS_Store
*/node_modules
*/dist
*/.nuxt-storybook
*/storybook-static


# local env files
Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,33 @@ $ (webapp) npm install
### Run unit tests

```
$ (webapp) npm run test:unit
$ (webapp) npm run test
```

### Compiles and hot-reloads for development

```
$ (webapp) npm run serve
$ (webapp) npm run dev
```

### Compiles and minifies for production
### Compiles and minifies for production (server build)

```
$ (webapp) npm run build
```

### Compiles and minifies for production (static build)

```
$ (webapp) npm run generate
Copy link
Contributor

Choose a reason for hiding this comment

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

Running npm run generate fails with the following erros:


 ERROR   /index.stories                                                                                                                                                                                   15:26:50

Error: render function or template not defined in component: anonymous
    at Qi (/home/robert/Development/Systems-Development-and-Frameworks/homework/webapp/node_modules/vue-server-renderer/build.prod.js:1:66721)
    at io (/home/robert/Development/Systems-Development-and-Frameworks/homework/webapp/node_modules/vue-server-renderer/build.prod.js:1:70594)
    at ro (/home/robert/Development/Systems-Development-and-Frameworks/homework/webapp/node_modules/vue-server-renderer/build.prod.js:1:70244)
    at eo (/home/robert/Development/Systems-Development-and-Frameworks/homework/webapp/node_modules/vue-server-renderer/build.prod.js:1:67491)
    at /home/robert/Development/Systems-Development-and-Frameworks/homework/webapp/node_modules/vue-server-renderer/build.prod.js:1:70711
    at runNextTicks (internal/process/task_queues.js:58:5)
    at listOnTimeout (internal/timers.js:523:9)
    at processTimers (internal/timers.js:497:7)


 ERROR   /                                                                                                                                                                                                15:26:50

TypeError: Cannot read property 'length' of undefined
    at a.render (pages/index.vue?1e15:1:0)
    at a.t._render (/home/robert/Development/Systems-Development-and-Frameworks/homework/webapp/node_modules/vue/dist/vue.runtime.common.prod.js:6:35273)
    at /home/robert/Development/Systems-Development-and-Frameworks/homework/webapp/node_modules/vue-server-renderer/build.prod.js:1:70637

According to your .nuxt.config.js your deployment target is server. Please only include relevant deployment instructions in your README.md.

```

### Serve in production

```
$ (webapp) npm run start
```

Comment on lines 32 to +48
Copy link

Choose a reason for hiding this comment

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

This looks good! Another ⭐

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

### Run linter

Copy link
Contributor

Choose a reason for hiding this comment

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

There is a small typo further down this file 👇

$ docker run -p 7474:7474 -p 7687:7687 --env=NEO4J_AUTH=$NEO4J_USERNAME/$NEO4J_PASSWORD neo4j:4.2.1

Missing $

```
Expand Down
18 changes: 18 additions & 0 deletions backend/src/NeodeDS.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,24 @@ export class NeodeDS extends DataSource {
.map(neo4jInt => neo4jInt.toNumber())
}

async getVoteForPost (postId, userId) {
return await Promise
.all([
this.getPost(postId),
this.getUser(userId)
])
.then(async ([{ id: postId }, { id: userId }]) => {
const { records: [record] } = await this.neode.cypher(`
MATCH (u:User{ id: $userId }), (p:Post{ id: $postId })
RETURN EXISTS((u)-[:DOWNVOTED]->(p)) AS has_downvote, EXISTS((u)-[:UPVOTED]->(p)) AS has_upvote
`, {
postId,
userId
})
return record.get('has_downvote') ? -1 : (record.get('has_upvote') ? +1 : 0)
})
}

async getPost (id) {
const node = await this.neode.first('Post', { id })
if (!node) throw new PostIdNotFoundError(id)
Expand Down
1 change: 1 addition & 0 deletions backend/src/api.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Mutation {

extend type Post {
votes: Int!
userVote: Int
}

input PostInput {
Expand Down
9 changes: 9 additions & 0 deletions backend/src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ export default ({ subschema }) => {
const [upvotes, downvotes] = await dataSources.db.getVotesForPost(obj.id)
return upvotes - downvotes
}
},
userVote: {
selectionSet: '{ id }',
resolve: async (obj, _, {
dataSources,
userId
}) => {
return userId !== null ? await dataSources.db.getVoteForPost(obj.id, userId) : null
}
}
},
User: {
Expand Down
90 changes: 90 additions & 0 deletions webapp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# Nuxt generate
dist

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless

# IDE / Editor
.idea

# Service worker
sw.*

# macOS
.DS_Store

# Vim swap files
*.swp
11 changes: 11 additions & 0 deletions webapp/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { nuxifyStorybook } = require('../.nuxt-storybook/storybook/main.js')

module.exports = nuxifyStorybook({
stories: [
'../pages/*.stories.js'
],
addons: [
'@storybook/addon-a11y',
'@storybook/addon-links',
]
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, you could have excluded this refactoring from the PR diff.

4 changes: 4 additions & 0 deletions webapp/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from '~~/.nuxt-storybook/storybook/preview.js'
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
}
14 changes: 14 additions & 0 deletions webapp/apollo/mutations/createPost.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# __typename added to not break mock-apollo-client
mutation createPost($title: String!) {
createPost(post: { title: $title }) {
id
title
votes
userVote
__typename
author {
id
__typename
}
}
}
7 changes: 7 additions & 0 deletions webapp/apollo/mutations/deletePost.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# __typename added to not break mock-apollo-client
mutation deletePost($id: ID!) {
deletePost(id: $id) {
id
__typename
}
}
7 changes: 7 additions & 0 deletions webapp/apollo/mutations/downvotePost.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mutation downvotePost($id: ID!) {
downvotePost(id: $id) {
id
votes
userVote
}
}
3 changes: 3 additions & 0 deletions webapp/apollo/mutations/login.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mutation login($email: String!, $password: String!) {
login(email: $email, password: $password)
}
7 changes: 7 additions & 0 deletions webapp/apollo/mutations/upvotePost.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mutation upvotePost($id: ID!) {
upvotePost(id: $id) {
id
votes
userVote
}
}
13 changes: 13 additions & 0 deletions webapp/apollo/queries/posts.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
posts {
id
Copy link
Contributor

Choose a reason for hiding this comment

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

Team @Systems-Development-and-Frameworks/lichtow has used GraphQL fragments to reduce duplication: https://graphql.org/learn/queries/#fragments

title
votes
userVote
author {
id
__typename
}
__typename
}
}
7 changes: 0 additions & 7 deletions webapp/assets/README.md

This file was deleted.

111 changes: 111 additions & 0 deletions webapp/components/LoginForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<template>
Copy link

Choose a reason for hiding this comment

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

Great ⭐ ⭐

For a login feature in your webapp including a Vue component...

<div>
<template v-if="isAuthenticated">
<span>You're logged in!</span>
<nuxt-link to="/">Return to home page</nuxt-link>
or
<a href="#" @click.prevent="$router.go(-1)">return to the previously browsed page</a>
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 redirect authenticated users from /login directly to where they came from, or to / alternatively.

</template>
<form v-else @submit.prevent="onSubmit" id="form-login">
<table>
<tbody>
<tr>
<td>
<label for="input-email"> E-Mail </label>
</td>
<td>
<input id="input-email" type="text" v-model.trim="email" placeholder="Enter your e-mail address.."
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
<input id="input-email" type="text" v-model.trim="email" placeholder="Enter your e-mail address.."
<input id="input-email" type="email" v-model.trim="email" placeholder="Enter your e-mail address.."

required/>
</td>
</tr>
<tr>
<td>
<label for="input-password"> Password </label>
</td>
<td>
<input id="input-password" type="password" v-model="password" placeholder="Enter your password.."
required/>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Login" :disabled="!(email.length && password.length)"/>
</td>
</tr>
<tr v-if="submitting">
<td colspan="2">
Logging in...
</td>
</tr>
<tr v-if="error">
<td colspan="2">
<span id="login-error-message">{{ error }}</span>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</template>

<script>
import login from '../apollo/mutations/login.gql'
import { mapActions, mapGetters } from 'vuex'
import { ApolloError } from '@apollo/client'
export default {
data () {
return {
submitting: false,
error: null,
email: '',
password: '',
}
},
computed: {
...mapGetters({
isAuthenticated: 'auth/isAuthenticated'
})
},
methods: {
...mapActions({
setToken: 'auth/setToken'
}),
async onSubmit () {
this.error = null
this.submitting = true
try {
const res = await this.$apollo.mutate({
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
const res = await this.$apollo.mutate({
const { data: { login } } = await this.$apollo.mutate({

mutation: login,
variables: {
email: this.email,
password: this.password
}
}).then(({ data }) => data && data.login)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't mix then/catch syntax with async/await for the sake of readability.

await this.setToken(res)
await this.$apolloHelpers.onLogin(res)
if (this.$route.params.returnPath) {
await this.$router.push({
path: this.$route.params.returnPath
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 prefer this.$emit('success') and leave the responsibility of handling the successful login to the parent (page) component.

})
}
} catch (e) {
if (e instanceof ApolloError) {
this.error = e.graphQLErrors.join(",\n")
} else {
this.error = e
}
} finally {
this.submitting = false
}
}
}
}
</script>

<style>
form#form-login > table {
margin: 0 auto;
}
</style>

Loading