Merge branch 'master' of github.com:Human-Connection/Human-Connection into dependabot/npm_and_yarn/webapp/prettier-1.17.1-fix_incompatibility

This commit is contained in:
Matt Rider 2019-05-29 10:33:43 -03:00
commit f732ff44d0
30 changed files with 281 additions and 111 deletions

View File

@ -22,7 +22,7 @@ install:
script:
# Backend
- docker-compose exec backend yarn run lint
- docker-compose exec backend yarn run test:jest --ci --verbose=false
- docker-compose exec backend yarn run test:jest --ci --verbose=false --coverage
- docker-compose exec backend yarn run db:reset
- docker-compose exec backend yarn run db:seed
- docker-compose exec backend yarn run test:cucumber
@ -30,7 +30,7 @@ script:
- docker-compose exec backend yarn run db:seed
# Frontend
- docker-compose exec webapp yarn run lint
- docker-compose exec webapp yarn run test --ci --verbose=false
- docker-compose exec webapp yarn run test --ci --verbose=false --coverage
- docker-compose exec -d backend yarn run test:before:seeder
# Fullstack
- CYPRESS_RETRIES=1 yarn run cypress:run

View File

@ -26,7 +26,6 @@
"license": "MIT",
"jest": {
"verbose": true,
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.js",
"!**/node_modules/**",
@ -106,7 +105,7 @@
"graphql-request": "~1.8.2",
"jest": "~24.8.0",
"nodemon": "~1.19.1",
"prettier": "~1.14.3",
"prettier": "~1.17.1",
"supertest": "~4.0.2"
}
}

View File

@ -1,9 +1,9 @@
import { rule, shield, allow, or } from 'graphql-shield'
/*
* TODO: implement
* See: https://github.com/Human-Connection/Nitro-Backend/pull/40#pullrequestreview-180898363
*/
* TODO: implement
* See: https://github.com/Human-Connection/Nitro-Backend/pull/40#pullrequestreview-180898363
*/
const isAuthenticated = rule()(async (parent, args, ctx, info) => {
return ctx.user !== null
})

View File

@ -6233,10 +6233,10 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
prettier@~1.14.3:
version "1.14.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895"
integrity sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==
prettier@~1.17.1:
version "1.17.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db"
integrity sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==
pretty-format@^24.8.0:
version "24.8.0"

View File

@ -8,5 +8,5 @@ do
fi
done
/migration/mongo/import.sh
/migration/mongo/export.sh
/migration/neo4j/import.sh

View File

@ -0,0 +1,16 @@
# SSH Access
# SSH_USERNAME='username'
# SSH_HOST='example.org'
# Mongo DB on Remote Maschine
# MONGODB_USERNAME='mongouser'
# MONGODB_PASSWORD='mongopassword'
# MONGODB_DATABASE='mongodatabase'
# MONGODB_AUTH_DB='admin'
# Export Settings
# On Windows this resolves to C:\Users\dornhoeschen\AppData\Local\Temp\mongo-export (MinGW)
EXPORT_PATH='/tmp/mongo-export/'
EXPORT_MONGOEXPORT_BIN='mongoexport'
# On Windows use something like this
# EXPORT_MONGOEXPORT_BIN='C:\Program Files\MongoDB\Server\3.6\bin\mongoexport.exe'

View File

@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -e
# import .env config
set -o allexport
source $(dirname "$0")/.env
set +o allexport
# Export collection function defintion
function export_collection () {
"${EXPORT_MONGOEXPORT_BIN}" --db ${MONGODB_DATABASE} --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --collection $1 --collection $1 --out "${EXPORT_PATH}$1.json"
mkdir -p ${EXPORT_PATH}splits/$1/
split -l 1000 -a 3 ${EXPORT_PATH}$1.json ${EXPORT_PATH}splits/$1/
}
# Delete old export & ensure directory
rm -rf ${EXPORT_PATH}*
mkdir -p ${EXPORT_PATH}
# Open SSH Tunnel
ssh -4 -M -S my-ctrl-socket -fnNT -L 27018:localhost:27017 -l ${SSH_USERNAME} ${SSH_HOST}
# Export all Data from the Alpha to json and split them up
export_collection "badges"
export_collection "categories"
export_collection "comments"
export_collection "contributions"
export_collection "emotions"
export_collection "follows"
export_collection "invites"
export_collection "notifications"
export_collection "organizations"
export_collection "pages"
export_collection "projects"
export_collection "settings"
export_collection "shouts"
export_collection "status"
export_collection "systemnotifications"
export_collection "users"
export_collection "userscandos"
export_collection "usersettings"
# Close SSH Tunnel
ssh -S my-ctrl-socket -O check -l ${SSH_USERNAME} ${SSH_HOST}
ssh -S my-ctrl-socket -O exit -l ${SSH_USERNAME} ${SSH_HOST}

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -e
echo "SSH_USERNAME ${SSH_USERNAME}"
echo "SSH_HOST ${SSH_HOST}"
echo "MONGODB_USERNAME ${MONGODB_USERNAME}"
echo "MONGODB_PASSWORD ${MONGODB_PASSWORD}"
echo "MONGODB_DATABASE ${MONGODB_DATABASE}"
echo "MONGODB_AUTH_DB ${MONGODB_AUTH_DB}"
echo "-------------------------------------------------"
rm -rf /tmp/mongo-export/*
mkdir -p /tmp/mongo-export/
ssh -4 -M -S my-ctrl-socket -fnNT -L 27018:localhost:27017 -l ${SSH_USERNAME} ${SSH_HOST}
for collection in "categories" "badges" "users" "contributions" "comments" "follows" "shouts"
do
mongoexport --db ${MONGODB_DATABASE} --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --collection $collection --collection $collection --out "/tmp/mongo-export/$collection.json"
mkdir -p /tmp/mongo-export/splits/$collection/
split -l 1000 -a 3 /tmp/mongo-export/$collection.json /tmp/mongo-export/splits/$collection/
done
ssh -S my-ctrl-socket -O check -l ${SSH_USERNAME} ${SSH_HOST}
ssh -S my-ctrl-socket -O exit -l ${SSH_USERNAME} ${SSH_HOST}

View File

@ -0,0 +1,16 @@
# Neo4J Settings
# NEO4J_USERNAME='neo4j'
# NEO4J_PASSWORD='letmein'
# Import Settings
# On Windows this resolves to C:\Users\dornhoeschen\AppData\Local\Temp\mongo-export (MinGW)
IMPORT_PATH='/tmp/mongo-export/'
IMPORT_CHUNK_PATH='/tmp/mongo-export/splits/current-chunk.json'
IMPORT_CHUNK_PATH_CQL='/tmp/mongo-export/splits/current-chunk.json'
# On Windows this path needs to be windows style since the cypher-shell runs native - note the forward slash
# IMPORT_CHUNK_PATH_CQL='C:/Users/dornhoeschen/AppData/Local/Temp/mongo-export/splits/current-chunk.json'
IMPORT_CYPHERSHELL_BIN='cypher-shell'
# On Windows use something like this
# IMPORT_CYPHERSHELL_BIN='C:\Program Files\neo4j-community\bin\cypher-shell.bat'

View File

@ -1,4 +1,4 @@
CALL apoc.load.json('file:/tmp/mongo-export/splits/current-chunk.json') YIELD value as badge
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as badge
MERGE(b:Badge {id: badge._id["$oid"]})
ON CREATE SET
b.key = badge.key,

View File

@ -1,4 +1,4 @@
CALL apoc.load.json('file:/tmp/mongo-export/splits/current-chunk.json') YIELD value as category
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as category
MERGE(c:Category {id: category._id["$oid"]})
ON CREATE SET
c.name = category.title,

View File

@ -1,4 +1,4 @@
CALL apoc.load.json('file:/tmp/mongo-export/splits/current-chunk.json') YIELD value as json
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as json
MERGE (comment:Comment {id: json._id["$oid"]})
ON CREATE SET

View File

@ -1,4 +1,4 @@
CALL apoc.load.json('file:/tmp/mongo-export/splits/current-chunk.json') YIELD value as post
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as post
MERGE (p:Post {id: post._id["$oid"]})
ON CREATE SET
p.title = post.title,
@ -20,6 +20,6 @@ MATCH (c:Category {id: categoryId})
MERGE (p)-[:CATEGORIZED]->(c)
WITH p, post.tags AS tags
UNWIND tags AS tag
MERGE (t:Tag {id: apoc.create.uuid(), name: tag})
MERGE (t:Tag {id: tag, name: tag})
MERGE (p)-[:TAGGED]->(t)
;

View File

@ -1,4 +1,4 @@
CALL apoc.load.json('file:/tmp/mongo-export/splits/current-chunk.json') YIELD value as follow
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as follow
MATCH (u1:User {id: follow.userId}), (u2:User {id: follow.foreignId})
MERGE (u1)-[:FOLLOWS]->(u2)
;

View File

@ -1,17 +1,48 @@
#!/usr/bin/env bash
set -e
SECONDS=0
SCRIPT_DIRECTORY="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# import .env config
set -o allexport
source $(dirname "$0")/.env
set +o allexport
echo "MATCH (n) DETACH DELETE n;" | cypher-shell
for collection in "badges" "categories" "users" "follows" "contributions" "shouts" "comments"
do
for chunk in /tmp/mongo-export/splits/$collection/*
# Import collection function defintion
function import_collection () {
for chunk in ${IMPORT_PATH}splits/$1/*
do
mv $chunk /tmp/mongo-export/splits/current-chunk.json
echo "Import ${chunk}" && cypher-shell < $SCRIPT_DIRECTORY/$collection.cql
mv $chunk ${IMPORT_CHUNK_PATH}
NEO4J_COMMAND="$(envsubst '${IMPORT_CHUNK_PATH_CQL}' < $(dirname "$0")/$1.cql)"
echo "Import ${chunk}"
echo "${NEO4J_COMMAND}" | "${IMPORT_CYPHERSHELL_BIN}" -u ${NEO4J_USERNAME} -p ${NEO4J_PASSWORD}
done
done
}
# Time variable
SECONDS=0
# Delete all Neo4J Database content
echo "Deleting Database Contents"
echo "MATCH (n) DETACH DELETE n;" | "${IMPORT_CYPHERSHELL_BIN}" -u ${NEO4J_USERNAME} -p ${NEO4J_PASSWORD}
# Import Data
echo "Start Importing Data"
import_collection "badges"
import_collection "categories"
import_collection "users"
import_collection "follows"
import_collection "contributions"
import_collection "shouts"
import_collection "comments"
#import_collection "emotions"
#import_collection "invites"
#import_collection "notifications"
#import_collection "organizations"
#import_collection "pages"
#import_collection "projects"
#import_collection "settings"
#import_collection "status"
#import_collection "systemnotifications"
#import_collection "userscandos"
#import_collection "usersettings"
echo "Time elapsed: $SECONDS seconds"

View File

@ -1,4 +1,4 @@
CALL apoc.load.json('file:/tmp/mongo-export/splits/current-chunk.json') YIELD value as shout
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as shout
MATCH (u:User {id: shout.userId}), (p:Post {id: shout.foreignId})
MERGE (u)-[:SHOUTED]->(p)
;

View File

@ -1,4 +1,4 @@
CALL apoc.load.json('file:/tmp/mongo-export/splits/current-chunk.json') YIELD value as user
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as user
MERGE(u:User {id: user._id["$oid"]})
ON CREATE SET
u.name = user.name,

View File

@ -0,0 +1,69 @@
import { mount, createLocalVue } from '@vue/test-utils'
import Styleguide from '@human-connection/styleguide'
import Avatar from './Avatar.vue'
const localVue = createLocalVue()
localVue.use(Styleguide)
describe('Avatar.vue', () => {
let propsData = {}
const Wrapper = () => {
return mount(Avatar, { propsData, localVue })
}
it('renders no image', () => {
expect(
Wrapper()
.find('img')
.exists(),
).toBe(false)
})
it('renders an icon', () => {
expect(
Wrapper()
.find('.ds-icon')
.exists(),
).toBe(true)
})
describe('given a user', () => {
describe('with a relative avatar url', () => {
beforeEach(() => {
propsData = {
user: {
avatar: '/avatar.jpg',
},
}
})
it('adds a prefix to load the image from the uploads service', () => {
expect(
Wrapper()
.find('img')
.attributes('src'),
).toBe('/api/avatar.jpg')
})
})
describe('with an absolute avatar url', () => {
beforeEach(() => {
propsData = {
user: {
avatar: 'http://lorempixel.com/640/480/animals',
},
}
})
it('keeps the avatar URL as is', () => {
// e.g. our seeds have absolute image URLs
expect(
Wrapper()
.find('img')
.attributes('src'),
).toBe('http://lorempixel.com/640/480/animals')
})
})
})
})

View File

@ -0,0 +1,28 @@
<template>
<ds-avatar :image="avatarUrl" :name="userName" class="avatar" :size="size" />
</template>
<script>
export default {
name: 'HcAvatar',
props: {
user: { type: Object, default: null },
size: { type: String, default: 'small' },
},
computed: {
avatarUrl() {
const { avatar: imageSrc } = this.user || {}
if (!imageSrc) return imageSrc
return imageSrc.startsWith('/') ? imageSrc.replace('/', '/api/') : imageSrc
},
userName() {
const { name } = this.user || {}
// The name is used to display the initials in case
// the image cannot be loaded.
return name
// If the name is undefined, then our styleguide will
// display an icon for the anonymous user.
},
},
}
</script>

View File

@ -317,10 +317,18 @@ export default {
},
},
},
mounted() {
this.$root.$on('changeLanguage', () => {
this.changePlaceHolderText()
})
},
beforeDestroy() {
this.editor.destroy()
},
methods: {
changePlaceHolderText() {
this.editor.extensions.options.placeholder.emptyNodeText = this.$t('editor.placeholder')
},
// navigate to the previous item
// if it's the first item, navigate to the last one
upHandler() {

View File

@ -68,6 +68,7 @@ export default {
changeLanguage(locale, toggleMenu) {
this.$i18n.set(locale)
toggleMenu()
this.$root.$emit('changeLanguage')
},
matcher(locale) {
return locale === this.$i18n.locale()

View File

@ -48,13 +48,6 @@ export default {
<div class="dz-image">
<div data-dz-thumbnail-bg></div>
</div>
<div class="dz-details">
<div class="dz-size"><span data-dz-size></span></div>
<div class="dz-filename"><span data-dz-name></span></div>
</div>
<div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
<div class="dz-error-message"><span data-dz-errormessage></span></div>
</div>
</div>
`
},
@ -117,15 +110,16 @@ export default {
}
#customdropzone .dz-preview {
transition: all 0.2s ease-out;
width: 160px;
display: flex;
}
#customdropzone .dz-preview .dz-image {
position: relative;
width: 122px;
height: 122px;
margin: -35px;
width: 100%;
height: 100%;
object-fit: contain;
overflow: hidden;
}
#customdropzone .dz-preview .dz-image > div {

View File

@ -3,7 +3,7 @@
<div
style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;"
>
<ds-avatar style="display: inline-block; vertical-align: middle;" size="small" />
<hc-avatar />
</div>
<div style="display: inline-block; height: 100%; vertical-align: middle;">
<b class="username" style="vertical-align: middle;">{{ $t('profile.userAnonym') }}</b>
@ -16,12 +16,7 @@
<div
style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;"
>
<ds-avatar
:image="user.avatar"
:name="userName"
style="display: inline-block; vertical-align: middle;"
size="small"
/>
<hc-avatar :user="user" />
</div>
<div style="display: inline-block; height: 100%; vertical-align: middle;">
<b class="username" style="vertical-align: middle;">{{ userName | truncate(18) }}</b>
@ -102,6 +97,7 @@ import { mapGetters } from 'vuex'
import HcRelativeDateTime from '~/components/RelativeDateTime'
import HcFollowButton from '~/components/FollowButton'
import HcBadges from '~/components/Badges'
import HcAvatar from '~/components/Avatar/Avatar.vue'
import Dropdown from '~/components/Dropdown'
export default {
@ -109,6 +105,7 @@ export default {
components: {
HcRelativeDateTime,
HcFollowButton,
HcAvatar,
HcBadges,
Dropdown,
},
@ -142,12 +139,6 @@ export default {
</script>
<style lang="scss">
.profile-avatar {
display: block;
margin: auto;
margin-top: -45px;
border: #fff 5px solid;
}
.user {
white-space: nowrap;
position: relative;

View File

@ -2,14 +2,7 @@
<ds-form v-model="form" @submit="handleSubmit">
<template slot-scope="{ errors }">
<ds-card>
<no-ssr>
<hc-editor
ref="editor"
:users="users"
:value="form.content"
@input="updateEditorContent"
/>
</no-ssr>
<hc-editor ref="editor" :users="users" :value="form.content" @input="updateEditorContent" />
<ds-space />
<ds-flex :gutter="{ base: 'small', md: 'small', sm: 'x-large', xs: 'x-large' }">
<ds-flex-item :width="{ base: '0%', md: '50%', sm: '0%', xs: '0%' }" />

View File

@ -1,4 +1,4 @@
import { config, mount, createLocalVue, createWrapper } from '@vue/test-utils'
import { mount, createLocalVue, createWrapper } from '@vue/test-utils'
import CommentForm from './index.vue'
import Styleguide from '@human-connection/styleguide'
@ -6,8 +6,6 @@ const localVue = createLocalVue()
localVue.use(Styleguide)
config.stubs['no-ssr'] = '<span><slot /></span>'
describe('CommentForm.vue', () => {
let mocks
let wrapper

View File

@ -33,6 +33,13 @@ describe('CommentList.vue', () => {
})
mocks = {
$t: jest.fn(),
$apollo: {
queries: {
Post: {
refetch: jest.fn(),
},
},
},
}
data = () => {
return {
@ -63,5 +70,10 @@ describe('CommentList.vue', () => {
it('displays comments when there are comments to display', () => {
expect(wrapper.find('div#comments').text()).toEqual('this is a comment')
})
it("refetches a post's comments from the backend", () => {
wrapper.vm.refetchPostComments()
expect(mocks.$apollo.queries.Post.refetch).toHaveBeenCalledTimes(1)
})
})
})

View File

@ -45,13 +45,15 @@ export default {
},
},
mounted() {
this.$root.$on('refetchPostComments', comment => {
this.refetchPostComments(comment)
this.$root.$on('refetchPostComments', () => {
this.refetchPostComments()
})
},
methods: {
refetchPostComments(comment) {
this.$apollo.queries.Post.refetch()
refetchPostComments() {
if (this.$apollo.queries.Post) {
this.$apollo.queries.Post.refetch()
}
},
},
apollo: {

View File

@ -39,7 +39,7 @@
"
@click.prevent="toggleMenu"
>
<ds-avatar :image="user.avatar" :name="user.name" size="small" />
<hc-avatar :user="user" />
<ds-icon size="xx-small" name="angle-down" />
</a>
</template>
@ -97,6 +97,7 @@ import SearchInput from '~/components/SearchInput.vue'
import Modal from '~/components/Modal'
import NotificationMenu from '~/components/notifications/NotificationMenu'
import Dropdown from '~/components/Dropdown'
import HcAvatar from '~/components/Avatar/Avatar.vue'
import seo from '~/mixins/seo'
export default {
@ -106,6 +107,7 @@ export default {
SearchInput,
Modal,
NotificationMenu,
HcAvatar,
},
mixins: [seo],
data() {

View File

@ -22,7 +22,6 @@
},
"jest": {
"verbose": true,
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.{js,vue}",
"!**/node_modules/**",

View File

@ -11,13 +11,7 @@
style="position: relative; height: auto;"
>
<hc-upload v-if="myProfile" :user="user" />
<ds-avatar
v-else
:image="user.avatar"
:name="userName"
class="profile-avatar"
size="x-large"
/>
<hc-avatar v-else :user="user" class="profile-avatar" size="x-large" />
<no-ssr>
<content-menu
placement="bottom-end"
@ -28,9 +22,7 @@
/>
</no-ssr>
<ds-space margin="small">
<ds-heading tag="h3" align="center" no-margin>
{{ userName }}
</ds-heading>
<ds-heading tag="h3" align="center" no-margin>{{ userName }}</ds-heading>
<ds-text v-if="user.location" align="center" color="soft" size="small">
<ds-icon name="map-marker" />
{{ user.location.name }}
@ -70,9 +62,7 @@
<template v-if="user.about">
<hr />
<ds-space margin-top="small" margin-bottom="small">
<ds-text color="soft" size="small">
{{ user.about }}
</ds-text>
<ds-text color="soft" size="small">{{ user.about }}</ds-text>
</ds-space>
</template>
</ds-card>
@ -225,6 +215,7 @@ import HcLoadMore from '~/components/LoadMore.vue'
import HcEmpty from '~/components/Empty.vue'
import ContentMenu from '~/components/ContentMenu'
import HcUpload from '~/components/Upload'
import HcAvatar from '~/components/Avatar/Avatar.vue'
export default {
components: {
@ -235,6 +226,7 @@ export default {
HcBadges,
HcLoadMore,
HcEmpty,
HcAvatar,
ContentMenu,
HcUpload,
},
@ -348,7 +340,7 @@ export default {
</script>
<style lang="scss">
.profile-avatar {
.profile-avatar.ds-avatar {
display: block;
margin: auto;
margin-top: -60px;