diff --git a/.travis.yml b/.travis.yml
index 42b427a11..f48b0bb36 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,12 +21,15 @@ install:
- wait-on http://localhost:7474
script:
+ - export CYPRESS_RETRIES=1
+ - export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
+ - echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, PR=$PR, BRANCH=$BRANCH"
# Backend
- docker-compose exec backend yarn run lint
- 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
+ - docker-compose exec backend yarn run test:cucumber --tags "not @wip"
- docker-compose exec backend yarn run db:reset
- docker-compose exec backend yarn run db:seed
# Frontend
@@ -34,7 +37,7 @@ script:
- 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
+ - yarn run cypress:run
# Coverage
- codecov
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 908252f41..e2a727871 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -9,4 +9,4 @@
],
"editor.formatOnSave": true,
"eslint.autoFixOnSave": true
-}
\ No newline at end of file
+}
diff --git a/backend/Dockerfile b/backend/Dockerfile
index d24f2747e..f0251bddc 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -24,4 +24,5 @@ RUN yarn run build
FROM base as production
ENV NODE_ENV=production
COPY --from=builder /nitro-backend/dist ./dist
+COPY ./public/img/ ./public/img/
RUN yarn install --frozen-lockfile --non-interactive
diff --git a/backend/package.json b/backend/package.json
index be5fb086d..c8537ae0b 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -43,16 +43,16 @@
},
"dependencies": {
"activitystrea.ms": "~2.1.3",
- "apollo-cache-inmemory": "~1.6.1",
- "apollo-client": "~2.6.1",
+ "apollo-cache-inmemory": "~1.6.2",
+ "apollo-client": "~2.6.2",
"apollo-link-context": "~1.0.14",
"apollo-link-http": "~1.5.14",
- "apollo-server": "~2.6.1",
+ "apollo-server": "~2.6.3",
"bcryptjs": "~2.4.3",
"cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5",
"cross-env": "~5.2.0",
- "date-fns": "2.0.0-alpha.29",
+ "date-fns": "2.0.0-alpha.33",
"debug": "~4.1.1",
"dotenv": "~8.0.0",
"express": "~4.17.1",
@@ -61,7 +61,7 @@
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
"graphql-middleware": "~3.0.2",
- "graphql-shield": "~5.3.6",
+ "graphql-shield": "~5.3.8",
"graphql-tag": "~2.10.1",
"graphql-yoga": "~1.17.4",
"helmet": "~3.18.0",
@@ -69,7 +69,6 @@
"linkifyjs": "~2.1.8",
"lodash": "~4.17.11",
"merge-graphql-schemas": "^1.5.8",
- "ms": "~2.1.1",
"neo4j-driver": "~1.7.4",
"neo4j-graphql-js": "git+https://github.com/Human-Connection/neo4j-graphql-js.git#temporary_fixes",
"node-fetch": "~2.6.0",
@@ -88,7 +87,7 @@
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
"@babel/preset-env": "~7.4.5",
"@babel/register": "~7.4.4",
- "apollo-server-testing": "~2.6.1",
+ "apollo-server-testing": "~2.6.3",
"babel-core": "~7.0.0-0",
"babel-eslint": "~10.0.1",
"babel-jest": "~24.8.0",
@@ -106,7 +105,7 @@
"graphql-request": "~1.8.2",
"jest": "~24.8.0",
"nodemon": "~1.19.1",
- "prettier": "~1.17.1",
+ "prettier": "~1.18.2",
"supertest": "~4.0.2"
}
}
diff --git a/backend/src/activitypub/NitroDataSource.js b/backend/src/activitypub/NitroDataSource.js
index eea37337a..0900bed6c 100644
--- a/backend/src/activitypub/NitroDataSource.js
+++ b/backend/src/activitypub/NitroDataSource.js
@@ -505,9 +505,7 @@ export default class NitroDataSource {
const result2 = await this.client.mutate({
mutation: gql`
mutation {
- AddCommentAuthor(from: {id: "${
- result.data.CreateComment.id
- }"}, to: {id: "${toUserId}"}) {
+ AddCommentAuthor(from: {id: "${result.data.CreateComment.id}"}, to: {id: "${toUserId}"}) {
id
}
}
@@ -519,9 +517,7 @@ export default class NitroDataSource {
result = await this.client.mutate({
mutation: gql`
mutation {
- AddCommentPost(from: { id: "${
- result.data.CreateComment.id
- }", to: { id: "${postId}" }}) {
+ AddCommentPost(from: { id: "${result.data.CreateComment.id}", to: { id: "${postId}" }}) {
id
}
}
diff --git a/backend/src/jwt/encode.js b/backend/src/jwt/encode.js
index 97c6dcd66..1552804cc 100644
--- a/backend/src/jwt/encode.js
+++ b/backend/src/jwt/encode.js
@@ -1,11 +1,10 @@
import jwt from 'jsonwebtoken'
-import ms from 'ms'
import CONFIG from './../config'
// Generate an Access Token for the given User ID
export default function encode(user) {
const token = jwt.sign(user, CONFIG.JWT_SECRET, {
- expiresIn: ms('1d'),
+ expiresIn: 24 * 60 * 60 * 1000, // one day
issuer: CONFIG.GRAPHQL_URI,
audience: CONFIG.CLIENT_URI,
subject: user.id.toString(),
diff --git a/backend/src/middleware/filterBubble/filterBubble.js b/backend/src/middleware/filterBubble/filterBubble.js
deleted file mode 100644
index bfdad5e2c..000000000
--- a/backend/src/middleware/filterBubble/filterBubble.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import replaceParams from './replaceParams'
-
-const replaceFilterBubbleParams = async (resolve, root, args, context, resolveInfo) => {
- args = await replaceParams(args, context)
- return resolve(root, args, context, resolveInfo)
-}
-
-export default {
- Query: {
- Post: replaceFilterBubbleParams,
- },
-}
diff --git a/backend/src/middleware/filterBubble/filterBubble.spec.js b/backend/src/middleware/filterBubble/filterBubble.spec.js
index afe1df1c9..62addeece 100644
--- a/backend/src/middleware/filterBubble/filterBubble.spec.js
+++ b/backend/src/middleware/filterBubble/filterBubble.spec.js
@@ -5,6 +5,7 @@ import Factory from '../../seed/factories'
const factory = Factory()
const currentUserParams = {
+ id: 'u1',
email: 'you@example.org',
name: 'This is you',
password: '1234',
@@ -41,7 +42,7 @@ afterEach(async () => {
await factory.cleanDatabase()
})
-describe('FilterBubble middleware', () => {
+describe('Filter posts by author is followed by sb.', () => {
describe('given an authenticated user', () => {
let authenticatedClient
@@ -52,7 +53,7 @@ describe('FilterBubble middleware', () => {
describe('no filter bubble', () => {
it('returns all posts', async () => {
- const query = '{ Post( filterBubble: {}) { title } }'
+ const query = '{ Post(filter: { }) { title } }'
const expected = {
Post: [
{ title: 'This is some random post' },
@@ -65,7 +66,7 @@ describe('FilterBubble middleware', () => {
describe('filtering for posts of followed users only', () => {
it('returns only posts authored by followed users', async () => {
- const query = '{ Post( filterBubble: { author: following }) { title } }'
+ const query = '{ Post( filter: { author: { followedBy_some: { id: "u1" } } }) { title } }'
const expected = {
Post: [{ title: 'This is the post of a followed user' }],
}
diff --git a/backend/src/middleware/filterBubble/replaceParams.js b/backend/src/middleware/filterBubble/replaceParams.js
deleted file mode 100644
index a10b6c29d..000000000
--- a/backend/src/middleware/filterBubble/replaceParams.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { UserInputError } from 'apollo-server'
-
-export default async function replaceParams(args, context) {
- const { author = 'all' } = args.filterBubble || {}
- const { user } = context
-
- if (author === 'following') {
- if (!user)
- throw new UserInputError(
- "You are unauthenticated - I don't know any users you are following.",
- )
-
- const session = context.driver.session()
- let { records } = await session.run(
- 'MATCH(followed:User)<-[:FOLLOWS]-(u {id: $userId}) RETURN followed.id',
- { userId: context.user.id },
- )
- const followedIds = records.map(record => record.get('followed.id'))
-
- // carefully override `id_in`
- args.filter = args.filter || {}
- args.filter.author = args.filter.author || {}
- args.filter.author.id_in = followedIds
-
- session.close()
- }
-
- delete args.filterBubble
-
- return args
-}
diff --git a/backend/src/middleware/filterBubble/replaceParams.spec.js b/backend/src/middleware/filterBubble/replaceParams.spec.js
deleted file mode 100644
index e14fda416..000000000
--- a/backend/src/middleware/filterBubble/replaceParams.spec.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import replaceParams from './replaceParams.js'
-
-describe('replaceParams', () => {
- let args
- let context
- let run
-
- let action = () => {
- return replaceParams(args, context)
- }
-
- beforeEach(() => {
- args = {}
- run = jest.fn().mockResolvedValue({
- records: [{ get: () => 1 }, { get: () => 2 }, { get: () => 3 }],
- })
- context = {
- driver: {
- session: () => {
- return {
- run,
- close: () => {},
- }
- },
- },
- }
- })
-
- describe('args == ', () => {
- describe('{}', () => {
- it('does not crash', async () => {
- await expect(action()).resolves.toEqual({})
- })
- })
-
- describe('unauthenticated user', () => {
- beforeEach(() => {
- context.user = null
- })
-
- describe('{ filterBubble: { author: following } }', () => {
- it('throws error', async () => {
- args = { filterBubble: { author: 'following' } }
- await expect(action()).rejects.toThrow('You are unauthenticated')
- })
- })
-
- describe('{ filterBubble: { author: all } }', () => {
- it('removes filterBubble param', async () => {
- const expected = {}
- await expect(action()).resolves.toEqual(expected)
- })
-
- it('does not make database calls', async () => {
- await action()
- expect(run).not.toHaveBeenCalled()
- })
- })
- })
-
- describe('authenticated user', () => {
- beforeEach(() => {
- context.user = { id: 'u4711' }
- })
-
- describe('{ filterBubble: { author: following } }', () => {
- beforeEach(() => {
- args = { filterBubble: { author: 'following' } }
- })
-
- it('returns args object with resolved ids of followed users', async () => {
- const expected = { filter: { author: { id_in: [1, 2, 3] } } }
- await expect(action()).resolves.toEqual(expected)
- })
-
- it('makes database calls', async () => {
- await action()
- expect(run).toHaveBeenCalledTimes(1)
- })
-
- describe('given any additional filter args', () => {
- describe('merges', () => {
- it('empty filter object', async () => {
- args.filter = {}
- const expected = { filter: { author: { id_in: [1, 2, 3] } } }
- await expect(action()).resolves.toEqual(expected)
- })
-
- it('filter.title', async () => {
- args.filter = { title: 'bla' }
- const expected = { filter: { title: 'bla', author: { id_in: [1, 2, 3] } } }
- await expect(action()).resolves.toEqual(expected)
- })
-
- it('filter.author', async () => {
- args.filter = { author: { name: 'bla' } }
- const expected = { filter: { author: { name: 'bla', id_in: [1, 2, 3] } } }
- await expect(action()).resolves.toEqual(expected)
- })
- })
- })
- })
-
- describe('{ filterBubble: { } }', () => {
- it('removes filterBubble param', async () => {
- const expected = {}
- await expect(action()).resolves.toEqual(expected)
- })
-
- it('does not make database calls', async () => {
- await action()
- expect(run).not.toHaveBeenCalled()
- })
- })
-
- describe('{ filterBubble: { author: all } }', () => {
- it('removes filterBubble param', async () => {
- const expected = {}
- await expect(action()).resolves.toEqual(expected)
- })
-
- it('does not make database calls', async () => {
- await action()
- expect(run).not.toHaveBeenCalled()
- })
- })
- })
- })
-})
diff --git a/backend/src/middleware/fixImageUrlsMiddleware.js b/backend/src/middleware/fixImageUrlsMiddleware.js
index c930915bf..3bfa8537a 100644
--- a/backend/src/middleware/fixImageUrlsMiddleware.js
+++ b/backend/src/middleware/fixImageUrlsMiddleware.js
@@ -6,8 +6,11 @@ const legacyUrls = [
export const fixUrl = url => {
legacyUrls.forEach(legacyUrl => {
- url = url.replace(legacyUrl, '/api')
+ url = url.replace(legacyUrl, '')
})
+ if (!url.startsWith('/')) {
+ url = `/${url}`
+ }
return url
}
diff --git a/backend/src/middleware/fixImageUrlsMiddleware.spec.js b/backend/src/middleware/fixImageUrlsMiddleware.spec.js
index b2d808dd9..0da66811a 100644
--- a/backend/src/middleware/fixImageUrlsMiddleware.spec.js
+++ b/backend/src/middleware/fixImageUrlsMiddleware.spec.js
@@ -1,12 +1,19 @@
import { fixImageURLs } from './fixImageUrlsMiddleware'
describe('fixImageURLs', () => {
+ describe('edge case: image url is exact match of legacy url', () => {
+ it('replaces it with `/`', () => {
+ const url = 'https://api-alpha.human-connection.org'
+ expect(fixImageURLs(url)).toEqual('/')
+ })
+ })
+
describe('image url of legacy alpha', () => {
it('removes domain', () => {
const url =
'https://api-alpha.human-connection.org/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png'
expect(fixImageURLs(url)).toEqual(
- '/api/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png',
+ '/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png',
)
})
})
@@ -16,7 +23,7 @@ describe('fixImageURLs', () => {
const url =
'https://staging-api.human-connection.org/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg'
expect(fixImageURLs(url)).toEqual(
- '/api/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg',
+ '/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg',
)
})
})
diff --git a/backend/src/middleware/index.js b/backend/src/middleware/index.js
index 6bc7be000..75314abc0 100644
--- a/backend/src/middleware/index.js
+++ b/backend/src/middleware/index.js
@@ -13,7 +13,6 @@ import includedFields from './includedFieldsMiddleware'
import orderBy from './orderByMiddleware'
import validation from './validation'
import notifications from './notifications'
-import filterBubble from './filterBubble/filterBubble'
export default schema => {
const middlewares = {
@@ -31,13 +30,11 @@ export default schema => {
user: user,
includedFields: includedFields,
orderBy: orderBy,
- filterBubble: filterBubble,
}
let order = [
'permissions',
'activityPub',
- 'filterBubble',
'password',
'dateTime',
'validation',
diff --git a/backend/src/middleware/notifications/spec.js b/backend/src/middleware/notifications/spec.js
index 65212e544..985654b0f 100644
--- a/backend/src/middleware/notifications/spec.js
+++ b/backend/src/middleware/notifications/spec.js
@@ -87,9 +87,7 @@ describe('currentUser { notifications }', () => {
describe('who mentions me again', () => {
beforeEach(async () => {
- const updatedContent = `${
- post.content
- } One more mention to @al-capone`
+ const updatedContent = `${post.content} One more mention to @al-capone`
// The response `post.content` contains a link but the XSSmiddleware
// should have the `mention` CSS class removed. I discovered this
// during development and thought: A feature not a bug! This way we
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index bc9b4c525..10b777748 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -1,4 +1,4 @@
-import { rule, shield, allow, or } from 'graphql-shield'
+import { rule, shield, deny, allow, or } from 'graphql-shield'
/*
* TODO: implement
@@ -16,6 +16,12 @@ const isAdmin = rule()(async (parent, args, { user }, info) => {
return user && user.role === 'admin'
})
+const onlyYourself = rule({
+ cache: 'no_cache',
+})(async (parent, args, context, info) => {
+ return context.user.id === args.id
+})
+
const isMyOwn = rule({
cache: 'no_cache',
})(async (parent, args, context, info) => {
@@ -48,6 +54,13 @@ const belongsToMe = rule({
return Boolean(notification)
})
+/* TODO: decide if we want to remove this check: the check
+ * `onlyEnabledContent` throws authorization errors only if you have
+ * arguments for `disabled` or `deleted` assuming these are filter
+ * parameters. Soft-delete middleware obfuscates data on its way out
+ * anyways. Furthermore, `neo4j-graphql-js` offers many ways to filter for
+ * data so I believe, this is not a good check anyways.
+ */
const onlyEnabledContent = rule({
cache: 'strict',
})(async (parent, args, ctx, info) => {
@@ -80,47 +93,70 @@ const isAuthor = rule({
return authorId === user.id
})
-// Permissions
-const permissions = shield({
- Query: {
- Notification: isAdmin,
- statistics: allow,
- currentUser: allow,
- Post: or(onlyEnabledContent, isModerator),
- },
- Mutation: {
- UpdateNotification: belongsToMe,
- CreatePost: isAuthenticated,
- UpdatePost: isAuthor,
- DeletePost: isAuthor,
- report: isAuthenticated,
- CreateBadge: isAdmin,
- UpdateBadge: isAdmin,
- DeleteBadge: isAdmin,
- AddUserBadges: isAdmin,
- CreateSocialMedia: isAuthenticated,
- DeleteSocialMedia: isAuthenticated,
- // AddBadgeRewarded: isAdmin,
- // RemoveBadgeRewarded: isAdmin,
- reward: isAdmin,
- unreward: isAdmin,
- // addFruitToBasket: isAuthenticated
- follow: isAuthenticated,
- unfollow: isAuthenticated,
- shout: isAuthenticated,
- unshout: isAuthenticated,
- changePassword: isAuthenticated,
- enable: isModerator,
- disable: isModerator,
- CreateComment: isAuthenticated,
- DeleteComment: isAuthor,
- // CreateUser: allow,
- },
- User: {
- email: isMyOwn,
- password: isMyOwn,
- privateKey: isMyOwn,
- },
+const isDeletingOwnAccount = rule({
+ cache: 'no_cache',
+})(async (parent, args, context, info) => {
+ return context.user.id === args.id
})
+// Permissions
+const permissions = shield(
+ {
+ Query: {
+ '*': deny,
+ findPosts: allow,
+ Category: isAdmin,
+ Tag: isAdmin,
+ Report: isModerator,
+ Notification: isAdmin,
+ statistics: allow,
+ currentUser: allow,
+ Post: or(onlyEnabledContent, isModerator),
+ Comment: allow,
+ User: allow,
+ isLoggedIn: allow,
+ },
+ Mutation: {
+ '*': deny,
+ login: allow,
+ UpdateNotification: belongsToMe,
+ CreateUser: isAdmin,
+ UpdateUser: onlyYourself,
+ CreatePost: isAuthenticated,
+ UpdatePost: isAuthor,
+ DeletePost: isAuthor,
+ report: isAuthenticated,
+ CreateBadge: isAdmin,
+ UpdateBadge: isAdmin,
+ DeleteBadge: isAdmin,
+ AddUserBadges: isAdmin,
+ CreateSocialMedia: isAuthenticated,
+ DeleteSocialMedia: isAuthenticated,
+ // AddBadgeRewarded: isAdmin,
+ // RemoveBadgeRewarded: isAdmin,
+ reward: isAdmin,
+ unreward: isAdmin,
+ // addFruitToBasket: isAuthenticated
+ follow: isAuthenticated,
+ unfollow: isAuthenticated,
+ shout: isAuthenticated,
+ unshout: isAuthenticated,
+ changePassword: isAuthenticated,
+ enable: isModerator,
+ disable: isModerator,
+ CreateComment: isAuthenticated,
+ DeleteComment: isAuthor,
+ DeleteUser: isDeletingOwnAccount,
+ },
+ User: {
+ email: isMyOwn,
+ password: isMyOwn,
+ privateKey: isMyOwn,
+ },
+ },
+ {
+ fallbackRule: allow,
+ },
+)
+
export default permissions
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index 79bba0a5d..4e060dc90 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -7,12 +7,14 @@ let headers
const factory = Factory()
beforeEach(async () => {
- await factory.create('User', { email: 'user@example.org', password: '1234' })
+ const adminParams = { role: 'admin', email: 'admin@example.org', password: '1234' }
+ await factory.create('User', adminParams)
await factory.create('User', {
email: 'someone@example.org',
password: '1234',
})
- headers = await login({ email: 'user@example.org', password: '1234' })
+ // we need to be an admin, otherwise we're not authorized to create a user
+ headers = await login(adminParams)
authenticatedClient = new GraphQLClient(host, { headers })
})
diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js
index 9e2ec70a2..3bff53ddb 100644
--- a/backend/src/schema/resolvers/posts.spec.js
+++ b/backend/src/schema/resolvers/posts.spec.js
@@ -74,6 +74,22 @@ describe('CreatePost', () => {
await expect(client.request(mutation)).resolves.toMatchObject(expected)
})
})
+
+ describe('language', () => {
+ it('allows a user to set the language of the post', async () => {
+ const createPostWithLanguageMutation = `
+ mutation {
+ CreatePost(title: "I am a title", content: "Some content", language: "en") {
+ language
+ }
+ }
+ `
+ const expected = { CreatePost: { language: 'en' } }
+ await expect(client.request(createPostWithLanguageMutation)).resolves.toEqual(
+ expect.objectContaining(expected),
+ )
+ })
+ })
})
})
diff --git a/backend/src/schema/resolvers/user_management.spec.js b/backend/src/schema/resolvers/user_management.spec.js
index cf648a6bd..463c5ea6d 100644
--- a/backend/src/schema/resolvers/user_management.spec.js
+++ b/backend/src/schema/resolvers/user_management.spec.js
@@ -315,6 +315,8 @@ describe('change password', () => {
describe('do not expose private RSA key', () => {
let headers
let client
+ let authenticatedClient
+
const queryUserPuplicKey = gql`
query($queriedUserSlug: String) {
User(slug: $queriedUserSlug) {
@@ -332,7 +334,7 @@ describe('do not expose private RSA key', () => {
}
`
- const actionGenUserWithKeys = async () => {
+ const generateUserWithKeys = async authenticatedClient => {
// Generate user with "privateKey" via 'CreateUser' mutation instead of using the factories "factory.create('User', {...})", see above.
const variables = {
id: 'bcb2d923-f3af-479e-9f00-61b12e864667',
@@ -341,7 +343,7 @@ describe('do not expose private RSA key', () => {
name: 'Apfel Strudel',
email: 'apfel-strudel@test.org',
}
- await client.request(
+ await authenticatedClient.request(
gql`
mutation($id: ID, $password: String!, $slug: String, $name: String, $email: String!) {
CreateUser(id: $id, password: $password, slug: $slug, name: $name, email: $email) {
@@ -353,14 +355,23 @@ describe('do not expose private RSA key', () => {
)
}
- // not authenticate
beforeEach(async () => {
+ const adminParams = {
+ role: 'admin',
+ email: 'admin@example.org',
+ password: '1234',
+ }
+ // create an admin user who has enough permissions to create other users
+ await factory.create('User', adminParams)
+ const headers = await login(adminParams)
+ authenticatedClient = new GraphQLClient(host, { headers })
+ // but also create an unauthenticated client to issue the `User` query
client = new GraphQLClient(host)
})
describe('unauthenticated query of "publicKey" (does the RSA key pair get generated at all?)', () => {
it('returns publicKey', async () => {
- await actionGenUserWithKeys()
+ await generateUserWithKeys(authenticatedClient)
await expect(
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }),
).toEqual(
@@ -378,7 +389,7 @@ describe('do not expose private RSA key', () => {
describe('unauthenticated query of "privateKey"', () => {
it('throws "Not Authorised!"', async () => {
- await actionGenUserWithKeys()
+ await generateUserWithKeys(authenticatedClient)
await expect(
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }),
).rejects.toThrow('Not Authorised')
@@ -393,7 +404,7 @@ describe('do not expose private RSA key', () => {
describe('authenticated query of "publicKey"', () => {
it('returns publicKey', async () => {
- await actionGenUserWithKeys()
+ await generateUserWithKeys(authenticatedClient)
await expect(
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }),
).toEqual(
@@ -411,7 +422,7 @@ describe('do not expose private RSA key', () => {
describe('authenticated query of "privateKey"', () => {
it('throws "Not Authorised!"', async () => {
- await actionGenUserWithKeys()
+ await generateUserWithKeys(authenticatedClient)
await expect(
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }),
).rejects.toThrow('Not Authorised')
diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js
index 53bf0967e..c5c3701b5 100644
--- a/backend/src/schema/resolvers/users.js
+++ b/backend/src/schema/resolvers/users.js
@@ -11,5 +11,27 @@ export default {
params = await fileUpload(params, { file: 'avatarUpload', url: 'avatar' })
return neo4jgraphql(object, params, context, resolveInfo, false)
},
+ DeleteUser: async (object, params, context, resolveInfo) => {
+ const { resource } = params
+ const session = context.driver.session()
+
+ if (resource && resource.length) {
+ await Promise.all(
+ resource.map(async node => {
+ await session.run(
+ `
+ MATCH (resource:${node})<-[:WROTE]-(author:User {id: $userId})
+ SET resource.deleted = true
+ RETURN author`,
+ {
+ userId: context.user.id,
+ },
+ )
+ }),
+ )
+ session.close()
+ }
+ return neo4jgraphql(object, params, context, resolveInfo, false)
+ },
},
}
diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js
index a5c50f4f9..352d38eaa 100644
--- a/backend/src/schema/resolvers/users.spec.js
+++ b/backend/src/schema/resolvers/users.spec.js
@@ -1,6 +1,7 @@
import { GraphQLClient } from 'graphql-request'
-import { host } from '../../jest/helpers'
+import { login, host } from '../../jest/helpers'
import Factory from '../../seed/factories'
+import gql from 'graphql-tag'
const factory = Factory()
let client
@@ -18,27 +19,58 @@ describe('users', () => {
}
}
`
- client = new GraphQLClient(host)
-
- it('with password and email', async () => {
+ describe('given valid password and email', () => {
const variables = {
name: 'John Doe',
password: '123',
email: '123@123.de',
}
- const expected = {
- CreateUser: {
- id: expect.any(String),
- },
- }
- await expect(client.request(mutation, variables)).resolves.toEqual(expected)
+
+ describe('unauthenticated', () => {
+ beforeEach(async () => {
+ client = new GraphQLClient(host)
+ })
+
+ it('is not allowed to create users', async () => {
+ await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
+ })
+ })
+
+ describe('authenticated admin', () => {
+ beforeEach(async () => {
+ const adminParams = {
+ role: 'admin',
+ email: 'admin@example.org',
+ password: '1234',
+ }
+ await factory.create('User', adminParams)
+ const headers = await login(adminParams)
+ client = new GraphQLClient(host, { headers })
+ })
+
+ it('is allowed to create new users', async () => {
+ const expected = {
+ CreateUser: {
+ id: expect.any(String),
+ },
+ }
+ await expect(client.request(mutation, variables)).resolves.toEqual(expected)
+ })
+ })
})
})
describe('UpdateUser', () => {
- beforeEach(async () => {
- await factory.create('User', { id: 'u47', name: 'John Doe' })
- })
+ const userParams = {
+ email: 'user@example.org',
+ password: '1234',
+ id: 'u47',
+ name: 'John Doe',
+ }
+ const variables = {
+ id: 'u47',
+ name: 'John Doughnut',
+ }
const mutation = `
mutation($id: ID!, $name: String) {
@@ -48,38 +80,199 @@ describe('users', () => {
}
}
`
- client = new GraphQLClient(host)
- it('name within specifications', async () => {
- const variables = {
- id: 'u47',
- name: 'James Doe',
- }
- const expected = {
- UpdateUser: {
- id: 'u47',
+ beforeEach(async () => {
+ await factory.create('User', userParams)
+ })
+
+ describe('as another user', () => {
+ beforeEach(async () => {
+ const someoneElseParams = {
+ email: 'someoneElse@example.org',
+ password: '1234',
name: 'James Doe',
- },
- }
- await expect(client.request(mutation, variables)).resolves.toEqual(expected)
+ }
+
+ await factory.create('User', someoneElseParams)
+ const headers = await login(someoneElseParams)
+ client = new GraphQLClient(host, { headers })
+ })
+
+ it('is not allowed to change other user accounts', async () => {
+ await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
+ })
})
- it('with no name', async () => {
- const variables = {
- id: 'u47',
- name: null,
+ describe('as the same user', () => {
+ beforeEach(async () => {
+ const headers = await login(userParams)
+ client = new GraphQLClient(host, { headers })
+ })
+
+ it('name within specifications', async () => {
+ const expected = {
+ UpdateUser: {
+ id: 'u47',
+ name: 'John Doughnut',
+ },
+ }
+ await expect(client.request(mutation, variables)).resolves.toEqual(expected)
+ })
+
+ it('with no name', async () => {
+ const variables = {
+ id: 'u47',
+ name: null,
+ }
+ const expected = 'Username must be at least 3 characters long!'
+ await expect(client.request(mutation, variables)).rejects.toThrow(expected)
+ })
+
+ it('with too short name', async () => {
+ const variables = {
+ id: 'u47',
+ name: ' ',
+ }
+ const expected = 'Username must be at least 3 characters long!'
+ await expect(client.request(mutation, variables)).rejects.toThrow(expected)
+ })
+ })
+ })
+
+ describe('DeleteUser', () => {
+ let deleteUserVariables
+ let asAuthor
+ const deleteUserMutation = gql`
+ mutation($id: ID!, $resource: [String]) {
+ DeleteUser(id: $id, resource: $resource) {
+ id
+ contributions {
+ id
+ deleted
+ }
+ comments {
+ id
+ deleted
+ }
+ }
}
- const expected = 'Username must be at least 3 characters long!'
- await expect(client.request(mutation, variables)).rejects.toThrow(expected)
+ `
+ beforeEach(async () => {
+ asAuthor = await factory.create('User', {
+ email: 'test@example.org',
+ password: '1234',
+ id: 'u343',
+ })
+ await factory.create('User', {
+ email: 'friendsAccount@example.org',
+ password: '1234',
+ id: 'u565',
+ })
+ deleteUserVariables = { id: 'u343', resource: [] }
})
- it('with too short name', async () => {
- const variables = {
- id: 'u47',
- name: ' ',
- }
- const expected = 'Username must be at least 3 characters long!'
- await expect(client.request(mutation, variables)).rejects.toThrow(expected)
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ client = new GraphQLClient(host)
+ await expect(client.request(deleteUserMutation, deleteUserVariables)).rejects.toThrow(
+ 'Not Authorised',
+ )
+ })
+ })
+
+ describe('authenticated', () => {
+ let headers
+ beforeEach(async () => {
+ headers = await login({
+ email: 'test@example.org',
+ password: '1234',
+ })
+ client = new GraphQLClient(host, { headers })
+ })
+
+ describe("attempting to delete another user's account", () => {
+ it('throws an authorization error', async () => {
+ deleteUserVariables = { id: 'u565' }
+ await expect(client.request(deleteUserMutation, deleteUserVariables)).rejects.toThrow(
+ 'Not Authorised',
+ )
+ })
+ })
+
+ describe('attempting to delete my own account', () => {
+ let expectedResponse
+ beforeEach(async () => {
+ await asAuthor.authenticateAs({
+ email: 'test@example.org',
+ password: '1234',
+ })
+ await asAuthor.create('Post', {
+ id: 'p139',
+ content: 'Post by user u343',
+ })
+ await asAuthor.create('Comment', {
+ id: 'c155',
+ postId: 'p139',
+ content: 'Comment by user u343',
+ })
+ expectedResponse = {
+ DeleteUser: {
+ id: 'u343',
+ contributions: [{ id: 'p139', deleted: false }],
+ comments: [{ id: 'c155', deleted: false }],
+ },
+ }
+ })
+ it("deletes my account, but doesn't delete posts or comments by default", async () => {
+ await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
+ expectedResponse,
+ )
+ })
+
+ describe("deletes a user's", () => {
+ it('posts on request', async () => {
+ deleteUserVariables = { id: 'u343', resource: ['Post'] }
+ expectedResponse = {
+ DeleteUser: {
+ id: 'u343',
+ contributions: [{ id: 'p139', deleted: true }],
+ comments: [{ id: 'c155', deleted: false }],
+ },
+ }
+ await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
+ expectedResponse,
+ )
+ })
+
+ it('comments on request', async () => {
+ deleteUserVariables = { id: 'u343', resource: ['Comment'] }
+ expectedResponse = {
+ DeleteUser: {
+ id: 'u343',
+ contributions: [{ id: 'p139', deleted: false }],
+ comments: [{ id: 'c155', deleted: true }],
+ },
+ }
+ await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
+ expectedResponse,
+ )
+ })
+
+ it('posts and comments on request', async () => {
+ deleteUserVariables = { id: 'u343', resource: ['Post', 'Comment'] }
+ expectedResponse = {
+ DeleteUser: {
+ id: 'u343',
+ contributions: [{ id: 'p139', deleted: true }],
+ comments: [{ id: 'c155', deleted: true }],
+ },
+ }
+ await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
+ expectedResponse,
+ )
+ })
+ })
+ })
})
})
})
diff --git a/backend/src/schema/types/schema.gql b/backend/src/schema/types/schema.gql
index ab8b25399..2a8be9e09 100644
--- a/backend/src/schema/types/schema.gql
+++ b/backend/src/schema/types/schema.gql
@@ -4,8 +4,9 @@ type Query {
currentUser: User
# Get the latest Network Statistics
statistics: Statistics!
- findPosts(filter: String!, limit: Int = 10): [Post]! @cypher(
- statement: """
+ findPosts(filter: String!, limit: Int = 10): [Post]!
+ @cypher(
+ statement: """
CALL db.index.fulltext.queryNodes('full_text_search', $filter)
YIELD node as post, score
MATCH (post)<-[:WROTE]-(user:User)
@@ -14,8 +15,8 @@ type Query {
AND NOT post.deleted = true AND NOT post.disabled = true
RETURN post
LIMIT $limit
- """
- )
+ """
+ )
CommentByPost(postId: ID!): [Comment]!
}
@@ -23,7 +24,7 @@ type Mutation {
# Get a JWT Token for the given Email and password
login(email: String!, password: String!): String!
signup(email: String!, password: String!): Boolean!
- changePassword(oldPassword:String!, newPassword: String!): String!
+ changePassword(oldPassword: String!, newPassword: String!): String!
report(id: ID!, description: String): Report
disable(id: ID!): ID
enable(id: ID!): ID
@@ -37,6 +38,7 @@ type Mutation {
follow(id: ID!, type: FollowTypeEnum): Boolean!
# Unfollow the given Type and ID
unfollow(id: ID!, type: FollowTypeEnum): Boolean!
+ DeleteUser(id: ID!, resource: [String]): User
}
type Statistics {
@@ -53,7 +55,7 @@ type Statistics {
type Notification {
id: ID!
- read: Boolean,
+ read: Boolean
user: User @relation(name: "NOTIFIED", direction: "OUT")
post: Post @relation(name: "NOTIFIED", direction: "IN")
createdAt: String
@@ -80,7 +82,8 @@ type Report {
id: ID!
submitter: User @relation(name: "REPORTED", direction: "IN")
description: String
- type: String! @cypher(statement: "MATCH (resource)<-[:REPORTED]-(this) RETURN labels(resource)[0]")
+ type: String!
+ @cypher(statement: "MATCH (resource)<-[:REPORTED]-(this) RETURN labels(resource)[0]")
createdAt: String
comment: Comment @relation(name: "REPORTED", direction: "OUT")
post: Post @relation(name: "REPORTED", direction: "OUT")
@@ -131,4 +134,3 @@ type SocialMedia {
url: String
ownedBy: [User]! @relation(name: "OWNED", direction: "IN")
}
-
diff --git a/backend/src/schema/types/type/Post.gql b/backend/src/schema/types/type/Post.gql
index 1179c3e20..271d92750 100644
--- a/backend/src/schema/types/type/Post.gql
+++ b/backend/src/schema/types/type/Post.gql
@@ -1,40 +1,3 @@
-enum FilterBubbleAuthorEnum {
- following
- all
-}
-
-input FilterBubble {
- author: FilterBubbleAuthorEnum
-}
-
-type Query {
- Post(
- id: ID
- activityId: String
- objectId: String
- title: String
- slug: String
- content: String
- contentExcerpt: String
- image: String
- imageUpload: Upload
- visibility: Visibility
- deleted: Boolean
- disabled: Boolean
- createdAt: String
- updatedAt: String
- commentsCount: Int
- shoutedCount: Int
- shoutedByCurrentUser: Boolean
- _id: String
- first: Int
- offset: Int
- orderBy: [_PostOrdering]
- filter: _PostFilter
- filterBubble: FilterBubble
- ): [Post]
-}
-
type Post {
id: ID!
activityId: String
@@ -52,29 +15,37 @@ type Post {
disabledBy: User @relation(name: "DISABLED", direction: "IN")
createdAt: String
updatedAt: String
-
- relatedContributions: [Post]! @cypher(
- statement: """
+ language: String
+ relatedContributions: [Post]!
+ @cypher(
+ statement: """
MATCH (this)-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post)
RETURN DISTINCT post
LIMIT 10
- """
- )
+ """
+ )
tags: [Tag]! @relation(name: "TAGGED", direction: "OUT")
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
comments: [Comment]! @relation(name: "COMMENTS", direction: "IN")
- commentsCount: Int! @cypher(statement: "MATCH (this)<-[:COMMENTS]-(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
+ commentsCount: Int!
+ @cypher(
+ statement: "MATCH (this)<-[:COMMENTS]-(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)"
+ )
shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN")
- shoutedCount: Int! @cypher(statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
+ shoutedCount: Int!
+ @cypher(
+ statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)"
+ )
# Has the currently logged in user shouted that post?
- shoutedByCurrentUser: Boolean! @cypher(
- statement: """
+ shoutedByCurrentUser: Boolean!
+ @cypher(
+ statement: """
MATCH (this)<-[:SHOUTED]-(u:User {id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
- """
- )
+ """
+ )
}
diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql
index 1287aa45f..6836f16fe 100644
--- a/backend/src/schema/types/type/User.gql
+++ b/backend/src/schema/types/type/User.gql
@@ -56,14 +56,14 @@ type User {
contributionsCount: Int! @cypher(
statement: """
MATCH (this)-[:WROTE]->(r:Post)
- WHERE (NOT exists(r.deleted) OR r.deleted = false)
- AND (NOT exists(r.disabled) OR r.disabled = false)
+ WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)
"""
)
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
+ commentedCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment)-[:COMMENTS]->(p:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true AND NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))")
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
@@ -77,4 +77,4 @@ type User {
badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
-}
\ No newline at end of file
+}
diff --git a/backend/test/features/activity-follow.feature b/backend/test/features/activity-follow.feature
index 3cfe73340..7aa0c447d 100644
--- a/backend/test/features/activity-follow.feature
+++ b/backend/test/features/activity-follow.feature
@@ -10,6 +10,7 @@ Feature: Follow a user
| stuart-little |
| tero-vota |
+ @wip
Scenario: Send a follow to a user inbox and make sure it's added to the right followers collection
When I send a POST request with the following activity to "/activitypub/users/tero-vota/inbox":
"""
diff --git a/backend/test/features/activity-like.feature b/backend/test/features/activity-like.feature
index ec8c99110..26ef9c857 100644
--- a/backend/test/features/activity-like.feature
+++ b/backend/test/features/activity-like.feature
@@ -27,6 +27,7 @@ Feature: Like an object like an article or note
}
"""
+ @wip
Scenario: Send a like of a person to an users inbox and make sure it's added to the likes collection
When I send a POST request with the following activity to "/activitypub/users/karl-heinz/inbox":
"""
diff --git a/backend/yarn.lock b/backend/yarn.lock
index 9c8fdc3c5..d2c5da176 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -1029,10 +1029,10 @@
"@types/express-serve-static-core" "*"
"@types/serve-static" "*"
-"@types/express@4.16.1":
- version "4.16.1"
- resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.1.tgz#d756bd1a85c34d87eaf44c888bad27ba8a4b7cf0"
- integrity sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==
+"@types/express@4.17.0":
+ version "4.17.0"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.0.tgz#49eaedb209582a86f12ed9b725160f12d04ef287"
+ integrity sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "*"
@@ -1119,10 +1119,10 @@
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
-"@types/yup@0.26.14":
- version "0.26.14"
- resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.14.tgz#d31f3b9a04039cca70ebb4db4d6c7fc3f694e80b"
- integrity sha512-OcBtVLHvYULVSltpuBdhFiVOKoSsOS58D872HydO93oBf3OdGq5zb+LnqGo18TNNSV2aW8hjIdS6H+wp68zFtQ==
+"@types/yup@0.26.16":
+ version "0.26.16"
+ resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.16.tgz#75c428236207c48d9f8062dd1495cda8c5485a15"
+ integrity sha512-E2RNc7DSeQ+2EIJ1H3+yFjYu6YiyQBUJ7yNpIxomrYJ3oFizLZ5yDS3T1JTUNBC2OCRkgnhLS0smob5UuCHfNA==
"@types/zen-observable@^0.5.3":
version "0.5.4"
@@ -1141,6 +1141,13 @@
dependencies:
tslib "^1.9.3"
+"@wry/equality@^0.1.2":
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.7.tgz#512234d078341c32cabda66b89b5dddb5741d9b9"
+ integrity sha512-p1rhJ6PQzpsBr9cMJMHvvx3LQEA28HFX7fAQx6khAX+1lufFeBuk+iRCAyHwj3v6JbpGKvHNa66f+9cpU8c7ew==
+ dependencies:
+ tslib "^1.9.3"
+
abab@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"
@@ -1281,13 +1288,13 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
-apollo-cache-control@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.7.1.tgz#3d4fba232f561f096f61051e103bf58ee4bf8b54"
- integrity sha512-3h1TEoMnzex6IIiFb5Ja3owTyLwT5YzK69cRgrSpSscdpYc3ID4KVs0Ht9cbOUmb/L/UKtYVkRC8KeVAYmHEjQ==
+apollo-cache-control@0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.7.2.tgz#b8852422d973c582493e85c776abc9c660090162"
+ integrity sha512-7prjFN8H9lRE0npqGG8kM3XICvNCcgQt6eCy8kkcPOIZwM+F8m8ShjEfNF9UWW32i+poOk3G67HegPRyjCc6/Q==
dependencies:
apollo-server-env "2.4.0"
- graphql-extensions "0.7.1"
+ graphql-extensions "0.7.2"
apollo-cache-control@^0.1.0:
version "0.1.1"
@@ -1296,34 +1303,34 @@ apollo-cache-control@^0.1.0:
dependencies:
graphql-extensions "^0.0.x"
-apollo-cache-inmemory@~1.6.1:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.1.tgz#536b6f366461f6264250041f9146363e2faa1d4c"
- integrity sha512-c/WJjh9MTWcdussCTjLKufpPjTx3qOFkBPHIDOOpQ+U0B7K1PczPl9N0LaC4ir3wAWL7s4A0t2EKtoR+6UP92g==
+apollo-cache-inmemory@~1.6.2:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.2.tgz#bbf2e4e1eacdf82b2d526f5c2f3b37e5acee3c5e"
+ integrity sha512-AyCl3PGFv5Qv1w4N9vlg63GBPHXgMCekZy5mhlS042ji0GW84uTySX+r3F61ZX3+KM1vA4m9hQyctrEGiv5XjQ==
dependencies:
- apollo-cache "^1.3.1"
- apollo-utilities "^1.3.1"
+ apollo-cache "^1.3.2"
+ apollo-utilities "^1.3.2"
optimism "^0.9.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
-apollo-cache@1.3.1, apollo-cache@^1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.1.tgz#c015f93a9a7f32b3eeea0c471addd6e854da754c"
- integrity sha512-BJ/Mehr3u6XCaHYSmgZ6DM71Fh30OkW6aEr828WjHvs+7i0RUuP51/PM7K6T0jPXtuw7UbArFFPZZsNgXnyyJA==
+apollo-cache@1.3.2, apollo-cache@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.2.tgz#df4dce56240d6c95c613510d7e409f7214e6d26a"
+ integrity sha512-+KA685AV5ETEJfjZuviRTEImGA11uNBp/MJGnaCvkgr+BYRrGLruVKBv6WvyFod27WEB2sp7SsG8cNBKANhGLg==
dependencies:
- apollo-utilities "^1.3.1"
+ apollo-utilities "^1.3.2"
tslib "^1.9.3"
-apollo-client@~2.6.1:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.1.tgz#fcf328618d6ad82b750a988bec113fe6edc8ba94"
- integrity sha512-Tb6ZthPZUHlGqeoH1WC8Qg/tLnkk9H5+xj4e5nzOAC6dCOW3pVU9tYXscrWdmZ65UDUg1khvTNjrQgPhdf4aTQ==
+apollo-client@~2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.2.tgz#03b6af651e09b6e413e486ddc87464c85bd6e514"
+ integrity sha512-oks1MaT5x7gHcPeC8vPC1UzzsKaEIC0tye+jg72eMDt5OKc7BobStTeS/o2Ib3e0ii40nKxGBnMdl/Xa/p56Yg==
dependencies:
"@types/zen-observable" "^0.8.0"
- apollo-cache "1.3.1"
+ apollo-cache "1.3.2"
apollo-link "^1.0.0"
- apollo-utilities "1.3.1"
+ apollo-utilities "1.3.2"
symbol-observable "^1.0.2"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@@ -1337,24 +1344,24 @@ apollo-datasource@0.5.0:
apollo-server-caching "0.4.0"
apollo-server-env "2.4.0"
-apollo-engine-reporting-protobuf@0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.3.0.tgz#2c764c054ff9968387cf16115546e0d5b04ee9f1"
- integrity sha512-PYowpx/E+TJT/8nKpp3JmJuKh3x1SZcxDF6Cquj0soV205TUpFFCZQMi91i5ACiEp2AkYvM/GDBIrw+rfIwzTg==
+apollo-engine-reporting-protobuf@0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.3.1.tgz#a581257fa8e3bb115ce38bf1b22e052d1475ad69"
+ integrity sha512-Ui3nPG6BSZF8BEqxFs6EkX6mj2OnFLMejxEHSOdM82bakyeouCGd7J0fiy8AD6liJoIyc4X7XfH4ZGGMvMh11A==
dependencies:
protobufjs "^6.8.6"
-apollo-engine-reporting@1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.2.1.tgz#0b77fad2e9221d62f4a29b8b4fab8f7f47dcc1d6"
- integrity sha512-DVXZhz/nSZR4lphakjb1guAD0qJ7Wm1PVtZEBjN097cnOor4XSOzQlPfTaYtVuhlxUKUuCx1syiBbOuV8sKqXg==
+apollo-engine-reporting@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.3.1.tgz#f2c2c63f865871a57c15cdbb2a3bcd4b4af28115"
+ integrity sha512-e0Xp+0yite8DH/xm9fnJt42CxfWAcY6waiq3icCMAgO9T7saXzVOPpl84SkuA+hIJUBtfaKrTnC+7Jxi/I7OrQ==
dependencies:
- apollo-engine-reporting-protobuf "0.3.0"
+ apollo-engine-reporting-protobuf "0.3.1"
apollo-graphql "^0.3.0"
- apollo-server-core "2.6.1"
+ apollo-server-core "2.6.3"
apollo-server-env "2.4.0"
async-retry "^1.2.1"
- graphql-extensions "0.7.1"
+ graphql-extensions "0.7.2"
apollo-env@0.5.1:
version "0.5.1"
@@ -1424,24 +1431,24 @@ apollo-server-caching@0.4.0:
dependencies:
lru-cache "^5.0.0"
-apollo-server-core@2.6.1:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.1.tgz#d0d878b0a4959b6c661fc43300ce45b29996176a"
- integrity sha512-jO2BtcP7ozSSK5qtw1gGDwO66WSNtzhvpDJD7erkA9byv8Z0jB2QIPNWN6iaj311LaPahM05k+8hMIhFy9oHWg==
+apollo-server-core@2.6.3:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.3.tgz#786c8251c82cf29acb5cae9635a321f0644332ae"
+ integrity sha512-tfC0QO1NbJW3ShkB5pRCnUaYEkW2AwnswaTeedkfv//EO3yiC/9LeouCK5F22T8stQG+vGjvCqf0C8ldI/XsIA==
dependencies:
"@apollographql/apollo-tools" "^0.3.6"
"@apollographql/graphql-playground-html" "1.6.20"
"@types/ws" "^6.0.0"
- apollo-cache-control "0.7.1"
+ apollo-cache-control "0.7.2"
apollo-datasource "0.5.0"
- apollo-engine-reporting "1.2.1"
+ apollo-engine-reporting "1.3.1"
apollo-server-caching "0.4.0"
apollo-server-env "2.4.0"
apollo-server-errors "2.3.0"
- apollo-server-plugin-base "0.5.1"
- apollo-tracing "0.7.1"
+ apollo-server-plugin-base "0.5.2"
+ apollo-tracing "0.7.2"
fast-json-stable-stringify "^2.0.0"
- graphql-extensions "0.7.1"
+ graphql-extensions "0.7.2"
graphql-subscriptions "^1.0.0"
graphql-tag "^2.9.2"
graphql-tools "^4.0.0"
@@ -1472,18 +1479,18 @@ apollo-server-errors@2.3.0:
resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.0.tgz#700622b66a16dffcad3b017e4796749814edc061"
integrity sha512-rUvzwMo2ZQgzzPh2kcJyfbRSfVKRMhfIlhY7BzUfM4x6ZT0aijlgsf714Ll3Mbf5Fxii32kD0A/DmKsTecpccw==
-apollo-server-express@2.6.1:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.1.tgz#1e2649d3fd38c0c0a2c830090fd41e086b259c9f"
- integrity sha512-TVu68LVp+COMGOXuxc0OFeCUQiPApxy7Isv2Vk85nikZV4t4FXlODB6PrRKf5rfvP31dvGsfE6GHPJTLLbKfyg==
+apollo-server-express@2.6.3:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.3.tgz#62034c978f84207615c0430fb37ab006f71146fe"
+ integrity sha512-8ca+VpKArgNzFar0D3DesWnn0g9YDtFLhO56TQprHh2Spxu9WxTnYNjsYs2MCCNf+iV/uy7vTvEknErvnIcZaQ==
dependencies:
"@apollographql/graphql-playground-html" "1.6.20"
"@types/accepts" "^1.3.5"
"@types/body-parser" "1.17.0"
"@types/cors" "^2.8.4"
- "@types/express" "4.16.1"
+ "@types/express" "4.17.0"
accepts "^1.3.5"
- apollo-server-core "2.6.1"
+ apollo-server-core "2.6.3"
body-parser "^1.18.3"
cors "^2.8.4"
graphql-subscriptions "^1.0.0"
@@ -1511,36 +1518,36 @@ apollo-server-module-graphiql@^1.3.4, apollo-server-module-graphiql@^1.4.0:
resolved "https://registry.yarnpkg.com/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz#c559efa285578820709f1769bb85d3b3eed3d8ec"
integrity sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA==
-apollo-server-plugin-base@0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.1.tgz#b81056666763879bdc98d8d58f3c4c43cbb30da6"
- integrity sha512-UejnBk6XDqYQ+Ydkbm+gvlOzP+doQA8glVUULs8rCi0/MshsFSsBVl6rtzruELDBVgZhJgGsd4pUexcvNc3aZA==
+apollo-server-plugin-base@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.2.tgz#f97ba983f1e825fec49cba8ff6a23d00e1901819"
+ integrity sha512-j81CpadRLhxikBYHMh91X4aTxfzFnmmebEiIR9rruS6dywWCxV2aLW87l9ocD1MiueNam0ysdwZkX4F3D4csNw==
-apollo-server-testing@~2.6.1:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.1.tgz#447f34980819fa52b120f26c632fab4efc55435b"
- integrity sha512-Qq0u79uKw3g14bq0nGxtUUiueFOv2ETkAax2mum+3f9Lm85VXELkY6c9bCWDVGjkUtt9Aog5qwSzWELb1KiUug==
+apollo-server-testing@~2.6.3:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.3.tgz#a0199a5d42000e60ecf0dea44b851f5f581e280e"
+ integrity sha512-LTkegcGVSkM+pA0FINDSYVl3TiFYKZyfjlKrEr/LN6wLiL6gbRgy6LMtk2j+qli/bnTDqqQREX8OEqmV8FKUoQ==
dependencies:
- apollo-server-core "2.6.1"
+ apollo-server-core "2.6.3"
-apollo-server@~2.6.1:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.1.tgz#1b1fc6020b75c0913550da5fa0f2005c62f1bc53"
- integrity sha512-Ed0zZjluRYPMC3Yr6oXQjcR11izu86nkjiS2MhjJA1mF8IXJfxbPp2hnX4Jf4vXPSkOP2e5ZHw0cdaIcu9GnRw==
+apollo-server@~2.6.3:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.3.tgz#71235325449c6d3881a5143975ca44c07a07d2d7"
+ integrity sha512-pTIXE5xEMAikKLTIBIqLNvimMETiZbzmiqDb6BGzIUicAz4Rxa1/+bDi1ZeJWrZQjE/TfBLd2Si3qam7dZGrjw==
dependencies:
- apollo-server-core "2.6.1"
- apollo-server-express "2.6.1"
+ apollo-server-core "2.6.3"
+ apollo-server-express "2.6.3"
express "^4.0.0"
graphql-subscriptions "^1.0.0"
graphql-tools "^4.0.0"
-apollo-tracing@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.7.1.tgz#6a7356b619f3aa0ca22c623b5d8bb1af5ca1c74c"
- integrity sha512-1BYQua+jCWFkZZJP0/rSpzY4XbLLbCrRHCYu8sJn0RCH/hs34BMdFXscS9uSglgIpXwUAIafgsU0hAVCrJjbTw==
+apollo-tracing@0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.7.2.tgz#7730159a4670bca465ac1bfa01f9902610a7aba4"
+ integrity sha512-bT4/n8Vy9DweC3+XWJelJD41FBlKMXR0OVxjLMiCe9clb4yTgKhYxRGTyh9KjmhWsng9gG/DphO0ixWsOgdXmA==
dependencies:
apollo-server-env "2.4.0"
- graphql-extensions "0.7.1"
+ graphql-extensions "0.7.2"
apollo-tracing@^0.1.0:
version "0.1.4"
@@ -1559,13 +1566,13 @@ apollo-upload-server@^7.0.0:
http-errors "^1.7.0"
object-path "^0.11.4"
-apollo-utilities@1.3.1, apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo-utilities@^1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.1.tgz#4c45f9b52783c324e2beef822700bdea374f82d1"
- integrity sha512-P5cJ75rvhm9hcx9V/xCW0vlHhRd0S2icEcYPoRYNTc5djbynpuO+mQuJ4zMHgjNDpvvDxDfZxXTJ6ZUuJZodiQ==
+apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo-utilities@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.2.tgz#8cbdcf8b012f664cd6cb5767f6130f5aed9115c9"
+ integrity sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==
dependencies:
+ "@wry/equality" "^0.1.2"
fast-json-stable-stringify "^2.0.0"
- lodash.isequal "^4.5.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@@ -2579,10 +2586,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
-date-fns@2.0.0-alpha.29:
- version "2.0.0-alpha.29"
- resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.29.tgz#9d4a36e3ebba63d009e957fea8fdfef7921bc6cb"
- integrity sha512-AIFZ0hG/1fdb7HZHTDyiEJdNiaFyZxXcx/kF8z3I9wxbhkN678KrrLSneKcsb0Xy5KqCA4wCIxmGpdVWSNZnpA==
+date-fns@2.0.0-alpha.33:
+ version "2.0.0-alpha.33"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.33.tgz#c2f73c3cc50ac301c9217eb93603c9bc40e891bf"
+ integrity sha512-tqUVEk3oxnJuNIvwAMKHAMo4uFRG0zXvjxZQll+BonoPt+m4NMcUgO14NDxbHuy7uYcrVErd2GdSsw02EDZQ7w==
debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
@@ -3704,10 +3711,10 @@ graphql-deduplicator@^2.0.1:
resolved "https://registry.yarnpkg.com/graphql-deduplicator/-/graphql-deduplicator-2.0.2.tgz#d8608161cf6be97725e178df0c41f6a1f9f778f3"
integrity sha512-0CGmTmQh4UvJfsaTPppJAcHwHln8Ayat7yXXxdnuWT+Mb1dBzkbErabCWzjXyKh/RefqlGTTA7EQOZHofMaKJA==
-graphql-extensions@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.7.1.tgz#f55b01ac8ddf09a215e21f34caeee3ae66a88f21"
- integrity sha512-4NkAz/f0j5a1DSPl3V77OcesBmwhHz56Soj0yTImlcDdRv9knyO2e+ehi1TIeKBOyIKS7d3A7zqOW/4ieGxlVA==
+graphql-extensions@0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.7.2.tgz#8711543f835661eaf24b48d6ac2aad44dbbd5506"
+ integrity sha512-TuVINuAOrEtzQAkAlCZMi9aP5rcZ+pVaqoBI5fD2k5O9fmb8OuXUQOW028MUhC66tg4E7h4YSF1uYUIimbu4SQ==
dependencies:
"@apollographql/apollo-tools" "^0.3.6"
@@ -3765,12 +3772,12 @@ graphql-request@~1.8.2:
dependencies:
cross-fetch "2.2.2"
-graphql-shield@~5.3.6:
- version "5.3.6"
- resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.6.tgz#20061b02f77056c0870a623c530ef28a1bf4fff4"
- integrity sha512-ihw/i4X+d1kpj1SVA6iBkVl2DZhPsI+xV08geR2TX3FWhpU7zakk/16yBzDRJTTCUgKsWfgyebrgIBsuhTwMnA==
+graphql-shield@~5.3.8:
+ version "5.3.8"
+ resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.8.tgz#f9e7ad2285f6cfbe20a8a49154ce6c1b184e3893"
+ integrity sha512-33rQ8U5jMurHIapctHk7hBcUg3nxC7fmMIMtyWiomJXhBmztFq/SG7jNaapnL5M7Q/0BmoaSQd3FLSpelP9KPw==
dependencies:
- "@types/yup" "0.26.14"
+ "@types/yup" "0.26.16"
lightercollective "^0.3.0"
object-hash "^1.3.1"
yup "^0.27.0"
@@ -5230,11 +5237,6 @@ lodash.isboolean@^3.0.3:
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
-lodash.isequal@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
- integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
-
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
@@ -5520,7 +5522,7 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
-ms@2.1.1, ms@^2.1.1, ms@~2.1.1:
+ms@2.1.1, ms@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
@@ -6228,10 +6230,10 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
-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==
+prettier@~1.18.2:
+ version "1.18.2"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
+ integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
pretty-format@^24.8.0:
version "24.8.0"
diff --git a/deployment/legacy-migration/maintenance-worker/binaries/.env b/deployment/legacy-migration/maintenance-worker/binaries/.env
new file mode 100644
index 000000000..773918095
--- /dev/null
+++ b/deployment/legacy-migration/maintenance-worker/binaries/.env
@@ -0,0 +1,6 @@
+# SSH Access
+# SSH_USERNAME='username'
+# SSH_HOST='example.org'
+
+# UPLOADS_DIRECTORY=/var/www/api/uploads
+OUTPUT_DIRECTORY='/uploads/'
\ No newline at end of file
diff --git a/deployment/legacy-migration/maintenance-worker/binaries/import_legacy_uploads b/deployment/legacy-migration/maintenance-worker/binaries/import_legacy_uploads
index 11fd81623..5c0b67d74 100755
--- a/deployment/legacy-migration/maintenance-worker/binaries/import_legacy_uploads
+++ b/deployment/legacy-migration/maintenance-worker/binaries/import_legacy_uploads
@@ -1,6 +1,11 @@
#!/usr/bin/env bash
set -e
+# import .env config
+set -o allexport
+source $(dirname "$0")/.env
+set +o allexport
+
for var in "SSH_USERNAME" "SSH_HOST" "UPLOADS_DIRECTORY"
do
if [[ -z "${!var}" ]]; then
@@ -9,4 +14,4 @@ do
fi
done
-rsync --archive --update --verbose ${SSH_USERNAME}@${SSH_HOST}:${UPLOADS_DIRECTORY}/ /uploads/
+rsync --archive --update --verbose ${SSH_USERNAME}@${SSH_HOST}:${UPLOADS_DIRECTORY}/ ${OUTPUT_DIRECTORY}
diff --git a/package.json b/package.json
index dd7454c54..fed6c742b 100644
--- a/package.json
+++ b/package.json
@@ -22,8 +22,8 @@
"codecov": "^3.5.0",
"cross-env": "^5.2.0",
"cypress": "^3.3.1",
- "cypress-cucumber-preprocessor": "^1.11.2",
- "cypress-file-upload": "^3.1.2",
+ "cypress-cucumber-preprocessor": "^1.12.0",
+ "cypress-file-upload": "^3.1.4",
"cypress-plugin-retries": "^1.2.2",
"dotenv": "^8.0.0",
"faker": "^4.1.0",
diff --git a/webapp/assets/styles/main.scss b/webapp/assets/styles/main.scss
index db967e973..560249b4a 100644
--- a/webapp/assets/styles/main.scss
+++ b/webapp/assets/styles/main.scss
@@ -10,7 +10,7 @@ $easeOut: cubic-bezier(0.19, 1, 0.22, 1);
&::before {
@include border-radius($border-radius-x-large);
box-shadow: inset 0 0 0 5px $color-danger;
- content: "";
+ content: '';
display: block;
position: absolute;
width: 100%;
@@ -102,10 +102,10 @@ hr {
height: 1px !important;
}
-[class$=menu-trigger] {
+[class$='menu-trigger'] {
user-select: none;
}
-[class$=menu-popover] {
+[class$='menu-popover'] {
display: inline-block;
nav {
@@ -145,10 +145,11 @@ hr {
}
}
-[class$="menu-popover"] {
+[class$='menu-popover'] {
min-width: 130px;
- a, button {
+ a,
+ button {
display: flex;
align-content: center;
align-items: center;
diff --git a/webapp/components/Avatar/Avatar.spec.js b/webapp/components/Avatar/Avatar.spec.js
index ae91fecfe..d3ebcb030 100644
--- a/webapp/components/Avatar/Avatar.spec.js
+++ b/webapp/components/Avatar/Avatar.spec.js
@@ -1,9 +1,11 @@
import { mount, createLocalVue } from '@vue/test-utils'
import Styleguide from '@human-connection/styleguide'
import Avatar from './Avatar.vue'
+import Filters from '~/plugins/vue-filters'
const localVue = createLocalVue()
localVue.use(Styleguide)
+localVue.use(Filters)
describe('Avatar.vue', () => {
let propsData = {}
diff --git a/webapp/components/Avatar/Avatar.vue b/webapp/components/Avatar/Avatar.vue
index 0d997c745..ec2f9b28b 100644
--- a/webapp/components/Avatar/Avatar.vue
+++ b/webapp/components/Avatar/Avatar.vue
@@ -1,5 +1,10 @@
-
Hier findest du weitere infos zum Thema.
{{ userName }} folgt niemandem
++ {{ userName }} {{ $t('profile.network.followingNobody') }} +
niemand folgt {{ userName }}
++ {{ userName }} {{ $t('profile.network.followedByNobody') }} +