mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge remote-tracking branch 'origin/master' into 553-delete-comment
# Conflicts: # webapp/components/Comment.vue # webapp/components/ContentMenu.vue # webapp/components/Modal/DeleteModal.spec.js # webapp/components/Modal/DeleteModal.vue # webapp/components/Modal/DisableModal.spec.js # webapp/components/Modal/DisableModal.vue # webapp/components/Modal/ReportModal.spec.js # webapp/components/Modal/ReportModal.vue # webapp/components/PostCard/index.vue # webapp/components/comments/CommentList/index.vue # webapp/locales/de.json # webapp/pages/index.vue # webapp/pages/profile/_id/_slug.vue
This commit is contained in:
commit
68980f85c6
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -5,7 +5,7 @@ before submitting a new issue. Following one of the issue templates will ensure
|
|||||||
Thanks!
|
Thanks!
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Issue
|
## 💬 Issue
|
||||||
<!-- Describe your Issue in detail. -->
|
<!-- Describe your Issue in detail. -->
|
||||||
|
|
||||||
<!-- Attach screenshots and drawings if needed. -->
|
<!-- Attach screenshots and drawings if needed. -->
|
||||||
|
|||||||
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: 🐛 Bug report
|
name: 🐛 Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
|
labels: bug
|
||||||
|
title: 🐛 [Bug]
|
||||||
---
|
---
|
||||||
|
|
||||||
## :bug: Bugreport
|
## :bug: Bugreport
|
||||||
|
|||||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,22 +1,12 @@
|
|||||||
---
|
---
|
||||||
name: 🚀 Feature request
|
name: 🚀 Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
|
labels: feature
|
||||||
|
title: 🚀 [Feature]
|
||||||
---
|
---
|
||||||
|
|
||||||
## :rocket: Feature
|
## :rocket: Feature
|
||||||
<!-- Describe the Feature. -->
|
<!-- Describe the Feature. Use Screenshots if possible. -->
|
||||||
|
|
||||||
|
|
||||||
### Is your feature request related to a problem? Please describe.
|
|
||||||
<!-- A clear and concise description of what the problem is.
|
|
||||||
Ex. I'm always frustrated when [...] -->
|
|
||||||
|
|
||||||
|
|
||||||
### Describe the prefered solution and alternatives you've considered
|
|
||||||
<!-- A clear and concise description of what you want to happen.
|
|
||||||
Are there any alternative solutions or features you've considered? -->
|
|
||||||
|
|
||||||
|
|
||||||
### Design & Layout
|
### Design & Layout
|
||||||
<!-- Attach Screenshots and Drawings. -->
|
<!-- Attach Screenshots and Drawings. -->
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/question.md
vendored
2
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: 💬 Question
|
name: 💬 Question
|
||||||
about: If you need help understanding HumanConnection.
|
about: If you need help understanding HumanConnection.
|
||||||
|
labels: question
|
||||||
|
title: 💬 [Question]
|
||||||
---
|
---
|
||||||
<!-- Chat with Team HumanConnection -->
|
<!-- Chat with Team HumanConnection -->
|
||||||
<!-- If you need an answer right away, visit the HumanConnection Discord:
|
<!-- If you need an answer right away, visit the HumanConnection Discord:
|
||||||
|
|||||||
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,27 +1,12 @@
|
|||||||
## Pullrequest
|
## 🍰 Pullrequest
|
||||||
<!-- Describe the Pullrequest. -->
|
<!-- Describe the Pullrequest. Use Screenshots if possible. -->
|
||||||
|
|
||||||
### Issues
|
### Issues
|
||||||
<!-- Which Issues does this fix, which are related?
|
<!-- Which Issues does this fix, which are related?
|
||||||
- fixes #XXX
|
- fixes #XXX
|
||||||
- relates #XXX
|
- relates #XXX
|
||||||
-->
|
-->
|
||||||
- [X] None
|
- None
|
||||||
|
|
||||||
### Checklist
|
|
||||||
<!-- Anything important to be thought of when deploying?
|
|
||||||
- [ ] Env-Variables adjustment needed
|
|
||||||
- [ ] Breaking/critical change
|
|
||||||
-->
|
|
||||||
- [X] None
|
|
||||||
|
|
||||||
### How2Test
|
|
||||||
<!-- Give a detailed description how to test your PR and confirm it is working as expected. -->
|
|
||||||
<!-- Maintainers will check the Tests
|
|
||||||
- [ ] Test1
|
|
||||||
- [ ] Test2
|
|
||||||
-->
|
|
||||||
- [X] None
|
|
||||||
|
|
||||||
### Todo
|
### Todo
|
||||||
<!-- In case some parts are still missing, list them here. -->
|
<!-- In case some parts are still missing, list them here. -->
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,7 +1,6 @@
|
|||||||
.env
|
.env
|
||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
.vscode
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
|
|||||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"octref.vetur",
|
||||||
|
"gruntfuggly.todo-tree",
|
||||||
|
]
|
||||||
|
}
|
||||||
12
.vscode/settings.json
vendored
Normal file
12
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
{
|
||||||
|
"language": "vue",
|
||||||
|
"autoFix": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"eslint.autoFixOnSave": true
|
||||||
|
}
|
||||||
@ -1,20 +1,25 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
"extends": "standard",
|
env: {
|
||||||
"parser": "babel-eslint",
|
es6: true,
|
||||||
"env": {
|
node: true,
|
||||||
"es6": true,
|
jest: true
|
||||||
"node": true,
|
|
||||||
"jest/globals": true
|
|
||||||
},
|
},
|
||||||
"rules": {
|
parserOptions: {
|
||||||
"indent": [
|
parser: 'babel-eslint'
|
||||||
"error",
|
},
|
||||||
2
|
extends: [
|
||||||
],
|
'standard',
|
||||||
"quotes": [
|
'plugin:prettier/recommended'
|
||||||
"error",
|
],
|
||||||
"single"
|
plugins: [
|
||||||
]
|
'jest'
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
//'indent': [ 'error', 2 ],
|
||||||
|
//'quotes': [ "error", "single"],
|
||||||
|
// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
'no-console': ['error'],
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
'prettier/prettier': ['error'],
|
||||||
},
|
},
|
||||||
"plugins": ["jest"]
|
|
||||||
};
|
};
|
||||||
|
|||||||
9
backend/.prettierrc.js
Normal file
9
backend/.prettierrc.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
module.exports = {
|
||||||
|
semi: false,
|
||||||
|
printWidth: 100,
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: "all",
|
||||||
|
tabWidth: 2,
|
||||||
|
bracketSpacing: true
|
||||||
|
};
|
||||||
@ -44,7 +44,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"activitystrea.ms": "~2.1.3",
|
"activitystrea.ms": "~2.1.3",
|
||||||
"apollo-cache-inmemory": "~1.5.1",
|
"apollo-cache-inmemory": "~1.6.0",
|
||||||
"apollo-client": "~2.5.1",
|
"apollo-client": "~2.5.1",
|
||||||
"apollo-link-context": "~1.0.14",
|
"apollo-link-context": "~1.0.14",
|
||||||
"apollo-link-http": "~1.5.14",
|
"apollo-link-http": "~1.5.14",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"dotenv": "~8.0.0",
|
"dotenv": "~8.0.0",
|
||||||
"express": "~4.17.0",
|
"express": "~4.17.0",
|
||||||
"faker": "~4.1.0",
|
"faker": "~4.1.0",
|
||||||
"graphql": "~14.3.0",
|
"graphql": "~14.3.1",
|
||||||
"graphql-custom-directives": "~0.2.14",
|
"graphql-custom-directives": "~0.2.14",
|
||||||
"graphql-iso-date": "~3.6.1",
|
"graphql-iso-date": "~3.6.1",
|
||||||
"graphql-middleware": "~3.0.2",
|
"graphql-middleware": "~3.0.2",
|
||||||
@ -71,7 +71,7 @@
|
|||||||
"lodash": "~4.17.11",
|
"lodash": "~4.17.11",
|
||||||
"ms": "~2.1.1",
|
"ms": "~2.1.1",
|
||||||
"neo4j-driver": "~1.7.4",
|
"neo4j-driver": "~1.7.4",
|
||||||
"neo4j-graphql-js": "~2.6.0",
|
"neo4j-graphql-js": "~2.6.1",
|
||||||
"node-fetch": "~2.6.0",
|
"node-fetch": "~2.6.0",
|
||||||
"npm-run-all": "~4.1.5",
|
"npm-run-all": "~4.1.5",
|
||||||
"request": "~2.88.0",
|
"request": "~2.88.0",
|
||||||
@ -83,10 +83,10 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "~7.4.4",
|
"@babel/cli": "~7.4.4",
|
||||||
"@babel/core": "~7.4.4",
|
"@babel/core": "~7.4.5",
|
||||||
"@babel/node": "~7.2.2",
|
"@babel/node": "~7.4.5",
|
||||||
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
|
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
|
||||||
"@babel/preset-env": "~7.4.4",
|
"@babel/preset-env": "~7.4.5",
|
||||||
"@babel/register": "~7.4.4",
|
"@babel/register": "~7.4.4",
|
||||||
"apollo-server-testing": "~2.5.0",
|
"apollo-server-testing": "~2.5.0",
|
||||||
"babel-core": "~7.0.0-0",
|
"babel-core": "~7.0.0-0",
|
||||||
@ -96,14 +96,17 @@
|
|||||||
"cucumber": "~5.1.0",
|
"cucumber": "~5.1.0",
|
||||||
"eslint": "~5.16.0",
|
"eslint": "~5.16.0",
|
||||||
"eslint-config-standard": "~12.0.0",
|
"eslint-config-standard": "~12.0.0",
|
||||||
|
"eslint-config-prettier": "~4.3.0",
|
||||||
"eslint-plugin-import": "~2.17.2",
|
"eslint-plugin-import": "~2.17.2",
|
||||||
"eslint-plugin-jest": "~22.5.1",
|
"eslint-plugin-jest": "~22.6.4",
|
||||||
"eslint-plugin-node": "~9.0.1",
|
"eslint-plugin-node": "~9.1.0",
|
||||||
|
"eslint-plugin-prettier": "~3.1.0",
|
||||||
"eslint-plugin-promise": "~4.1.1",
|
"eslint-plugin-promise": "~4.1.1",
|
||||||
"eslint-plugin-standard": "~4.0.0",
|
"eslint-plugin-standard": "~4.0.0",
|
||||||
"graphql-request": "~1.8.2",
|
"graphql-request": "~1.8.2",
|
||||||
"jest": "~24.8.0",
|
"jest": "~24.8.0",
|
||||||
"nodemon": "~1.19.0",
|
"nodemon": "~1.19.0",
|
||||||
|
"prettier": "~1.14.3",
|
||||||
"supertest": "~4.0.2"
|
"supertest": "~4.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,13 +1,5 @@
|
|||||||
import {
|
import { extractNameFromId, extractDomainFromUrl, signAndSend } from './utils'
|
||||||
extractNameFromId,
|
import { isPublicAddressed, sendAcceptActivity, sendRejectActivity } from './utils/activity'
|
||||||
extractDomainFromUrl,
|
|
||||||
signAndSend
|
|
||||||
} from './utils'
|
|
||||||
import {
|
|
||||||
isPublicAddressed,
|
|
||||||
sendAcceptActivity,
|
|
||||||
sendRejectActivity
|
|
||||||
} from './utils/activity'
|
|
||||||
import request from 'request'
|
import request from 'request'
|
||||||
import as from 'activitystrea.ms'
|
import as from 'activitystrea.ms'
|
||||||
import NitroDataSource from './NitroDataSource'
|
import NitroDataSource from './NitroDataSource'
|
||||||
@ -22,182 +14,203 @@ let activityPub = null
|
|||||||
export { activityPub }
|
export { activityPub }
|
||||||
|
|
||||||
export default class ActivityPub {
|
export default class ActivityPub {
|
||||||
constructor (activityPubEndpointUri, internalGraphQlUri) {
|
constructor(activityPubEndpointUri, internalGraphQlUri) {
|
||||||
this.endpoint = activityPubEndpointUri
|
this.endpoint = activityPubEndpointUri
|
||||||
this.dataSource = new NitroDataSource(internalGraphQlUri)
|
this.dataSource = new NitroDataSource(internalGraphQlUri)
|
||||||
this.collections = new Collections(this.dataSource)
|
this.collections = new Collections(this.dataSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
static init (server) {
|
static init(server) {
|
||||||
if (!activityPub) {
|
if (!activityPub) {
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
activityPub = new ActivityPub(process.env.CLIENT_URI || 'http://localhost:3000', process.env.GRAPHQL_URI || 'http://localhost:4000')
|
activityPub = new ActivityPub(
|
||||||
|
process.env.CLIENT_URI || 'http://localhost:3000',
|
||||||
|
process.env.GRAPHQL_URI || 'http://localhost:4000',
|
||||||
|
)
|
||||||
|
|
||||||
// integrate into running graphql express server
|
// integrate into running graphql express server
|
||||||
server.express.set('ap', activityPub)
|
server.express.set('ap', activityPub)
|
||||||
server.express.use(router)
|
server.express.use(router)
|
||||||
console.log('-> ActivityPub middleware added to the graphql express server')
|
console.log('-> ActivityPub middleware added to the graphql express server') // eslint-disable-line no-console
|
||||||
} else {
|
} else {
|
||||||
console.log('-> ActivityPub middleware already added to the graphql express server')
|
console.log('-> ActivityPub middleware already added to the graphql express server') // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFollowActivity (activity) {
|
handleFollowActivity(activity) {
|
||||||
debug(`inside FOLLOW ${activity.actor}`)
|
debug(`inside FOLLOW ${activity.actor}`)
|
||||||
let toActorName = extractNameFromId(activity.object)
|
let toActorName = extractNameFromId(activity.object)
|
||||||
let fromDomain = extractDomainFromUrl(activity.actor)
|
let fromDomain = extractDomainFromUrl(activity.actor)
|
||||||
const dataSource = this.dataSource
|
const dataSource = this.dataSource
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
request({
|
request(
|
||||||
url: activity.actor,
|
{
|
||||||
headers: {
|
url: activity.actor,
|
||||||
'Accept': 'application/activity+json'
|
headers: {
|
||||||
}
|
Accept: 'application/activity+json',
|
||||||
}, async (err, response, toActorObject) => {
|
},
|
||||||
if (err) return reject(err)
|
},
|
||||||
// save shared inbox
|
async (err, response, toActorObject) => {
|
||||||
toActorObject = JSON.parse(toActorObject)
|
if (err) return reject(err)
|
||||||
await this.dataSource.addSharedInboxEndpoint(toActorObject.endpoints.sharedInbox)
|
// save shared inbox
|
||||||
|
toActorObject = JSON.parse(toActorObject)
|
||||||
|
await this.dataSource.addSharedInboxEndpoint(toActorObject.endpoints.sharedInbox)
|
||||||
|
|
||||||
let followersCollectionPage = await this.dataSource.getFollowersCollectionPage(activity.object)
|
let followersCollectionPage = await this.dataSource.getFollowersCollectionPage(
|
||||||
|
activity.object,
|
||||||
|
)
|
||||||
|
|
||||||
const followActivity = as.follow()
|
const followActivity = as
|
||||||
.id(activity.id)
|
.follow()
|
||||||
.actor(activity.actor)
|
.id(activity.id)
|
||||||
.object(activity.object)
|
.actor(activity.actor)
|
||||||
|
.object(activity.object)
|
||||||
|
|
||||||
// add follower if not already in collection
|
// add follower if not already in collection
|
||||||
if (followersCollectionPage.orderedItems.includes(activity.actor)) {
|
if (followersCollectionPage.orderedItems.includes(activity.actor)) {
|
||||||
debug('follower already in collection!')
|
debug('follower already in collection!')
|
||||||
|
debug(`inbox = ${toActorObject.inbox}`)
|
||||||
|
resolve(
|
||||||
|
sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
followersCollectionPage.orderedItems.push(activity.actor)
|
||||||
|
}
|
||||||
|
debug(`toActorObject = ${toActorObject}`)
|
||||||
|
toActorObject =
|
||||||
|
typeof toActorObject !== 'object' ? JSON.parse(toActorObject) : toActorObject
|
||||||
|
debug(`followers = ${JSON.stringify(followersCollectionPage.orderedItems, null, 2)}`)
|
||||||
debug(`inbox = ${toActorObject.inbox}`)
|
debug(`inbox = ${toActorObject.inbox}`)
|
||||||
resolve(sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox))
|
debug(`outbox = ${toActorObject.outbox}`)
|
||||||
} else {
|
debug(`followers = ${toActorObject.followers}`)
|
||||||
followersCollectionPage.orderedItems.push(activity.actor)
|
debug(`following = ${toActorObject.following}`)
|
||||||
}
|
|
||||||
debug(`toActorObject = ${toActorObject}`)
|
|
||||||
toActorObject = typeof toActorObject !== 'object' ? JSON.parse(toActorObject) : toActorObject
|
|
||||||
debug(`followers = ${JSON.stringify(followersCollectionPage.orderedItems, null, 2)}`)
|
|
||||||
debug(`inbox = ${toActorObject.inbox}`)
|
|
||||||
debug(`outbox = ${toActorObject.outbox}`)
|
|
||||||
debug(`followers = ${toActorObject.followers}`)
|
|
||||||
debug(`following = ${toActorObject.following}`)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await dataSource.saveFollowersCollectionPage(followersCollectionPage)
|
await dataSource.saveFollowersCollectionPage(followersCollectionPage)
|
||||||
debug('follow activity saved')
|
debug('follow activity saved')
|
||||||
resolve(sendAcceptActivity(followActivity, toActorName, fromDomain, toActorObject.inbox))
|
resolve(
|
||||||
} catch (e) {
|
sendAcceptActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
|
||||||
debug('followers update error!', e)
|
)
|
||||||
resolve(sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox))
|
} catch (e) {
|
||||||
}
|
debug('followers update error!', e)
|
||||||
})
|
resolve(
|
||||||
|
sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUndoActivity (activity) {
|
handleUndoActivity(activity) {
|
||||||
debug('inside UNDO')
|
debug('inside UNDO')
|
||||||
switch (activity.object.type) {
|
switch (activity.object.type) {
|
||||||
case 'Follow':
|
case 'Follow':
|
||||||
const followActivity = activity.object
|
const followActivity = activity.object
|
||||||
return this.dataSource.undoFollowActivity(followActivity.actor, followActivity.object)
|
return this.dataSource.undoFollowActivity(followActivity.actor, followActivity.object)
|
||||||
case 'Like':
|
case 'Like':
|
||||||
return this.dataSource.deleteShouted(activity)
|
return this.dataSource.deleteShouted(activity)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCreateActivity (activity) {
|
handleCreateActivity(activity) {
|
||||||
debug('inside create')
|
debug('inside create')
|
||||||
switch (activity.object.type) {
|
switch (activity.object.type) {
|
||||||
case 'Article':
|
case 'Article':
|
||||||
case 'Note':
|
case 'Note':
|
||||||
const articleObject = activity.object
|
const articleObject = activity.object
|
||||||
if (articleObject.inReplyTo) {
|
if (articleObject.inReplyTo) {
|
||||||
return this.dataSource.createComment(activity)
|
return this.dataSource.createComment(activity)
|
||||||
} else {
|
} else {
|
||||||
return this.dataSource.createPost(activity)
|
return this.dataSource.createPost(activity)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteActivity (activity) {
|
handleDeleteActivity(activity) {
|
||||||
debug('inside delete')
|
debug('inside delete')
|
||||||
switch (activity.object.type) {
|
switch (activity.object.type) {
|
||||||
case 'Article':
|
case 'Article':
|
||||||
case 'Note':
|
case 'Note':
|
||||||
return this.dataSource.deletePost(activity)
|
return this.dataSource.deletePost(activity)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdateActivity (activity) {
|
handleUpdateActivity(activity) {
|
||||||
debug('inside update')
|
debug('inside update')
|
||||||
switch (activity.object.type) {
|
switch (activity.object.type) {
|
||||||
case 'Note':
|
case 'Note':
|
||||||
case 'Article':
|
case 'Article':
|
||||||
return this.dataSource.updatePost(activity)
|
return this.dataSource.updatePost(activity)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLikeActivity (activity) {
|
handleLikeActivity(activity) {
|
||||||
// TODO differ if activity is an Article/Note/etc.
|
// TODO differ if activity is an Article/Note/etc.
|
||||||
return this.dataSource.createShouted(activity)
|
return this.dataSource.createShouted(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDislikeActivity (activity) {
|
handleDislikeActivity(activity) {
|
||||||
// TODO differ if activity is an Article/Note/etc.
|
// TODO differ if activity is an Article/Note/etc.
|
||||||
return this.dataSource.deleteShouted(activity)
|
return this.dataSource.deleteShouted(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleAcceptActivity (activity) {
|
async handleAcceptActivity(activity) {
|
||||||
debug('inside accept')
|
debug('inside accept')
|
||||||
switch (activity.object.type) {
|
switch (activity.object.type) {
|
||||||
case 'Follow':
|
case 'Follow':
|
||||||
const followObject = activity.object
|
const followObject = activity.object
|
||||||
const followingCollectionPage = await this.collections.getFollowingCollectionPage(followObject.actor)
|
const followingCollectionPage = await this.collections.getFollowingCollectionPage(
|
||||||
followingCollectionPage.orderedItems.push(followObject.object)
|
followObject.actor,
|
||||||
await this.dataSource.saveFollowingCollectionPage(followingCollectionPage)
|
)
|
||||||
|
followingCollectionPage.orderedItems.push(followObject.object)
|
||||||
|
await this.dataSource.saveFollowingCollectionPage(followingCollectionPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getActorObject (url) {
|
getActorObject(url) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
request({
|
request(
|
||||||
url: url,
|
{
|
||||||
headers: {
|
url: url,
|
||||||
'Accept': 'application/json'
|
headers: {
|
||||||
}
|
Accept: 'application/json',
|
||||||
}, (err, response, body) => {
|
},
|
||||||
if (err) {
|
},
|
||||||
reject(err)
|
(err, response, body) => {
|
||||||
}
|
if (err) {
|
||||||
resolve(JSON.parse(body))
|
reject(err)
|
||||||
})
|
}
|
||||||
|
resolve(JSON.parse(body))
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
generateStatusId (slug) {
|
generateStatusId(slug) {
|
||||||
return `https://${this.host}/activitypub/users/${slug}/status/${uuid()}`
|
return `https://${this.host}/activitypub/users/${slug}/status/${uuid()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendActivity (activity) {
|
async sendActivity(activity) {
|
||||||
delete activity.send
|
delete activity.send
|
||||||
const fromName = extractNameFromId(activity.actor)
|
const fromName = extractNameFromId(activity.actor)
|
||||||
if (Array.isArray(activity.to) && isPublicAddressed(activity)) {
|
if (Array.isArray(activity.to) && isPublicAddressed(activity)) {
|
||||||
debug('is public addressed')
|
debug('is public addressed')
|
||||||
const sharedInboxEndpoints = await this.dataSource.getSharedInboxEndpoints()
|
const sharedInboxEndpoints = await this.dataSource.getSharedInboxEndpoints()
|
||||||
// serve shared inbox endpoints
|
// serve shared inbox endpoints
|
||||||
sharedInboxEndpoints.map((sharedInbox) => {
|
sharedInboxEndpoints.map(sharedInbox => {
|
||||||
return this.trySend(activity, fromName, new URL(sharedInbox).host, sharedInbox)
|
return this.trySend(activity, fromName, new URL(sharedInbox).host, sharedInbox)
|
||||||
})
|
})
|
||||||
activity.to = activity.to.filter((recipient) => {
|
activity.to = activity.to.filter(recipient => {
|
||||||
return !(isPublicAddressed({ to: recipient }))
|
return !isPublicAddressed({ to: recipient })
|
||||||
})
|
})
|
||||||
// serve the rest
|
// serve the rest
|
||||||
activity.to.map(async (recipient) => {
|
activity.to.map(async recipient => {
|
||||||
debug('serve rest')
|
debug('serve rest')
|
||||||
const actorObject = await this.getActorObject(recipient)
|
const actorObject = await this.getActorObject(recipient)
|
||||||
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
|
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
|
||||||
@ -207,18 +220,18 @@ export default class ActivityPub {
|
|||||||
const actorObject = await this.getActorObject(activity.to)
|
const actorObject = await this.getActorObject(activity.to)
|
||||||
return this.trySend(activity, fromName, new URL(activity.to).host, actorObject.inbox)
|
return this.trySend(activity, fromName, new URL(activity.to).host, actorObject.inbox)
|
||||||
} else if (Array.isArray(activity.to)) {
|
} else if (Array.isArray(activity.to)) {
|
||||||
activity.to.map(async (recipient) => {
|
activity.to.map(async recipient => {
|
||||||
const actorObject = await this.getActorObject(recipient)
|
const actorObject = await this.getActorObject(recipient)
|
||||||
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
|
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async trySend (activity, fromName, host, url, tries = 5) {
|
async trySend(activity, fromName, host, url, tries = 5) {
|
||||||
try {
|
try {
|
||||||
return await signAndSend(activity, fromName, host, url)
|
return await signAndSend(activity, fromName, host, url)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tries > 0) {
|
if (tries > 0) {
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
return this.trySend(activity, fromName, host, url, --tries)
|
return this.trySend(activity, fromName, host, url, --tries)
|
||||||
}, 20000)
|
}, 20000)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,28 +1,28 @@
|
|||||||
export default class Collections {
|
export default class Collections {
|
||||||
constructor (dataSource) {
|
constructor(dataSource) {
|
||||||
this.dataSource = dataSource
|
this.dataSource = dataSource
|
||||||
}
|
}
|
||||||
getFollowersCollection (actorId) {
|
getFollowersCollection(actorId) {
|
||||||
return this.dataSource.getFollowersCollection(actorId)
|
return this.dataSource.getFollowersCollection(actorId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getFollowersCollectionPage (actorId) {
|
getFollowersCollectionPage(actorId) {
|
||||||
return this.dataSource.getFollowersCollectionPage(actorId)
|
return this.dataSource.getFollowersCollectionPage(actorId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getFollowingCollection (actorId) {
|
getFollowingCollection(actorId) {
|
||||||
return this.dataSource.getFollowingCollection(actorId)
|
return this.dataSource.getFollowingCollection(actorId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getFollowingCollectionPage (actorId) {
|
getFollowingCollectionPage(actorId) {
|
||||||
return this.dataSource.getFollowingCollectionPage(actorId)
|
return this.dataSource.getFollowingCollectionPage(actorId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getOutboxCollection (actorId) {
|
getOutboxCollection(actorId) {
|
||||||
return this.dataSource.getOutboxCollection(actorId)
|
return this.dataSource.getOutboxCollection(actorId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getOutboxCollectionPage (actorId) {
|
getOutboxCollectionPage(actorId) {
|
||||||
return this.dataSource.getOutboxCollectionPage(actorId)
|
return this.dataSource.getOutboxCollectionPage(actorId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,16 +2,10 @@ import {
|
|||||||
throwErrorIfApolloErrorOccurred,
|
throwErrorIfApolloErrorOccurred,
|
||||||
extractIdFromActivityId,
|
extractIdFromActivityId,
|
||||||
extractNameFromId,
|
extractNameFromId,
|
||||||
constructIdFromName
|
constructIdFromName,
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import {
|
import { createOrderedCollection, createOrderedCollectionPage } from './utils/collection'
|
||||||
createOrderedCollection,
|
import { createArticleObject, isPublicAddressed } from './utils/activity'
|
||||||
createOrderedCollectionPage
|
|
||||||
} from './utils/collection'
|
|
||||||
import {
|
|
||||||
createArticleObject,
|
|
||||||
isPublicAddressed
|
|
||||||
} from './utils/activity'
|
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { createHttpLink } from 'apollo-link-http'
|
import { createHttpLink } from 'apollo-link-http'
|
||||||
@ -23,35 +17,36 @@ import trunc from 'trunc-html'
|
|||||||
const debug = require('debug')('ea:nitro-datasource')
|
const debug = require('debug')('ea:nitro-datasource')
|
||||||
|
|
||||||
export default class NitroDataSource {
|
export default class NitroDataSource {
|
||||||
constructor (uri) {
|
constructor(uri) {
|
||||||
this.uri = uri
|
this.uri = uri
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
query: {
|
query: {
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
errorPolicy: 'all'
|
errorPolicy: 'all',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
const link = createHttpLink({ uri: this.uri, fetch: fetch }) // eslint-disable-line
|
const link = createHttpLink({ uri: this.uri, fetch: fetch }) // eslint-disable-line
|
||||||
const cache = new InMemoryCache()
|
const cache = new InMemoryCache()
|
||||||
const authLink = setContext((_, { headers }) => {
|
const authLink = setContext((_, { headers }) => {
|
||||||
// generate the authentication token (maybe from env? Which user?)
|
// generate the authentication token (maybe from env? Which user?)
|
||||||
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiUGV0ZXIgTHVzdGlnIiwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9qb2huY2FmYXp6YS8xMjguanBnIiwiaWQiOiJ1MSIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5vcmciLCJzbHVnIjoicGV0ZXItbHVzdGlnIiwiaWF0IjoxNTUyNDIwMTExLCJleHAiOjE2Mzg4MjAxMTEsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDAwMCIsInN1YiI6InUxIn0.G7An1yeQUViJs-0Qj-Tc-zm0WrLCMB3M02pfPnm6xzw'
|
const token =
|
||||||
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiUGV0ZXIgTHVzdGlnIiwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9qb2huY2FmYXp6YS8xMjguanBnIiwiaWQiOiJ1MSIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5vcmciLCJzbHVnIjoicGV0ZXItbHVzdGlnIiwiaWF0IjoxNTUyNDIwMTExLCJleHAiOjE2Mzg4MjAxMTEsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDAwMCIsInN1YiI6InUxIn0.G7An1yeQUViJs-0Qj-Tc-zm0WrLCMB3M02pfPnm6xzw'
|
||||||
// return the headers to the context so httpLink can read them
|
// return the headers to the context so httpLink can read them
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
...headers,
|
...headers,
|
||||||
Authorization: token ? `Bearer ${token}` : ''
|
Authorization: token ? `Bearer ${token}` : '',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.client = new ApolloClient({
|
this.client = new ApolloClient({
|
||||||
link: authLink.concat(link),
|
link: authLink.concat(link),
|
||||||
cache: cache,
|
cache: cache,
|
||||||
defaultOptions
|
defaultOptions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFollowersCollection (actorId) {
|
async getFollowersCollection(actorId) {
|
||||||
const slug = extractNameFromId(actorId)
|
const slug = extractNameFromId(actorId)
|
||||||
debug(`slug= ${slug}`)
|
debug(`slug= ${slug}`)
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
@ -61,7 +56,7 @@ export default class NitroDataSource {
|
|||||||
followedByCount
|
followedByCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
debug('successfully fetched followers')
|
debug('successfully fetched followers')
|
||||||
debug(result.data)
|
debug(result.data)
|
||||||
@ -78,7 +73,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFollowersCollectionPage (actorId) {
|
async getFollowersCollectionPage(actorId) {
|
||||||
const slug = extractNameFromId(actorId)
|
const slug = extractNameFromId(actorId)
|
||||||
debug(`getFollowersPage slug = ${slug}`)
|
debug(`getFollowersPage slug = ${slug}`)
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
@ -91,7 +86,7 @@ export default class NitroDataSource {
|
|||||||
followedByCount
|
followedByCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
debug(result.data)
|
debug(result.data)
|
||||||
@ -104,9 +99,9 @@ export default class NitroDataSource {
|
|||||||
followersCollection.totalItems = followersCount
|
followersCollection.totalItems = followersCount
|
||||||
debug(`followers = ${JSON.stringify(followers, null, 2)}`)
|
debug(`followers = ${JSON.stringify(followers, null, 2)}`)
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
followers.map(async (follower) => {
|
followers.map(async follower => {
|
||||||
followersCollection.orderedItems.push(constructIdFromName(follower.slug))
|
followersCollection.orderedItems.push(constructIdFromName(follower.slug))
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
return followersCollection
|
return followersCollection
|
||||||
@ -115,7 +110,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFollowingCollection (actorId) {
|
async getFollowingCollection(actorId) {
|
||||||
const slug = extractNameFromId(actorId)
|
const slug = extractNameFromId(actorId)
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
query: gql`
|
query: gql`
|
||||||
@ -124,7 +119,7 @@ export default class NitroDataSource {
|
|||||||
followingCount
|
followingCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
debug(result.data)
|
debug(result.data)
|
||||||
@ -141,7 +136,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFollowingCollectionPage (actorId) {
|
async getFollowingCollectionPage(actorId) {
|
||||||
const slug = extractNameFromId(actorId)
|
const slug = extractNameFromId(actorId)
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
query: gql`
|
query: gql`
|
||||||
@ -153,7 +148,7 @@ export default class NitroDataSource {
|
|||||||
followingCount
|
followingCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
debug(result.data)
|
debug(result.data)
|
||||||
@ -166,9 +161,9 @@ export default class NitroDataSource {
|
|||||||
followingCollection.totalItems = followingCount
|
followingCollection.totalItems = followingCount
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
following.map(async (user) => {
|
following.map(async user => {
|
||||||
followingCollection.orderedItems.push(await constructIdFromName(user.slug))
|
followingCollection.orderedItems.push(await constructIdFromName(user.slug))
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
return followingCollection
|
return followingCollection
|
||||||
@ -177,7 +172,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOutboxCollection (actorId) {
|
async getOutboxCollection(actorId) {
|
||||||
const slug = extractNameFromId(actorId)
|
const slug = extractNameFromId(actorId)
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
query: gql`
|
query: gql`
|
||||||
@ -192,7 +187,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
debug(result.data)
|
debug(result.data)
|
||||||
@ -209,7 +204,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOutboxCollectionPage (actorId) {
|
async getOutboxCollectionPage(actorId) {
|
||||||
const slug = extractNameFromId(actorId)
|
const slug = extractNameFromId(actorId)
|
||||||
debug(`inside getting outbox collection page => ${slug}`)
|
debug(`inside getting outbox collection page => ${slug}`)
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
@ -232,7 +227,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
debug(result.data)
|
debug(result.data)
|
||||||
@ -243,9 +238,18 @@ export default class NitroDataSource {
|
|||||||
const outboxCollection = createOrderedCollectionPage(slug, 'outbox')
|
const outboxCollection = createOrderedCollectionPage(slug, 'outbox')
|
||||||
outboxCollection.totalItems = posts.length
|
outboxCollection.totalItems = posts.length
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
posts.map(async (post) => {
|
posts.map(async post => {
|
||||||
outboxCollection.orderedItems.push(await createArticleObject(post.activityId, post.objectId, post.content, post.author.slug, post.id, post.createdAt))
|
outboxCollection.orderedItems.push(
|
||||||
})
|
await createArticleObject(
|
||||||
|
post.activityId,
|
||||||
|
post.objectId,
|
||||||
|
post.content,
|
||||||
|
post.author.slug,
|
||||||
|
post.id,
|
||||||
|
post.createdAt,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
debug('after createNote')
|
debug('after createNote')
|
||||||
@ -255,7 +259,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async undoFollowActivity (fromActorId, toActorId) {
|
async undoFollowActivity(fromActorId, toActorId) {
|
||||||
const fromUserId = await this.ensureUser(fromActorId)
|
const fromUserId = await this.ensureUser(fromActorId)
|
||||||
const toUserId = await this.ensureUser(toActorId)
|
const toUserId = await this.ensureUser(toActorId)
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
@ -265,13 +269,13 @@ export default class NitroDataSource {
|
|||||||
from { name }
|
from { name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
debug(`undoFollowActivity result = ${JSON.stringify(result, null, 2)}`)
|
debug(`undoFollowActivity result = ${JSON.stringify(result, null, 2)}`)
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveFollowersCollectionPage (followersCollection, onlyNewestItem = true) {
|
async saveFollowersCollectionPage(followersCollection, onlyNewestItem = true) {
|
||||||
debug('inside saveFollowers')
|
debug('inside saveFollowers')
|
||||||
let orderedItems = followersCollection.orderedItems
|
let orderedItems = followersCollection.orderedItems
|
||||||
const toUserName = extractNameFromId(followersCollection.id)
|
const toUserName = extractNameFromId(followersCollection.id)
|
||||||
@ -279,7 +283,7 @@ export default class NitroDataSource {
|
|||||||
orderedItems = onlyNewestItem ? [orderedItems.pop()] : orderedItems
|
orderedItems = onlyNewestItem ? [orderedItems.pop()] : orderedItems
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
orderedItems.map(async (follower) => {
|
orderedItems.map(async follower => {
|
||||||
debug(`follower = ${follower}`)
|
debug(`follower = ${follower}`)
|
||||||
const fromUserId = await this.ensureUser(follower)
|
const fromUserId = await this.ensureUser(follower)
|
||||||
debug(`fromUserId = ${fromUserId}`)
|
debug(`fromUserId = ${fromUserId}`)
|
||||||
@ -291,22 +295,22 @@ export default class NitroDataSource {
|
|||||||
from { name }
|
from { name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
debug(`addUserFollowedBy edge = ${JSON.stringify(result, null, 2)}`)
|
debug(`addUserFollowedBy edge = ${JSON.stringify(result, null, 2)}`)
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
debug('saveFollowers: added follow edge successfully')
|
debug('saveFollowers: added follow edge successfully')
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
async saveFollowingCollectionPage (followingCollection, onlyNewestItem = true) {
|
async saveFollowingCollectionPage(followingCollection, onlyNewestItem = true) {
|
||||||
debug('inside saveFollowers')
|
debug('inside saveFollowers')
|
||||||
let orderedItems = followingCollection.orderedItems
|
let orderedItems = followingCollection.orderedItems
|
||||||
const fromUserName = extractNameFromId(followingCollection.id)
|
const fromUserName = extractNameFromId(followingCollection.id)
|
||||||
const fromUserId = await this.ensureUser(constructIdFromName(fromUserName))
|
const fromUserId = await this.ensureUser(constructIdFromName(fromUserName))
|
||||||
orderedItems = onlyNewestItem ? [orderedItems.pop()] : orderedItems
|
orderedItems = onlyNewestItem ? [orderedItems.pop()] : orderedItems
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
orderedItems.map(async (following) => {
|
orderedItems.map(async following => {
|
||||||
debug(`follower = ${following}`)
|
debug(`follower = ${following}`)
|
||||||
const toUserId = await this.ensureUser(following)
|
const toUserId = await this.ensureUser(following)
|
||||||
debug(`fromUserId = ${fromUserId}`)
|
debug(`fromUserId = ${fromUserId}`)
|
||||||
@ -318,33 +322,45 @@ export default class NitroDataSource {
|
|||||||
from { name }
|
from { name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
debug(`addUserFollowing edge = ${JSON.stringify(result, null, 2)}`)
|
debug(`addUserFollowing edge = ${JSON.stringify(result, null, 2)}`)
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
debug('saveFollowing: added follow edge successfully')
|
debug('saveFollowing: added follow edge successfully')
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPost (activity) {
|
async createPost(activity) {
|
||||||
// TODO how to handle the to field? Now the post is just created, doesn't matter who is the recipient
|
// TODO how to handle the to field? Now the post is just created, doesn't matter who is the recipient
|
||||||
// createPost
|
// createPost
|
||||||
const postObject = activity.object
|
const postObject = activity.object
|
||||||
if (!isPublicAddressed(postObject)) {
|
if (!isPublicAddressed(postObject)) {
|
||||||
return debug('createPost: not send to public (sending to specific persons is not implemented yet)')
|
return debug(
|
||||||
|
'createPost: not send to public (sending to specific persons is not implemented yet)',
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const title = postObject.summary ? postObject.summary : postObject.content.split(' ').slice(0, 5).join(' ')
|
const title = postObject.summary
|
||||||
|
? postObject.summary
|
||||||
|
: postObject.content
|
||||||
|
.split(' ')
|
||||||
|
.slice(0, 5)
|
||||||
|
.join(' ')
|
||||||
const postId = extractIdFromActivityId(postObject.id)
|
const postId = extractIdFromActivityId(postObject.id)
|
||||||
debug('inside create post')
|
debug('inside create post')
|
||||||
let result = await this.client.mutate({
|
let result = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
CreatePost(content: "${postObject.content}", contentExcerpt: "${trunc(postObject.content, 120)}", title: "${title}", id: "${postId}", objectId: "${postObject.id}", activityId: "${activity.id}") {
|
CreatePost(content: "${postObject.content}", contentExcerpt: "${trunc(
|
||||||
|
postObject.content,
|
||||||
|
120,
|
||||||
|
)}", title: "${title}", id: "${postId}", objectId: "${postObject.id}", activityId: "${
|
||||||
|
activity.id
|
||||||
|
}") {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
@ -362,13 +378,13 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async deletePost (activity) {
|
async deletePost(activity) {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
@ -376,28 +392,30 @@ export default class NitroDataSource {
|
|||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePost (activity) {
|
async updatePost(activity) {
|
||||||
const postObject = activity.object
|
const postObject = activity.object
|
||||||
const postId = extractIdFromActivityId(postObject.id)
|
const postId = extractIdFromActivityId(postObject.id)
|
||||||
const date = postObject.updated ? postObject.updated : new Date().toISOString()
|
const date = postObject.updated ? postObject.updated : new Date().toISOString()
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
UpdatePost(content: "${postObject.content}", contentExcerpt: "${trunc(postObject.content, 120).html}", id: "${postId}", updatedAt: "${date}") {
|
UpdatePost(content: "${postObject.content}", contentExcerpt: "${
|
||||||
|
trunc(postObject.content, 120).html
|
||||||
|
}", id: "${postId}", updatedAt: "${date}") {
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createShouted (activity) {
|
async createShouted(activity) {
|
||||||
const userId = await this.ensureUser(activity.actor)
|
const userId = await this.ensureUser(activity.actor)
|
||||||
const postId = extractIdFromActivityId(activity.object)
|
const postId = extractIdFromActivityId(activity.object)
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
@ -409,7 +427,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
if (!result.data.AddUserShouted) {
|
if (!result.data.AddUserShouted) {
|
||||||
@ -418,7 +436,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteShouted (activity) {
|
async deleteShouted(activity) {
|
||||||
const userId = await this.ensureUser(activity.actor)
|
const userId = await this.ensureUser(activity.actor)
|
||||||
const postId = extractIdFromActivityId(activity.object)
|
const postId = extractIdFromActivityId(activity.object)
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
@ -430,7 +448,7 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
if (!result.data.AddUserShouted) {
|
if (!result.data.AddUserShouted) {
|
||||||
@ -439,27 +457,27 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSharedInboxEndpoints () {
|
async getSharedInboxEndpoints() {
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
query: gql`
|
query: gql`
|
||||||
query {
|
query {
|
||||||
SharedInboxEndpoint {
|
SharedInboxEndpoint {
|
||||||
uri
|
uri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
return result.data.SharedInboxEnpoint
|
return result.data.SharedInboxEnpoint
|
||||||
}
|
}
|
||||||
async addSharedInboxEndpoint (uri) {
|
async addSharedInboxEndpoint(uri) {
|
||||||
try {
|
try {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
CreateSharedInboxEndpoint(uri: "${uri}")
|
CreateSharedInboxEndpoint(uri: "${uri}")
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
return true
|
return true
|
||||||
@ -468,16 +486,18 @@ export default class NitroDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createComment (activity) {
|
async createComment(activity) {
|
||||||
const postObject = activity.object
|
const postObject = activity.object
|
||||||
let result = await this.client.mutate({
|
let result = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
CreateComment(content: "${postObject.content}", activityId: "${extractIdFromActivityId(activity.id)}") {
|
CreateComment(content: "${
|
||||||
|
postObject.content
|
||||||
|
}", activityId: "${extractIdFromActivityId(activity.id)}") {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
|
|
||||||
@ -485,11 +505,13 @@ export default class NitroDataSource {
|
|||||||
const result2 = await this.client.mutate({
|
const result2 = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
AddCommentAuthor(from: {id: "${result.data.CreateComment.id}"}, to: {id: "${toUserId}"}) {
|
AddCommentAuthor(from: {id: "${
|
||||||
|
result.data.CreateComment.id
|
||||||
|
}"}, to: {id: "${toUserId}"}) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result2)
|
throwErrorIfApolloErrorOccurred(result2)
|
||||||
|
|
||||||
@ -497,11 +519,13 @@ export default class NitroDataSource {
|
|||||||
result = await this.client.mutate({
|
result = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
AddCommentPost(from: { id: "${result.data.CreateComment.id}", to: { id: "${postId}" }}) {
|
AddCommentPost(from: { id: "${
|
||||||
|
result.data.CreateComment.id
|
||||||
|
}", to: { id: "${postId}" }}) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
@ -513,7 +537,7 @@ export default class NitroDataSource {
|
|||||||
* @param actorId
|
* @param actorId
|
||||||
* @returns {Promise<*>}
|
* @returns {Promise<*>}
|
||||||
*/
|
*/
|
||||||
async ensureUser (actorId) {
|
async ensureUser(actorId) {
|
||||||
debug(`inside ensureUser = ${actorId}`)
|
debug(`inside ensureUser = ${actorId}`)
|
||||||
const name = extractNameFromId(actorId)
|
const name = extractNameFromId(actorId)
|
||||||
const queryResult = await this.client.query({
|
const queryResult = await this.client.query({
|
||||||
@ -523,10 +547,14 @@ export default class NitroDataSource {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (queryResult.data && Array.isArray(queryResult.data.User) && queryResult.data.User.length > 0) {
|
if (
|
||||||
|
queryResult.data &&
|
||||||
|
Array.isArray(queryResult.data.User) &&
|
||||||
|
queryResult.data.User.length > 0
|
||||||
|
) {
|
||||||
debug('ensureUser: user exists.. return id')
|
debug('ensureUser: user exists.. return id')
|
||||||
// user already exists.. return the id
|
// user already exists.. return the id
|
||||||
return queryResult.data.User[0].id
|
return queryResult.data.User[0].id
|
||||||
@ -534,7 +562,10 @@ export default class NitroDataSource {
|
|||||||
debug('ensureUser: user not exists.. createUser')
|
debug('ensureUser: user not exists.. createUser')
|
||||||
// user does not exist.. create it
|
// user does not exist.. create it
|
||||||
const pw = crypto.randomBytes(16).toString('hex')
|
const pw = crypto.randomBytes(16).toString('hex')
|
||||||
const slug = name.toLowerCase().split(' ').join('-')
|
const slug = name
|
||||||
|
.toLowerCase()
|
||||||
|
.split(' ')
|
||||||
|
.join('-')
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation {
|
mutation {
|
||||||
@ -542,7 +573,7 @@ export default class NitroDataSource {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
|
|
||||||
|
|||||||
@ -7,24 +7,24 @@ const router = express.Router()
|
|||||||
|
|
||||||
// Shared Inbox endpoint (federated Server)
|
// Shared Inbox endpoint (federated Server)
|
||||||
// For now its only able to handle Note Activities!!
|
// For now its only able to handle Note Activities!!
|
||||||
router.post('/', async function (req, res, next) {
|
router.post('/', async function(req, res, next) {
|
||||||
debug(`Content-Type = ${req.get('Content-Type')}`)
|
debug(`Content-Type = ${req.get('Content-Type')}`)
|
||||||
debug(`body = ${JSON.stringify(req.body, null, 2)}`)
|
debug(`body = ${JSON.stringify(req.body, null, 2)}`)
|
||||||
debug(`Request headers = ${JSON.stringify(req.headers, null, 2)}`)
|
debug(`Request headers = ${JSON.stringify(req.headers, null, 2)}`)
|
||||||
switch (req.body.type) {
|
switch (req.body.type) {
|
||||||
case 'Create':
|
case 'Create':
|
||||||
await activityPub.handleCreateActivity(req.body).catch(next)
|
await activityPub.handleCreateActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
case 'Undo':
|
case 'Undo':
|
||||||
await activityPub.handleUndoActivity(req.body).catch(next)
|
await activityPub.handleUndoActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
case 'Follow':
|
case 'Follow':
|
||||||
await activityPub.handleFollowActivity(req.body).catch(next)
|
await activityPub.handleFollowActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
await activityPub.handleDeleteActivity(req.body).catch(next)
|
await activityPub.handleDeleteActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
case 'Update':
|
case 'Update':
|
||||||
await activityPub.handleUpdateActivity(req.body).catch(next)
|
await activityPub.handleUpdateActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
|
|||||||
@ -7,23 +7,21 @@ import verify from './verify'
|
|||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
router.use('/.well-known/webFinger',
|
router.use('/.well-known/webFinger', cors(), express.urlencoded({ extended: true }), webFinger)
|
||||||
cors(),
|
router.use(
|
||||||
express.urlencoded({ extended: true }),
|
'/activitypub/users',
|
||||||
webFinger
|
|
||||||
)
|
|
||||||
router.use('/activitypub/users',
|
|
||||||
cors(),
|
cors(),
|
||||||
express.json({ type: ['application/activity+json', 'application/ld+json', 'application/json'] }),
|
express.json({ type: ['application/activity+json', 'application/ld+json', 'application/json'] }),
|
||||||
express.urlencoded({ extended: true }),
|
express.urlencoded({ extended: true }),
|
||||||
user
|
user,
|
||||||
)
|
)
|
||||||
router.use('/activitypub/inbox',
|
router.use(
|
||||||
|
'/activitypub/inbox',
|
||||||
cors(),
|
cors(),
|
||||||
express.json({ type: ['application/activity+json', 'application/ld+json', 'application/json'] }),
|
express.json({ type: ['application/activity+json', 'application/ld+json', 'application/json'] }),
|
||||||
express.urlencoded({ extended: true }),
|
express.urlencoded({ extended: true }),
|
||||||
verify,
|
verify,
|
||||||
inbox
|
inbox,
|
||||||
)
|
)
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { createActor } from '../utils/actor'
|
|||||||
const gql = require('graphql-tag')
|
const gql = require('graphql-tag')
|
||||||
const debug = require('debug')('ea:serveUser')
|
const debug = require('debug')('ea:serveUser')
|
||||||
|
|
||||||
export async function serveUser (req, res, next) {
|
export async function serveUser(req, res, next) {
|
||||||
let name = req.params.name
|
let name = req.params.name
|
||||||
|
|
||||||
if (name.startsWith('@')) {
|
if (name.startsWith('@')) {
|
||||||
@ -10,21 +10,32 @@ export async function serveUser (req, res, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug(`name = ${name}`)
|
debug(`name = ${name}`)
|
||||||
const result = await req.app.get('ap').dataSource.client.query({
|
const result = await req.app
|
||||||
query: gql`
|
.get('ap')
|
||||||
|
.dataSource.client.query({
|
||||||
|
query: gql`
|
||||||
query {
|
query {
|
||||||
User(slug: "${name}") {
|
User(slug: "${name}") {
|
||||||
publicKey
|
publicKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
}).catch(reason => { debug(`serveUser User fetch error: ${reason}`) })
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
debug(`serveUser User fetch error: ${reason}`)
|
||||||
|
})
|
||||||
|
|
||||||
if (result.data && Array.isArray(result.data.User) && result.data.User.length > 0) {
|
if (result.data && Array.isArray(result.data.User) && result.data.User.length > 0) {
|
||||||
const publicKey = result.data.User[0].publicKey
|
const publicKey = result.data.User[0].publicKey
|
||||||
const actor = createActor(name, publicKey)
|
const actor = createActor(name, publicKey)
|
||||||
debug(`actor = ${JSON.stringify(actor, null, 2)}`)
|
debug(`actor = ${JSON.stringify(actor, null, 2)}`)
|
||||||
debug(`accepts json = ${req.accepts(['application/activity+json', 'application/ld+json', 'application/json'])}`)
|
debug(
|
||||||
|
`accepts json = ${req.accepts([
|
||||||
|
'application/activity+json',
|
||||||
|
'application/ld+json',
|
||||||
|
'application/json',
|
||||||
|
])}`,
|
||||||
|
)
|
||||||
if (req.accepts(['application/activity+json', 'application/ld+json', 'application/json'])) {
|
if (req.accepts(['application/activity+json', 'application/ld+json', 'application/json'])) {
|
||||||
return res.json(actor)
|
return res.json(actor)
|
||||||
} else if (req.accepts('text/html')) {
|
} else if (req.accepts('text/html')) {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import verify from './verify'
|
|||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const debug = require('debug')('ea:user')
|
const debug = require('debug')('ea:user')
|
||||||
|
|
||||||
router.get('/:name', async function (req, res, next) {
|
router.get('/:name', async function(req, res, next) {
|
||||||
debug('inside user.js -> serveUser')
|
debug('inside user.js -> serveUser')
|
||||||
await serveUser(req, res, next)
|
await serveUser(req, res, next)
|
||||||
})
|
})
|
||||||
@ -45,24 +45,24 @@ router.get('/:name/outbox', (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/:name/inbox', verify, async function (req, res, next) {
|
router.post('/:name/inbox', verify, async function(req, res, next) {
|
||||||
debug(`body = ${JSON.stringify(req.body, null, 2)}`)
|
debug(`body = ${JSON.stringify(req.body, null, 2)}`)
|
||||||
debug(`actorId = ${req.body.actor}`)
|
debug(`actorId = ${req.body.actor}`)
|
||||||
// const result = await saveActorId(req.body.actor)
|
// const result = await saveActorId(req.body.actor)
|
||||||
switch (req.body.type) {
|
switch (req.body.type) {
|
||||||
case 'Create':
|
case 'Create':
|
||||||
await activityPub.handleCreateActivity(req.body).catch(next)
|
await activityPub.handleCreateActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
case 'Undo':
|
case 'Undo':
|
||||||
await activityPub.handleUndoActivity(req.body).catch(next)
|
await activityPub.handleUndoActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
case 'Follow':
|
case 'Follow':
|
||||||
await activityPub.handleFollowActivity(req.body).catch(next)
|
await activityPub.handleFollowActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
await activityPub.handleDeleteActivity(req.body).catch(next)
|
await activityPub.handleDeleteActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
case 'Update':
|
case 'Update':
|
||||||
await activityPub.handleUpdateActivity(req.body).catch(next)
|
await activityPub.handleUpdateActivity(req.body).catch(next)
|
||||||
break
|
break
|
||||||
|
|||||||
@ -4,7 +4,12 @@ const debug = require('debug')('ea:verify')
|
|||||||
export default async (req, res, next) => {
|
export default async (req, res, next) => {
|
||||||
debug(`actorId = ${req.body.actor}`)
|
debug(`actorId = ${req.body.actor}`)
|
||||||
// TODO stop if signature validation fails
|
// TODO stop if signature validation fails
|
||||||
if (await verifySignature(`${req.protocol}://${req.hostname}:${req.app.get('port')}${req.originalUrl}`, req.headers)) {
|
if (
|
||||||
|
await verifySignature(
|
||||||
|
`${req.protocol}://${req.hostname}:${req.app.get('port')}${req.originalUrl}`,
|
||||||
|
req.headers,
|
||||||
|
)
|
||||||
|
) {
|
||||||
debug('verify = true')
|
debug('verify = true')
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -4,10 +4,14 @@ import gql from 'graphql-tag'
|
|||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
router.get('/', async function (req, res) {
|
router.get('/', async function(req, res) {
|
||||||
const resource = req.query.resource
|
const resource = req.query.resource
|
||||||
if (!resource || !resource.includes('acct:')) {
|
if (!resource || !resource.includes('acct:')) {
|
||||||
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.')
|
return res
|
||||||
|
.status(400)
|
||||||
|
.send(
|
||||||
|
'Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.',
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
const nameAndDomain = resource.replace('acct:', '')
|
const nameAndDomain = resource.replace('acct:', '')
|
||||||
const name = nameAndDomain.split('@')[0]
|
const name = nameAndDomain.split('@')[0]
|
||||||
@ -21,7 +25,7 @@ router.get('/', async function (req, res) {
|
|||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(500).json({ error })
|
return res.status(500).json({ error })
|
||||||
|
|||||||
@ -14,9 +14,9 @@ describe('activityPub/security', () => {
|
|||||||
privateKey = pair.privateKey
|
privateKey = pair.privateKey
|
||||||
publicKey = pair.publicKey
|
publicKey = pair.publicKey
|
||||||
headers = {
|
headers = {
|
||||||
'Date': '2019-03-08T14:35:45.759Z',
|
Date: '2019-03-08T14:35:45.759Z',
|
||||||
'Host': 'democracy-app.de',
|
Host: 'democracy-app.de',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -27,13 +27,23 @@ describe('activityPub/security', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const signer = crypto.createSign('rsa-sha256')
|
const signer = crypto.createSign('rsa-sha256')
|
||||||
signer.update('(request-target): post /activitypub/users/max/inbox\ndate: 2019-03-08T14:35:45.759Z\nhost: democracy-app.de\ncontent-type: application/json')
|
signer.update(
|
||||||
|
'(request-target): post /activitypub/users/max/inbox\ndate: 2019-03-08T14:35:45.759Z\nhost: democracy-app.de\ncontent-type: application/json',
|
||||||
|
)
|
||||||
signatureB64 = signer.sign({ key: privateKey, passphrase }, 'base64')
|
signatureB64 = signer.sign({ key: privateKey, passphrase }, 'base64')
|
||||||
httpSignature = createSignature({ privateKey, keyId: 'https://human-connection.org/activitypub/users/lea#main-key', url: 'https://democracy-app.de/activitypub/users/max/inbox', headers, passphrase })
|
httpSignature = createSignature({
|
||||||
|
privateKey,
|
||||||
|
keyId: 'https://human-connection.org/activitypub/users/lea#main-key',
|
||||||
|
url: 'https://democracy-app.de/activitypub/users/max/inbox',
|
||||||
|
headers,
|
||||||
|
passphrase,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('contains keyId', () => {
|
it('contains keyId', () => {
|
||||||
expect(httpSignature).toContain('keyId="https://human-connection.org/activitypub/users/lea#main-key"')
|
expect(httpSignature).toContain(
|
||||||
|
'keyId="https://human-connection.org/activitypub/users/lea#main-key"',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('contains default algorithm "rsa-sha256"', () => {
|
it('contains default algorithm "rsa-sha256"', () => {
|
||||||
@ -54,13 +64,19 @@ describe('activityPub/security', () => {
|
|||||||
let httpSignature
|
let httpSignature
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
httpSignature = createSignature({ privateKey, keyId: 'http://localhost:4001/activitypub/users/test-user#main-key', url: 'https://democracy-app.de/activitypub/users/max/inbox', headers, passphrase })
|
httpSignature = createSignature({
|
||||||
|
privateKey,
|
||||||
|
keyId: 'http://localhost:4001/activitypub/users/test-user#main-key',
|
||||||
|
url: 'https://democracy-app.de/activitypub/users/max/inbox',
|
||||||
|
headers,
|
||||||
|
passphrase,
|
||||||
|
})
|
||||||
const body = {
|
const body = {
|
||||||
'publicKey': {
|
publicKey: {
|
||||||
'id': 'https://localhost:4001/activitypub/users/test-user#main-key',
|
id: 'https://localhost:4001/activitypub/users/test-user#main-key',
|
||||||
'owner': 'https://localhost:4001/activitypub/users/test-user',
|
owner: 'https://localhost:4001/activitypub/users/test-user',
|
||||||
'publicKeyPem': publicKey
|
publicKeyPem: publicKey,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockedRequest = jest.fn((_, callback) => callback(null, null, JSON.stringify(body)))
|
const mockedRequest = jest.fn((_, callback) => callback(null, null, JSON.stringify(body)))
|
||||||
@ -68,7 +84,9 @@ describe('activityPub/security', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('resolves false', async () => {
|
it('resolves false', async () => {
|
||||||
await expect(verifySignature('https://democracy-app.de/activitypub/users/max/inbox', headers)).resolves.toEqual(false)
|
await expect(
|
||||||
|
verifySignature('https://democracy-app.de/activitypub/users/max/inbox', headers),
|
||||||
|
).resolves.toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('valid signature', () => {
|
describe('valid signature', () => {
|
||||||
@ -77,7 +95,9 @@ describe('activityPub/security', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('resolves true', async () => {
|
it('resolves true', async () => {
|
||||||
await expect(verifySignature('https://democracy-app.de/activitypub/users/max/inbox', headers)).resolves.toEqual(true)
|
await expect(
|
||||||
|
verifySignature('https://democracy-app.de/activitypub/users/max/inbox', headers),
|
||||||
|
).resolves.toEqual(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -6,42 +6,48 @@ const debug = require('debug')('ea:security')
|
|||||||
|
|
||||||
dotenv.config({ path: resolve('src', 'activitypub', '.env') })
|
dotenv.config({ path: resolve('src', 'activitypub', '.env') })
|
||||||
|
|
||||||
export function generateRsaKeyPair (options = {}) {
|
export function generateRsaKeyPair(options = {}) {
|
||||||
const { passphrase = process.env.PRIVATE_KEY_PASSPHRASE } = options
|
const { passphrase = process.env.PRIVATE_KEY_PASSPHRASE } = options
|
||||||
return crypto.generateKeyPairSync('rsa', {
|
return crypto.generateKeyPairSync('rsa', {
|
||||||
modulusLength: 4096,
|
modulusLength: 4096,
|
||||||
publicKeyEncoding: {
|
publicKeyEncoding: {
|
||||||
type: 'spki',
|
type: 'spki',
|
||||||
format: 'pem'
|
format: 'pem',
|
||||||
},
|
},
|
||||||
privateKeyEncoding: {
|
privateKeyEncoding: {
|
||||||
type: 'pkcs8',
|
type: 'pkcs8',
|
||||||
format: 'pem',
|
format: 'pem',
|
||||||
cipher: 'aes-256-cbc',
|
cipher: 'aes-256-cbc',
|
||||||
passphrase
|
passphrase,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// signing
|
// signing
|
||||||
export function createSignature (options) {
|
export function createSignature(options) {
|
||||||
const {
|
const {
|
||||||
privateKey, keyId, url,
|
privateKey,
|
||||||
|
keyId,
|
||||||
|
url,
|
||||||
headers = {},
|
headers = {},
|
||||||
algorithm = 'rsa-sha256',
|
algorithm = 'rsa-sha256',
|
||||||
passphrase = process.env.PRIVATE_KEY_PASSPHRASE
|
passphrase = process.env.PRIVATE_KEY_PASSPHRASE,
|
||||||
} = options
|
} = options
|
||||||
if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) { throw Error(`SIGNING: Unsupported hashing algorithm = ${algorithm}`) }
|
if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) {
|
||||||
|
throw Error(`SIGNING: Unsupported hashing algorithm = ${algorithm}`)
|
||||||
|
}
|
||||||
const signer = crypto.createSign(algorithm)
|
const signer = crypto.createSign(algorithm)
|
||||||
const signingString = constructSigningString(url, headers)
|
const signingString = constructSigningString(url, headers)
|
||||||
signer.update(signingString)
|
signer.update(signingString)
|
||||||
const signatureB64 = signer.sign({ key: privateKey, passphrase }, 'base64')
|
const signatureB64 = signer.sign({ key: privateKey, passphrase }, 'base64')
|
||||||
const headersString = Object.keys(headers).reduce((result, key) => { return result + ' ' + key.toLowerCase() }, '')
|
const headersString = Object.keys(headers).reduce((result, key) => {
|
||||||
|
return result + ' ' + key.toLowerCase()
|
||||||
|
}, '')
|
||||||
return `keyId="${keyId}",algorithm="${algorithm}",headers="(request-target)${headersString}",signature="${signatureB64}"`
|
return `keyId="${keyId}",algorithm="${algorithm}",headers="(request-target)${headersString}",signature="${signatureB64}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifying
|
// verifying
|
||||||
export function verifySignature (url, headers) {
|
export function verifySignature(url, headers) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const signatureHeader = headers['signature'] ? headers['signature'] : headers['Signature']
|
const signatureHeader = headers['signature'] ? headers['signature'] : headers['Signature']
|
||||||
if (!signatureHeader) {
|
if (!signatureHeader) {
|
||||||
@ -61,40 +67,47 @@ export function verifySignature (url, headers) {
|
|||||||
|
|
||||||
const usedHeaders = headersString.split(' ')
|
const usedHeaders = headersString.split(' ')
|
||||||
const verifyHeaders = {}
|
const verifyHeaders = {}
|
||||||
Object.keys(headers).forEach((key) => {
|
Object.keys(headers).forEach(key => {
|
||||||
if (usedHeaders.includes(key.toLowerCase())) {
|
if (usedHeaders.includes(key.toLowerCase())) {
|
||||||
verifyHeaders[key.toLowerCase()] = headers[key]
|
verifyHeaders[key.toLowerCase()] = headers[key]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const signingString = constructSigningString(url, verifyHeaders)
|
const signingString = constructSigningString(url, verifyHeaders)
|
||||||
debug(`keyId= ${keyId}`)
|
debug(`keyId= ${keyId}`)
|
||||||
request({
|
request(
|
||||||
url: keyId,
|
{
|
||||||
headers: {
|
url: keyId,
|
||||||
'Accept': 'application/json'
|
headers: {
|
||||||
}
|
Accept: 'application/json',
|
||||||
}, (err, response, body) => {
|
},
|
||||||
if (err) reject(err)
|
},
|
||||||
debug(`body = ${body}`)
|
(err, response, body) => {
|
||||||
const actor = JSON.parse(body)
|
if (err) reject(err)
|
||||||
const publicKeyPem = actor.publicKey.publicKeyPem
|
debug(`body = ${body}`)
|
||||||
resolve(httpVerify(publicKeyPem, signature, signingString, algorithm))
|
const actor = JSON.parse(body)
|
||||||
})
|
const publicKeyPem = actor.publicKey.publicKeyPem
|
||||||
|
resolve(httpVerify(publicKeyPem, signature, signingString, algorithm))
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// private: signing
|
// private: signing
|
||||||
function constructSigningString (url, headers) {
|
function constructSigningString(url, headers) {
|
||||||
const urlObj = new URL(url)
|
const urlObj = new URL(url)
|
||||||
let signingString = `(request-target): post ${urlObj.pathname}${urlObj.search !== '' ? urlObj.search : ''}`
|
let signingString = `(request-target): post ${urlObj.pathname}${
|
||||||
|
urlObj.search !== '' ? urlObj.search : ''
|
||||||
|
}`
|
||||||
return Object.keys(headers).reduce((result, key) => {
|
return Object.keys(headers).reduce((result, key) => {
|
||||||
return result + `\n${key.toLowerCase()}: ${headers[key]}`
|
return result + `\n${key.toLowerCase()}: ${headers[key]}`
|
||||||
}, signingString)
|
}, signingString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// private: verifying
|
// private: verifying
|
||||||
function httpVerify (pubKey, signature, signingString, algorithm) {
|
function httpVerify(pubKey, signature, signingString, algorithm) {
|
||||||
if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) { throw Error(`SIGNING: Unsupported hashing algorithm = ${algorithm}`) }
|
if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) {
|
||||||
|
throw Error(`SIGNING: Unsupported hashing algorithm = ${algorithm}`)
|
||||||
|
}
|
||||||
const verifier = crypto.createVerify(algorithm)
|
const verifier = crypto.createVerify(algorithm)
|
||||||
verifier.update(signingString)
|
verifier.update(signingString)
|
||||||
return verifier.verify(pubKey, signature, 'base64')
|
return verifier.verify(pubKey, signature, 'base64')
|
||||||
@ -103,14 +116,16 @@ function httpVerify (pubKey, signature, signingString, algorithm) {
|
|||||||
// private: verifying
|
// private: verifying
|
||||||
// This function can be used to extract the signature,headers,algorithm etc. out of the Signature Header.
|
// This function can be used to extract the signature,headers,algorithm etc. out of the Signature Header.
|
||||||
// Just pass what you want as key
|
// Just pass what you want as key
|
||||||
function extractKeyValueFromSignatureHeader (signatureHeader, key) {
|
function extractKeyValueFromSignatureHeader(signatureHeader, key) {
|
||||||
const keyString = signatureHeader.split(',').filter((el) => {
|
const keyString = signatureHeader.split(',').filter(el => {
|
||||||
return !!el.startsWith(key)
|
return !!el.startsWith(key)
|
||||||
})[0]
|
})[0]
|
||||||
|
|
||||||
let firstEqualIndex = keyString.search('=')
|
let firstEqualIndex = keyString.search('=')
|
||||||
// When headers are requested add 17 to the index to remove "(request-target) " from the string
|
// When headers are requested add 17 to the index to remove "(request-target) " from the string
|
||||||
if (key === 'headers') { firstEqualIndex += 17 }
|
if (key === 'headers') {
|
||||||
|
firstEqualIndex += 17
|
||||||
|
}
|
||||||
return keyString.substring(firstEqualIndex + 2, keyString.length - 1)
|
return keyString.substring(firstEqualIndex + 2, keyString.length - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,4 +166,5 @@ export const SUPPORTED_HASH_ALGORITHMS = [
|
|||||||
'sha512WithRSAEncryption',
|
'sha512WithRSAEncryption',
|
||||||
'ssl3-md5',
|
'ssl3-md5',
|
||||||
'ssl3-sha1',
|
'ssl3-sha1',
|
||||||
'whirlpool']
|
'whirlpool',
|
||||||
|
]
|
||||||
|
|||||||
@ -6,45 +6,45 @@ import as from 'activitystrea.ms'
|
|||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
const debug = require('debug')('ea:utils:activity')
|
const debug = require('debug')('ea:utils:activity')
|
||||||
|
|
||||||
export function createNoteObject (text, name, id, published) {
|
export function createNoteObject(text, name, id, published) {
|
||||||
const createUuid = crypto.randomBytes(16).toString('hex')
|
const createUuid = crypto.randomBytes(16).toString('hex')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': `${activityPub.endpoint}/activitypub/users/${name}/status/${createUuid}`,
|
id: `${activityPub.endpoint}/activitypub/users/${name}/status/${createUuid}`,
|
||||||
'type': 'Create',
|
type: 'Create',
|
||||||
'actor': `${activityPub.endpoint}/activitypub/users/${name}`,
|
actor: `${activityPub.endpoint}/activitypub/users/${name}`,
|
||||||
'object': {
|
object: {
|
||||||
'id': `${activityPub.endpoint}/activitypub/users/${name}/status/${id}`,
|
id: `${activityPub.endpoint}/activitypub/users/${name}/status/${id}`,
|
||||||
'type': 'Note',
|
type: 'Note',
|
||||||
'published': published,
|
published: published,
|
||||||
'attributedTo': `${activityPub.endpoint}/activitypub/users/${name}`,
|
attributedTo: `${activityPub.endpoint}/activitypub/users/${name}`,
|
||||||
'content': text,
|
content: text,
|
||||||
'to': 'https://www.w3.org/ns/activitystreams#Public'
|
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createArticleObject (activityId, objectId, text, name, id, published) {
|
export async function createArticleObject(activityId, objectId, text, name, id, published) {
|
||||||
const actorId = await getActorId(name)
|
const actorId = await getActorId(name)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': `${activityId}`,
|
id: `${activityId}`,
|
||||||
'type': 'Create',
|
type: 'Create',
|
||||||
'actor': `${actorId}`,
|
actor: `${actorId}`,
|
||||||
'object': {
|
object: {
|
||||||
'id': `${objectId}`,
|
id: `${objectId}`,
|
||||||
'type': 'Article',
|
type: 'Article',
|
||||||
'published': published,
|
published: published,
|
||||||
'attributedTo': `${actorId}`,
|
attributedTo: `${actorId}`,
|
||||||
'content': text,
|
content: text,
|
||||||
'to': 'https://www.w3.org/ns/activitystreams#Public'
|
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getActorId (name) {
|
export async function getActorId(name) {
|
||||||
const result = await activityPub.dataSource.client.query({
|
const result = await activityPub.dataSource.client.query({
|
||||||
query: gql`
|
query: gql`
|
||||||
query {
|
query {
|
||||||
@ -52,7 +52,7 @@ export async function getActorId (name) {
|
|||||||
actorId
|
actorId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
throwErrorIfApolloErrorOccurred(result)
|
throwErrorIfApolloErrorOccurred(result)
|
||||||
if (Array.isArray(result.data.User) && result.data.User[0]) {
|
if (Array.isArray(result.data.User) && result.data.User[0]) {
|
||||||
@ -62,9 +62,12 @@ export async function getActorId (name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendAcceptActivity (theBody, name, targetDomain, url) {
|
export function sendAcceptActivity(theBody, name, targetDomain, url) {
|
||||||
as.accept()
|
as.accept()
|
||||||
.id(`${activityPub.endpoint}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
|
.id(
|
||||||
|
`${activityPub.endpoint}/activitypub/users/${name}/status/` +
|
||||||
|
crypto.randomBytes(16).toString('hex'),
|
||||||
|
)
|
||||||
.actor(`${activityPub.endpoint}/activitypub/users/${name}`)
|
.actor(`${activityPub.endpoint}/activitypub/users/${name}`)
|
||||||
.object(theBody)
|
.object(theBody)
|
||||||
.prettyWrite((err, doc) => {
|
.prettyWrite((err, doc) => {
|
||||||
@ -77,9 +80,12 @@ export function sendAcceptActivity (theBody, name, targetDomain, url) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendRejectActivity (theBody, name, targetDomain, url) {
|
export function sendRejectActivity(theBody, name, targetDomain, url) {
|
||||||
as.reject()
|
as.reject()
|
||||||
.id(`${activityPub.endpoint}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
|
.id(
|
||||||
|
`${activityPub.endpoint}/activitypub/users/${name}/status/` +
|
||||||
|
crypto.randomBytes(16).toString('hex'),
|
||||||
|
)
|
||||||
.actor(`${activityPub.endpoint}/activitypub/users/${name}`)
|
.actor(`${activityPub.endpoint}/activitypub/users/${name}`)
|
||||||
.object(theBody)
|
.object(theBody)
|
||||||
.prettyWrite((err, doc) => {
|
.prettyWrite((err, doc) => {
|
||||||
@ -92,7 +98,7 @@ export function sendRejectActivity (theBody, name, targetDomain, url) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPublicAddressed (postObject) {
|
export function isPublicAddressed(postObject) {
|
||||||
if (typeof postObject.to === 'string') {
|
if (typeof postObject.to === 'string') {
|
||||||
postObject.to = [postObject.to]
|
postObject.to = [postObject.to]
|
||||||
}
|
}
|
||||||
@ -102,7 +108,9 @@ export function isPublicAddressed (postObject) {
|
|||||||
if (Array.isArray(postObject)) {
|
if (Array.isArray(postObject)) {
|
||||||
postObject.to = postObject
|
postObject.to = postObject
|
||||||
}
|
}
|
||||||
return postObject.to.includes('Public') ||
|
return (
|
||||||
|
postObject.to.includes('Public') ||
|
||||||
postObject.to.includes('as:Public') ||
|
postObject.to.includes('as:Public') ||
|
||||||
postObject.to.includes('https://www.w3.org/ns/activitystreams#Public')
|
postObject.to.includes('https://www.w3.org/ns/activitystreams#Public')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,41 +1,38 @@
|
|||||||
import { activityPub } from '../ActivityPub'
|
import { activityPub } from '../ActivityPub'
|
||||||
|
|
||||||
export function createActor (name, pubkey) {
|
export function createActor(name, pubkey) {
|
||||||
return {
|
return {
|
||||||
'@context': [
|
'@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'],
|
||||||
'https://www.w3.org/ns/activitystreams',
|
id: `${activityPub.endpoint}/activitypub/users/${name}`,
|
||||||
'https://w3id.org/security/v1'
|
type: 'Person',
|
||||||
],
|
preferredUsername: `${name}`,
|
||||||
'id': `${activityPub.endpoint}/activitypub/users/${name}`,
|
name: `${name}`,
|
||||||
'type': 'Person',
|
following: `${activityPub.endpoint}/activitypub/users/${name}/following`,
|
||||||
'preferredUsername': `${name}`,
|
followers: `${activityPub.endpoint}/activitypub/users/${name}/followers`,
|
||||||
'name': `${name}`,
|
inbox: `${activityPub.endpoint}/activitypub/users/${name}/inbox`,
|
||||||
'following': `${activityPub.endpoint}/activitypub/users/${name}/following`,
|
outbox: `${activityPub.endpoint}/activitypub/users/${name}/outbox`,
|
||||||
'followers': `${activityPub.endpoint}/activitypub/users/${name}/followers`,
|
url: `${activityPub.endpoint}/activitypub/@${name}`,
|
||||||
'inbox': `${activityPub.endpoint}/activitypub/users/${name}/inbox`,
|
endpoints: {
|
||||||
'outbox': `${activityPub.endpoint}/activitypub/users/${name}/outbox`,
|
sharedInbox: `${activityPub.endpoint}/activitypub/inbox`,
|
||||||
'url': `${activityPub.endpoint}/activitypub/@${name}`,
|
},
|
||||||
'endpoints': {
|
publicKey: {
|
||||||
'sharedInbox': `${activityPub.endpoint}/activitypub/inbox`
|
id: `${activityPub.endpoint}/activitypub/users/${name}#main-key`,
|
||||||
|
owner: `${activityPub.endpoint}/activitypub/users/${name}`,
|
||||||
|
publicKeyPem: pubkey,
|
||||||
},
|
},
|
||||||
'publicKey': {
|
|
||||||
'id': `${activityPub.endpoint}/activitypub/users/${name}#main-key`,
|
|
||||||
'owner': `${activityPub.endpoint}/activitypub/users/${name}`,
|
|
||||||
'publicKeyPem': pubkey
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createWebFinger (name) {
|
export function createWebFinger(name) {
|
||||||
const { host } = new URL(activityPub.endpoint)
|
const { host } = new URL(activityPub.endpoint)
|
||||||
return {
|
return {
|
||||||
'subject': `acct:${name}@${host}`,
|
subject: `acct:${name}@${host}`,
|
||||||
'links': [
|
links: [
|
||||||
{
|
{
|
||||||
'rel': 'self',
|
rel: 'self',
|
||||||
'type': 'application/activity+json',
|
type: 'application/activity+json',
|
||||||
'href': `${activityPub.endpoint}/activitypub/users/${name}`
|
href: `${activityPub.endpoint}/activitypub/users/${name}`,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,68 +2,71 @@ import { activityPub } from '../ActivityPub'
|
|||||||
import { constructIdFromName } from './index'
|
import { constructIdFromName } from './index'
|
||||||
const debug = require('debug')('ea:utils:collections')
|
const debug = require('debug')('ea:utils:collections')
|
||||||
|
|
||||||
export function createOrderedCollection (name, collectionName) {
|
export function createOrderedCollection(name, collectionName) {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}`,
|
id: `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}`,
|
||||||
'summary': `${name}s ${collectionName} collection`,
|
summary: `${name}s ${collectionName} collection`,
|
||||||
'type': 'OrderedCollection',
|
type: 'OrderedCollection',
|
||||||
'first': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}?page=true`,
|
first: `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}?page=true`,
|
||||||
'totalItems': 0
|
totalItems: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createOrderedCollectionPage (name, collectionName) {
|
export function createOrderedCollectionPage(name, collectionName) {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}?page=true`,
|
id: `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}?page=true`,
|
||||||
'summary': `${name}s ${collectionName} collection`,
|
summary: `${name}s ${collectionName} collection`,
|
||||||
'type': 'OrderedCollectionPage',
|
type: 'OrderedCollectionPage',
|
||||||
'totalItems': 0,
|
totalItems: 0,
|
||||||
'partOf': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}`,
|
partOf: `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}`,
|
||||||
'orderedItems': []
|
orderedItems: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function sendCollection (collectionName, req, res) {
|
export function sendCollection(collectionName, req, res) {
|
||||||
const name = req.params.name
|
const name = req.params.name
|
||||||
const id = constructIdFromName(name)
|
const id = constructIdFromName(name)
|
||||||
|
|
||||||
switch (collectionName) {
|
switch (collectionName) {
|
||||||
case 'followers':
|
case 'followers':
|
||||||
attachThenCatch(activityPub.collections.getFollowersCollection(id), res)
|
attachThenCatch(activityPub.collections.getFollowersCollection(id), res)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'followersPage':
|
case 'followersPage':
|
||||||
attachThenCatch(activityPub.collections.getFollowersCollectionPage(id), res)
|
attachThenCatch(activityPub.collections.getFollowersCollectionPage(id), res)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'following':
|
case 'following':
|
||||||
attachThenCatch(activityPub.collections.getFollowingCollection(id), res)
|
attachThenCatch(activityPub.collections.getFollowingCollection(id), res)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'followingPage':
|
case 'followingPage':
|
||||||
attachThenCatch(activityPub.collections.getFollowingCollectionPage(id), res)
|
attachThenCatch(activityPub.collections.getFollowingCollectionPage(id), res)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'outbox':
|
case 'outbox':
|
||||||
attachThenCatch(activityPub.collections.getOutboxCollection(id), res)
|
attachThenCatch(activityPub.collections.getOutboxCollection(id), res)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'outboxPage':
|
case 'outboxPage':
|
||||||
attachThenCatch(activityPub.collections.getOutboxCollectionPage(id), res)
|
attachThenCatch(activityPub.collections.getOutboxCollectionPage(id), res)
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
res.status(500).end()
|
res.status(500).end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachThenCatch (promise, res) {
|
function attachThenCatch(promise, res) {
|
||||||
return promise
|
return promise
|
||||||
.then((collection) => {
|
.then(collection => {
|
||||||
res.status(200).contentType('application/activity+json').send(collection)
|
res
|
||||||
|
.status(200)
|
||||||
|
.contentType('application/activity+json')
|
||||||
|
.send(collection)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch(err => {
|
||||||
debug(`error getting a Collection: = ${err}`)
|
debug(`error getting a Collection: = ${err}`)
|
||||||
res.status(500).end()
|
res.status(500).end()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { createSignature } from '../security'
|
|||||||
import request from 'request'
|
import request from 'request'
|
||||||
const debug = require('debug')('ea:utils')
|
const debug = require('debug')('ea:utils')
|
||||||
|
|
||||||
export function extractNameFromId (uri) {
|
export function extractNameFromId(uri) {
|
||||||
const urlObject = new URL(uri)
|
const urlObject = new URL(uri)
|
||||||
const pathname = urlObject.pathname
|
const pathname = urlObject.pathname
|
||||||
const splitted = pathname.split('/')
|
const splitted = pathname.split('/')
|
||||||
@ -12,28 +12,30 @@ export function extractNameFromId (uri) {
|
|||||||
return splitted[splitted.indexOf('users') + 1]
|
return splitted[splitted.indexOf('users') + 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractIdFromActivityId (uri) {
|
export function extractIdFromActivityId(uri) {
|
||||||
const urlObject = new URL(uri)
|
const urlObject = new URL(uri)
|
||||||
const pathname = urlObject.pathname
|
const pathname = urlObject.pathname
|
||||||
const splitted = pathname.split('/')
|
const splitted = pathname.split('/')
|
||||||
|
|
||||||
return splitted[splitted.indexOf('status') + 1]
|
return splitted[splitted.indexOf('status') + 1]
|
||||||
}
|
}
|
||||||
export function constructIdFromName (name, fromDomain = activityPub.endpoint) {
|
export function constructIdFromName(name, fromDomain = activityPub.endpoint) {
|
||||||
return `${fromDomain}/activitypub/users/${name}`
|
return `${fromDomain}/activitypub/users/${name}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractDomainFromUrl (url) {
|
export function extractDomainFromUrl(url) {
|
||||||
return new URL(url).host
|
return new URL(url).host
|
||||||
}
|
}
|
||||||
|
|
||||||
export function throwErrorIfApolloErrorOccurred (result) {
|
export function throwErrorIfApolloErrorOccurred(result) {
|
||||||
if (result.error && (result.error.message || result.error.errors)) {
|
if (result.error && (result.error.message || result.error.errors)) {
|
||||||
throw new Error(`${result.error.message ? result.error.message : result.error.errors[0].message}`)
|
throw new Error(
|
||||||
|
`${result.error.message ? result.error.message : result.error.errors[0].message}`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function signAndSend (activity, fromName, targetDomain, url) {
|
export function signAndSend(activity, fromName, targetDomain, url) {
|
||||||
// fix for development: replace with http
|
// fix for development: replace with http
|
||||||
url = url.indexOf('localhost') > -1 ? url.replace('https', 'http') : url
|
url = url.indexOf('localhost') > -1 ? url.replace('https', 'http') : url
|
||||||
debug(`passhprase = ${process.env.PRIVATE_KEY_PASSPHRASE}`)
|
debug(`passhprase = ${process.env.PRIVATE_KEY_PASSPHRASE}`)
|
||||||
@ -47,7 +49,7 @@ export function signAndSend (activity, fromName, targetDomain, url) {
|
|||||||
privateKey
|
privateKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
@ -69,34 +71,38 @@ export function signAndSend (activity, fromName, targetDomain, url) {
|
|||||||
const date = new Date().toUTCString()
|
const date = new Date().toUTCString()
|
||||||
|
|
||||||
debug(`url = ${url}`)
|
debug(`url = ${url}`)
|
||||||
request({
|
request(
|
||||||
url: url,
|
{
|
||||||
headers: {
|
url: url,
|
||||||
'Host': targetDomain,
|
headers: {
|
||||||
'Date': date,
|
Host: targetDomain,
|
||||||
'Signature': createSignature({ privateKey,
|
Date: date,
|
||||||
keyId: `${activityPub.endpoint}/activitypub/users/${fromName}#main-key`,
|
Signature: createSignature({
|
||||||
url,
|
privateKey,
|
||||||
headers: {
|
keyId: `${activityPub.endpoint}/activitypub/users/${fromName}#main-key`,
|
||||||
'Host': targetDomain,
|
url,
|
||||||
'Date': date,
|
headers: {
|
||||||
'Content-Type': 'application/activity+json'
|
Host: targetDomain,
|
||||||
}
|
Date: date,
|
||||||
}),
|
'Content-Type': 'application/activity+json',
|
||||||
'Content-Type': 'application/activity+json'
|
},
|
||||||
|
}),
|
||||||
|
'Content-Type': 'application/activity+json',
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(parsedActivity),
|
||||||
},
|
},
|
||||||
method: 'POST',
|
(error, response) => {
|
||||||
body: JSON.stringify(parsedActivity)
|
if (error) {
|
||||||
}, (error, response) => {
|
debug(`Error = ${JSON.stringify(error, null, 2)}`)
|
||||||
if (error) {
|
reject(error)
|
||||||
debug(`Error = ${JSON.stringify(error, null, 2)}`)
|
} else {
|
||||||
reject(error)
|
debug('Response Headers:', JSON.stringify(response.headers, null, 2))
|
||||||
} else {
|
debug('Response Body:', JSON.stringify(response.body, null, 2))
|
||||||
debug('Response Headers:', JSON.stringify(response.headers, null, 2))
|
resolve()
|
||||||
debug('Response Body:', JSON.stringify(response.body, null, 2))
|
}
|
||||||
resolve()
|
},
|
||||||
}
|
)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
GraphQLLowerCaseDirective,
|
GraphQLLowerCaseDirective,
|
||||||
GraphQLTrimDirective,
|
GraphQLTrimDirective,
|
||||||
GraphQLDefaultToDirective
|
GraphQLDefaultToDirective,
|
||||||
} from 'graphql-custom-directives'
|
} from 'graphql-custom-directives'
|
||||||
|
|
||||||
export default function applyDirectives (augmentedSchema) {
|
export default function applyDirectives(augmentedSchema) {
|
||||||
const directives = [
|
const directives = [GraphQLLowerCaseDirective, GraphQLTrimDirective, GraphQLDefaultToDirective]
|
||||||
GraphQLLowerCaseDirective,
|
|
||||||
GraphQLTrimDirective,
|
|
||||||
GraphQLDefaultToDirective
|
|
||||||
]
|
|
||||||
augmentedSchema._directives.push.apply(augmentedSchema._directives, directives)
|
augmentedSchema._directives.push.apply(augmentedSchema._directives, directives)
|
||||||
|
|
||||||
return augmentedSchema
|
return augmentedSchema
|
||||||
|
|||||||
@ -5,11 +5,11 @@ dotenv.config()
|
|||||||
|
|
||||||
let driver
|
let driver
|
||||||
|
|
||||||
export function getDriver (options = {}) {
|
export function getDriver(options = {}) {
|
||||||
const {
|
const {
|
||||||
uri = process.env.NEO4J_URI || 'bolt://localhost:7687',
|
uri = process.env.NEO4J_URI || 'bolt://localhost:7687',
|
||||||
username = process.env.NEO4J_USERNAME || 'neo4j',
|
username = process.env.NEO4J_USERNAME || 'neo4j',
|
||||||
password = process.env.NEO4J_PASSWORD || 'neo4j'
|
password = process.env.NEO4J_PASSWORD || 'neo4j',
|
||||||
} = options
|
} = options
|
||||||
if (!driver) {
|
if (!driver) {
|
||||||
driver = neo4j.driver(uri, neo4j.auth.basic(username, password))
|
driver = neo4j.driver(uri, neo4j.auth.basic(username, password))
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
import {
|
import { GraphQLDate, GraphQLTime, GraphQLDateTime } from 'graphql-iso-date'
|
||||||
GraphQLDate,
|
|
||||||
GraphQLTime,
|
|
||||||
GraphQLDateTime
|
|
||||||
} from 'graphql-iso-date'
|
|
||||||
|
|
||||||
export default function applyScalars (augmentedSchema) {
|
export default function applyScalars(augmentedSchema) {
|
||||||
augmentedSchema._typeMap.Date = GraphQLDate
|
augmentedSchema._typeMap.Date = GraphQLDate
|
||||||
augmentedSchema._typeMap.Time = GraphQLTime
|
augmentedSchema._typeMap.Time = GraphQLTime
|
||||||
augmentedSchema._typeMap.DateTime = GraphQLDateTime
|
augmentedSchema._typeMap.DateTime = GraphQLDateTime
|
||||||
|
|||||||
@ -14,9 +14,7 @@ import notifications from './resolvers/notifications'
|
|||||||
import comments from './resolvers/comments'
|
import comments from './resolvers/comments'
|
||||||
|
|
||||||
export const typeDefs = fs
|
export const typeDefs = fs
|
||||||
.readFileSync(
|
.readFileSync(process.env.GRAPHQL_SCHEMA || path.join(__dirname, 'schema.graphql'))
|
||||||
process.env.GRAPHQL_SCHEMA || path.join(__dirname, 'schema.graphql')
|
|
||||||
)
|
|
||||||
.toString('utf-8')
|
.toString('utf-8')
|
||||||
|
|
||||||
export const resolvers = {
|
export const resolvers = {
|
||||||
@ -24,7 +22,7 @@ export const resolvers = {
|
|||||||
...statistics.Query,
|
...statistics.Query,
|
||||||
...userManagement.Query,
|
...userManagement.Query,
|
||||||
...notifications.Query,
|
...notifications.Query,
|
||||||
...comments.Query
|
...comments.Query,
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
...userManagement.Mutation,
|
...userManagement.Mutation,
|
||||||
@ -36,6 +34,6 @@ export const resolvers = {
|
|||||||
...rewards.Mutation,
|
...rewards.Mutation,
|
||||||
...socialMedia.Mutation,
|
...socialMedia.Mutation,
|
||||||
...notifications.Mutation,
|
...notifications.Mutation,
|
||||||
...comments.Mutation
|
...comments.Mutation,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
* @param callback
|
* @param callback
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async function asyncForEach (array, callback) {
|
async function asyncForEach(array, callback) {
|
||||||
for (let index = 0; index < array.length; index++) {
|
for (let index = 0; index < array.length; index++) {
|
||||||
await callback(array[index], index, array)
|
await callback(array[index], index, array)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* iterate through all fields and replace it with the callback result
|
* iterate through all fields and replace it with the callback result
|
||||||
* @property data Array
|
* @property data Array
|
||||||
* @property fields Array
|
* @property fields Array
|
||||||
* @property callback Function
|
* @property callback Function
|
||||||
*/
|
*/
|
||||||
function walkRecursive (data, fields, callback, _key) {
|
function walkRecursive(data, fields, callback, _key) {
|
||||||
if (!Array.isArray(fields)) {
|
if (!Array.isArray(fields)) {
|
||||||
throw new Error('please provide an fields array for the walkRecursive helper')
|
throw new Error('please provide an fields array for the walkRecursive helper')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import createServer from './server'
|
|||||||
import ActivityPub from './activitypub/ActivityPub'
|
import ActivityPub from './activitypub/ActivityPub'
|
||||||
|
|
||||||
const serverConfig = {
|
const serverConfig = {
|
||||||
port: process.env.GRAPHQL_PORT || 4000
|
port: process.env.GRAPHQL_PORT || 4000,
|
||||||
// cors: {
|
// cors: {
|
||||||
// credentials: true,
|
// credentials: true,
|
||||||
// origin: [process.env.CLIENT_URI] // your frontend url.
|
// origin: [process.env.CLIENT_URI] // your frontend url.
|
||||||
|
|||||||
@ -4,13 +4,13 @@ import { request } from 'graphql-request'
|
|||||||
// not to be confused with the seeder host
|
// not to be confused with the seeder host
|
||||||
export const host = 'http://127.0.0.1:4123'
|
export const host = 'http://127.0.0.1:4123'
|
||||||
|
|
||||||
export async function login ({ email, password }) {
|
export async function login({ email, password }) {
|
||||||
const mutation = `
|
const mutation = `
|
||||||
mutation {
|
mutation {
|
||||||
login(email:"${email}", password:"${password}")
|
login(email:"${email}", password:"${password}")
|
||||||
}`
|
}`
|
||||||
const response = await request(host, mutation)
|
const response = await request(host, mutation)
|
||||||
return {
|
return {
|
||||||
authorization: `Bearer ${response.login}`
|
authorization: `Bearer ${response.login}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export default async (driver, authorizationHeader) => {
|
|||||||
try {
|
try {
|
||||||
const decoded = await jwt.verify(token, process.env.JWT_SECRET)
|
const decoded = await jwt.verify(token, process.env.JWT_SECRET)
|
||||||
id = decoded.sub
|
id = decoded.sub
|
||||||
} catch {
|
} catch (err) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
@ -18,13 +18,13 @@ export default async (driver, authorizationHeader) => {
|
|||||||
`
|
`
|
||||||
const result = await session.run(query, { id })
|
const result = await session.run(query, { id })
|
||||||
session.close()
|
session.close()
|
||||||
const [currentUser] = await result.records.map((record) => {
|
const [currentUser] = await result.records.map(record => {
|
||||||
return record.get('user')
|
return record.get('user')
|
||||||
})
|
})
|
||||||
if (!currentUser) return null
|
if (!currentUser) return null
|
||||||
if (currentUser.disabled) return null
|
if (currentUser.disabled) return null
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
...currentUser
|
...currentUser,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
|
|
||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import ms from 'ms'
|
import ms from 'ms'
|
||||||
|
|
||||||
// Generate an Access Token for the given User ID
|
// Generate an Access Token for the given User ID
|
||||||
export default function encode (user) {
|
export default function encode(user) {
|
||||||
const token = jwt.sign(user, process.env.JWT_SECRET, {
|
const token = jwt.sign(user, process.env.JWT_SECRET, {
|
||||||
expiresIn: ms('1d'),
|
expiresIn: ms('1d'),
|
||||||
issuer: process.env.GRAPHQL_URI,
|
issuer: process.env.GRAPHQL_URI,
|
||||||
audience: process.env.CLIENT_URI,
|
audience: process.env.CLIENT_URI,
|
||||||
subject: user.id.toString()
|
subject: user.id.toString(),
|
||||||
})
|
})
|
||||||
// jwt.verifySignature(token, process.env.JWT_SECRET, (err, data) => {
|
// jwt.verifySignature(token, process.env.JWT_SECRET, (err, data) => {
|
||||||
// console.log('token verification:', err, data)
|
// console.log('token verification:', err, data)
|
||||||
|
|||||||
@ -22,13 +22,15 @@ export default {
|
|||||||
.id(`${actorId}/status/${args.activityId}`)
|
.id(`${actorId}/status/${args.activityId}`)
|
||||||
.actor(`${actorId}`)
|
.actor(`${actorId}`)
|
||||||
.object(
|
.object(
|
||||||
as.article()
|
as
|
||||||
|
.article()
|
||||||
.id(`${actorId}/status/${post.id}`)
|
.id(`${actorId}/status/${post.id}`)
|
||||||
.content(post.content)
|
.content(post.content)
|
||||||
.to('https://www.w3.org/ns/activitystreams#Public')
|
.to('https://www.w3.org/ns/activitystreams#Public')
|
||||||
.publishedNow()
|
.publishedNow()
|
||||||
.attributedTo(`${actorId}`)
|
.attributedTo(`${actorId}`),
|
||||||
).prettyWrite((err, doc) => {
|
)
|
||||||
|
.prettyWrite((err, doc) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err)
|
reject(err)
|
||||||
} else {
|
} else {
|
||||||
@ -51,6 +53,6 @@ export default {
|
|||||||
Object.assign(args, keys)
|
Object.assign(args, keys)
|
||||||
args.actorId = `${activityPub.host}/activitypub/users/${args.slug}`
|
args.actorId = `${activityPub.host}/activitypub/users/${args.slug}`
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
const setCreatedAt = (resolve, root, args, context, info) => {
|
const setCreatedAt = (resolve, root, args, context, info) => {
|
||||||
args.createdAt = (new Date()).toISOString()
|
args.createdAt = new Date().toISOString()
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
}
|
}
|
||||||
const setUpdatedAt = (resolve, root, args, context, info) => {
|
const setUpdatedAt = (resolve, root, args, context, info) => {
|
||||||
args.updatedAt = (new Date()).toISOString()
|
args.updatedAt = new Date().toISOString()
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,6 +18,6 @@ export default {
|
|||||||
UpdatePost: setUpdatedAt,
|
UpdatePost: setUpdatedAt,
|
||||||
UpdateComment: setUpdatedAt,
|
UpdateComment: setUpdatedAt,
|
||||||
UpdateOrganization: setUpdatedAt,
|
UpdateOrganization: setUpdatedAt,
|
||||||
UpdateNotification: setUpdatedAt
|
UpdateNotification: setUpdatedAt,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,6 @@ export default {
|
|||||||
args.descriptionExcerpt = trunc(args.description, 120).html
|
args.descriptionExcerpt = trunc(args.description, 120).html
|
||||||
const result = await resolve(root, args, context, info)
|
const result = await resolve(root, args, context, info)
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,24 @@
|
|||||||
|
|
||||||
const legacyUrls = [
|
const legacyUrls = [
|
||||||
'https://api-alpha.human-connection.org',
|
'https://api-alpha.human-connection.org',
|
||||||
'https://staging-api.human-connection.org',
|
'https://staging-api.human-connection.org',
|
||||||
'http://localhost:3000'
|
'http://localhost:3000',
|
||||||
]
|
]
|
||||||
|
|
||||||
export const fixUrl = (url) => {
|
export const fixUrl = url => {
|
||||||
legacyUrls.forEach((legacyUrl) => {
|
legacyUrls.forEach(legacyUrl => {
|
||||||
url = url.replace(legacyUrl, '/api')
|
url = url.replace(legacyUrl, '/api')
|
||||||
})
|
})
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkUrl = (thing) => {
|
const checkUrl = thing => {
|
||||||
return thing && typeof thing === 'string' && legacyUrls.find((legacyUrl) => {
|
return (
|
||||||
return thing.indexOf(legacyUrl) === 0
|
thing &&
|
||||||
})
|
typeof thing === 'string' &&
|
||||||
|
legacyUrls.find(legacyUrl => {
|
||||||
|
return thing.indexOf(legacyUrl) === 0
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fixImageURLs = (result, recursive) => {
|
export const fixImageURLs = (result, recursive) => {
|
||||||
@ -41,5 +44,5 @@ export default {
|
|||||||
Query: async (resolve, root, args, context, info) => {
|
Query: async (resolve, root, args, context, info) => {
|
||||||
let result = await resolve(root, args, context, info)
|
let result = await resolve(root, args, context, info)
|
||||||
return fixImageURLs(result)
|
return fixImageURLs(result)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,15 +3,21 @@ import { fixImageURLs } from './fixImageUrlsMiddleware'
|
|||||||
describe('fixImageURLs', () => {
|
describe('fixImageURLs', () => {
|
||||||
describe('image url of legacy alpha', () => {
|
describe('image url of legacy alpha', () => {
|
||||||
it('removes domain', () => {
|
it('removes domain', () => {
|
||||||
const url = 'https://api-alpha.human-connection.org/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png'
|
const url =
|
||||||
expect(fixImageURLs(url)).toEqual('/api/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png')
|
'https://api-alpha.human-connection.org/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png'
|
||||||
|
expect(fixImageURLs(url)).toEqual(
|
||||||
|
'/api/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('image url of legacy staging', () => {
|
describe('image url of legacy staging', () => {
|
||||||
it('removes domain', () => {
|
it('removes domain', () => {
|
||||||
const url = 'https://staging-api.human-connection.org/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg'
|
const url =
|
||||||
expect(fixImageURLs(url)).toEqual('/api/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg')
|
'https://staging-api.human-connection.org/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg'
|
||||||
|
expect(fixImageURLs(url)).toEqual(
|
||||||
|
'/api/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -24,7 +30,7 @@ describe('fixImageURLs', () => {
|
|||||||
|
|
||||||
describe('some string', () => {
|
describe('some string', () => {
|
||||||
it('returns untouched', () => {})
|
it('returns untouched', () => {})
|
||||||
const string = 'Yeah I\'m a String'
|
const string = "Yeah I'm a String"
|
||||||
expect(fixImageURLs(string)).toEqual(string)
|
expect(fixImageURLs(string)).toEqual(string)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,21 +2,21 @@ import cloneDeep from 'lodash/cloneDeep'
|
|||||||
|
|
||||||
const _includeFieldsRecursively = (selectionSet, includedFields) => {
|
const _includeFieldsRecursively = (selectionSet, includedFields) => {
|
||||||
if (!selectionSet) return
|
if (!selectionSet) return
|
||||||
includedFields.forEach((includedField) => {
|
includedFields.forEach(includedField => {
|
||||||
selectionSet.selections.unshift({
|
selectionSet.selections.unshift({
|
||||||
kind: 'Field',
|
kind: 'Field',
|
||||||
name: { kind: 'Name', value: includedField }
|
name: { kind: 'Name', value: includedField },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
selectionSet.selections.forEach((selection) => {
|
selectionSet.selections.forEach(selection => {
|
||||||
_includeFieldsRecursively(selection.selectionSet, includedFields)
|
_includeFieldsRecursively(selection.selectionSet, includedFields)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const includeFieldsRecursively = (includedFields) => {
|
const includeFieldsRecursively = includedFields => {
|
||||||
return (resolve, root, args, context, resolveInfo) => {
|
return (resolve, root, args, context, resolveInfo) => {
|
||||||
const copy = cloneDeep(resolveInfo)
|
const copy = cloneDeep(resolveInfo)
|
||||||
copy.fieldNodes.forEach((fieldNode) => {
|
copy.fieldNodes.forEach(fieldNode => {
|
||||||
_includeFieldsRecursively(fieldNode.selectionSet, includedFields)
|
_includeFieldsRecursively(fieldNode.selectionSet, includedFields)
|
||||||
})
|
})
|
||||||
return resolve(root, args, context, copy)
|
return resolve(root, args, context, copy)
|
||||||
@ -25,5 +25,5 @@ const includeFieldsRecursively = (includedFields) => {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: includeFieldsRecursively(['id', 'createdAt', 'disabled', 'deleted']),
|
Query: includeFieldsRecursively(['id', 'createdAt', 'disabled', 'deleted']),
|
||||||
Mutation: includeFieldsRecursively(['id', 'createdAt', 'disabled', 'deleted'])
|
Mutation: includeFieldsRecursively(['id', 'createdAt', 'disabled', 'deleted']),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export default schema => {
|
|||||||
softDeleteMiddleware,
|
softDeleteMiddleware,
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
includedFieldsMiddleware,
|
includedFieldsMiddleware,
|
||||||
orderByMiddleware
|
orderByMiddleware,
|
||||||
]
|
]
|
||||||
|
|
||||||
// add permisions middleware at the first position (unless we're seeding)
|
// add permisions middleware at the first position (unless we're seeding)
|
||||||
@ -35,7 +35,8 @@ export default schema => {
|
|||||||
const DISABLED_MIDDLEWARES = process.env.DISABLED_MIDDLEWARES || ''
|
const DISABLED_MIDDLEWARES = process.env.DISABLED_MIDDLEWARES || ''
|
||||||
const disabled = DISABLED_MIDDLEWARES.split(',')
|
const disabled = DISABLED_MIDDLEWARES.split(',')
|
||||||
if (!disabled.includes('activityPub')) middleware.unshift(activityPubMiddleware)
|
if (!disabled.includes('activityPub')) middleware.unshift(activityPubMiddleware)
|
||||||
if (!disabled.includes('permissions')) middleware.unshift(permissionsMiddleware.generate(schema))
|
if (!disabled.includes('permissions'))
|
||||||
|
middleware.unshift(permissionsMiddleware.generate(schema))
|
||||||
}
|
}
|
||||||
return middleware
|
return middleware
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import request from 'request'
|
import request from 'request'
|
||||||
import { UserInputError } from 'apollo-server'
|
import { UserInputError } from 'apollo-server'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import isEmpty from 'lodash/isEmpty'
|
||||||
@ -6,7 +5,7 @@ import asyncForEach from '../../helpers/asyncForEach'
|
|||||||
|
|
||||||
const fetch = url => {
|
const fetch = url => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
request(url, function (error, response, body) {
|
request(url, function(error, response, body) {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error)
|
reject(error)
|
||||||
} else {
|
} else {
|
||||||
@ -16,16 +15,7 @@ const fetch = url => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const locales = [
|
const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl']
|
||||||
'en',
|
|
||||||
'de',
|
|
||||||
'fr',
|
|
||||||
'nl',
|
|
||||||
'it',
|
|
||||||
'es',
|
|
||||||
'pt',
|
|
||||||
'pl'
|
|
||||||
]
|
|
||||||
|
|
||||||
const createLocation = async (session, mapboxData) => {
|
const createLocation = async (session, mapboxData) => {
|
||||||
const data = {
|
const data = {
|
||||||
@ -39,21 +29,22 @@ const createLocation = async (session, mapboxData) => {
|
|||||||
namePT: mapboxData.text_pt,
|
namePT: mapboxData.text_pt,
|
||||||
namePL: mapboxData.text_pl,
|
namePL: mapboxData.text_pl,
|
||||||
type: mapboxData.id.split('.')[0].toLowerCase(),
|
type: mapboxData.id.split('.')[0].toLowerCase(),
|
||||||
lat: (mapboxData.center && mapboxData.center.length) ? mapboxData.center[0] : null,
|
lat: mapboxData.center && mapboxData.center.length ? mapboxData.center[0] : null,
|
||||||
lng: (mapboxData.center && mapboxData.center.length) ? mapboxData.center[1] : null
|
lng: mapboxData.center && mapboxData.center.length ? mapboxData.center[1] : null,
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = 'MERGE (l:Location {id: $id}) ' +
|
let query =
|
||||||
'SET l.name = $nameEN, ' +
|
'MERGE (l:Location {id: $id}) ' +
|
||||||
'l.nameEN = $nameEN, ' +
|
'SET l.name = $nameEN, ' +
|
||||||
'l.nameDE = $nameDE, ' +
|
'l.nameEN = $nameEN, ' +
|
||||||
'l.nameFR = $nameFR, ' +
|
'l.nameDE = $nameDE, ' +
|
||||||
'l.nameNL = $nameNL, ' +
|
'l.nameFR = $nameFR, ' +
|
||||||
'l.nameIT = $nameIT, ' +
|
'l.nameNL = $nameNL, ' +
|
||||||
'l.nameES = $nameES, ' +
|
'l.nameIT = $nameIT, ' +
|
||||||
'l.namePT = $namePT, ' +
|
'l.nameES = $nameES, ' +
|
||||||
'l.namePL = $namePL, ' +
|
'l.namePT = $namePT, ' +
|
||||||
'l.type = $type'
|
'l.namePL = $namePL, ' +
|
||||||
|
'l.type = $type'
|
||||||
|
|
||||||
if (data.lat && data.lng) {
|
if (data.lat && data.lng) {
|
||||||
query += ', l.lat = $lat, l.lng = $lng'
|
query += ', l.lat = $lat, l.lng = $lng'
|
||||||
@ -68,7 +59,11 @@ const createOrUpdateLocations = async (userId, locationName, driver) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const mapboxToken = process.env.MAPBOX_TOKEN
|
const mapboxToken = process.env.MAPBOX_TOKEN
|
||||||
const res = await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(locationName)}.json?access_token=${mapboxToken}&types=region,place,country&language=${locales.join(',')}`)
|
const res = await fetch(
|
||||||
|
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
|
||||||
|
locationName,
|
||||||
|
)}.json?access_token=${mapboxToken}&types=region,place,country&language=${locales.join(',')}`,
|
||||||
|
)
|
||||||
|
|
||||||
if (!res || !res.features || !res.features[0]) {
|
if (!res || !res.features || !res.features[0]) {
|
||||||
throw new UserInputError('locationName is invalid')
|
throw new UserInputError('locationName is invalid')
|
||||||
@ -100,24 +95,29 @@ const createOrUpdateLocations = async (userId, locationName, driver) => {
|
|||||||
|
|
||||||
await session.run(
|
await session.run(
|
||||||
'MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId}) ' +
|
'MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId}) ' +
|
||||||
'MERGE (child)<-[:IS_IN]-(parent) ' +
|
'MERGE (child)<-[:IS_IN]-(parent) ' +
|
||||||
'RETURN child.id, parent.id', {
|
'RETURN child.id, parent.id',
|
||||||
|
{
|
||||||
parentId: parent.id,
|
parentId: parent.id,
|
||||||
childId: ctx.id
|
childId: ctx.id,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
parent = ctx
|
parent = ctx
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// delete all current locations from user
|
// delete all current locations from user
|
||||||
await session.run('MATCH (u:User {id: $userId})-[r:IS_IN]->(l:Location) DETACH DELETE r', {
|
await session.run('MATCH (u:User {id: $userId})-[r:IS_IN]->(l:Location) DETACH DELETE r', {
|
||||||
userId: userId
|
userId: userId,
|
||||||
})
|
})
|
||||||
// connect user with location
|
// connect user with location
|
||||||
await session.run('MATCH (u:User {id: $userId}), (l:Location {id: $locationId}) MERGE (u)-[:IS_IN]->(l) RETURN l.id, u.id', {
|
await session.run(
|
||||||
userId: userId,
|
'MATCH (u:User {id: $userId}), (l:Location {id: $locationId}) MERGE (u)-[:IS_IN]->(l) RETURN l.id, u.id',
|
||||||
locationId: data.id
|
{
|
||||||
})
|
userId: userId,
|
||||||
|
locationId: data.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
session.close()
|
session.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
const ID_REGEX = /\/profile\/([\w\-.!~*'"(),]+)/g
|
const ID_REGEX = /\/profile\/([\w\-.!~*'"(),]+)/g
|
||||||
|
|
||||||
export default function (content) {
|
export default function(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
const urls = $('.mention').map((_, el) => {
|
const urls = $('.mention')
|
||||||
return $(el).attr('href')
|
.map((_, el) => {
|
||||||
}).get()
|
return $(el).attr('href')
|
||||||
|
})
|
||||||
|
.get()
|
||||||
const ids = []
|
const ids = []
|
||||||
urls.forEach((url) => {
|
urls.forEach(url => {
|
||||||
let match
|
let match
|
||||||
while ((match = ID_REGEX.exec(url)) != null) {
|
while ((match = ID_REGEX.exec(url)) != null) {
|
||||||
ids.push(match[1])
|
ids.push(match[1])
|
||||||
|
|||||||
@ -3,41 +3,48 @@ import extractIds from './extractMentions'
|
|||||||
describe('extract', () => {
|
describe('extract', () => {
|
||||||
describe('searches through links', () => {
|
describe('searches through links', () => {
|
||||||
it('ignores links without .mention class', () => {
|
it('ignores links without .mention class', () => {
|
||||||
const content = '<p>Something inspirational about <a href="/profile/u2" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3" target="_blank">@jenny-rostock</a>.</p>'
|
const content =
|
||||||
|
'<p>Something inspirational about <a href="/profile/u2" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3" target="_blank">@jenny-rostock</a>.</p>'
|
||||||
expect(extractIds(content)).toEqual([])
|
expect(extractIds(content)).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('given a link with .mention class', () => {
|
describe('given a link with .mention class', () => {
|
||||||
it('extracts ids', () => {
|
it('extracts ids', () => {
|
||||||
const content = '<p>Something inspirational about <a href="/profile/u2" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3/jenny-rostock" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
const content =
|
||||||
|
'<p>Something inspirational about <a href="/profile/u2" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3/jenny-rostock" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
||||||
expect(extractIds(content)).toEqual(['u2', 'u3'])
|
expect(extractIds(content)).toEqual(['u2', 'u3'])
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('handles links', () => {
|
describe('handles links', () => {
|
||||||
it('with slug and id', () => {
|
it('with slug and id', () => {
|
||||||
const content = '<p>Something inspirational about <a href="/profile/u2/bob-der-baumeister" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3/jenny-rostock/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
const content =
|
||||||
|
'<p>Something inspirational about <a href="/profile/u2/bob-der-baumeister" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3/jenny-rostock/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
||||||
expect(extractIds(content)).toEqual(['u2', 'u3'])
|
expect(extractIds(content)).toEqual(['u2', 'u3'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('with domains', () => {
|
it('with domains', () => {
|
||||||
const content = '<p>Something inspirational about <a href="http://localhost:3000/profile/u2/bob-der-baumeister" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="http://localhost:3000//profile/u3/jenny-rostock/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
const content =
|
||||||
|
'<p>Something inspirational about <a href="http://localhost:3000/profile/u2/bob-der-baumeister" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="http://localhost:3000//profile/u3/jenny-rostock/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
||||||
expect(extractIds(content)).toEqual(['u2', 'u3'])
|
expect(extractIds(content)).toEqual(['u2', 'u3'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('special characters', () => {
|
it('special characters', () => {
|
||||||
const content = '<p>Something inspirational about <a href="http://localhost:3000/profile/u!*(),2/bob-der-baumeister" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="http://localhost:3000//profile/u.~-3/jenny-rostock/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
const content =
|
||||||
|
'<p>Something inspirational about <a href="http://localhost:3000/profile/u!*(),2/bob-der-baumeister" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="http://localhost:3000//profile/u.~-3/jenny-rostock/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
||||||
expect(extractIds(content)).toEqual(['u!*(),2', 'u.~-3'])
|
expect(extractIds(content)).toEqual(['u!*(),2', 'u.~-3'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('does not crash if', () => {
|
describe('does not crash if', () => {
|
||||||
it('`href` contains no user id', () => {
|
it('`href` contains no user id', () => {
|
||||||
const content = '<p>Something inspirational about <a href="/profile" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="/profile/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
const content =
|
||||||
|
'<p>Something inspirational about <a href="/profile" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="/profile/" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
||||||
expect(extractIds(content)).toEqual([])
|
expect(extractIds(content)).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('`href` is empty or invalid', () => {
|
it('`href` is empty or invalid', () => {
|
||||||
const content = '<p>Something inspirational about <a href="" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="not-a-url" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
const content =
|
||||||
|
'<p>Something inspirational about <a href="" class="mention" target="_blank">@bob-der-baumeister</a> and <a href="not-a-url" class="mention" target="_blank">@jenny-rostock</a>.</p>'
|
||||||
expect(extractIds(content)).toEqual([])
|
expect(extractIds(content)).toEqual([])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const notify = async (resolve, root, args, context, resolveInfo) => {
|
|||||||
|
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const { id: postId } = post
|
const { id: postId } = post
|
||||||
const createdAt = (new Date()).toISOString()
|
const createdAt = new Date().toISOString()
|
||||||
const cypher = `
|
const cypher = `
|
||||||
match(u:User) where u.id in $ids
|
match(u:User) where u.id in $ids
|
||||||
match(p:Post) where p.id = $postId
|
match(p:Post) where p.id = $postId
|
||||||
@ -25,6 +25,6 @@ const notify = async (resolve, root, args, context, resolveInfo) => {
|
|||||||
export default {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
CreatePost: notify,
|
CreatePost: notify,
|
||||||
UpdatePost: notify
|
UpdatePost: notify,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ beforeEach(async () => {
|
|||||||
name: 'Al Capone',
|
name: 'Al Capone',
|
||||||
slug: 'al-capone',
|
slug: 'al-capone',
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ describe('currentUser { notifications }', () => {
|
|||||||
authorParams = {
|
authorParams = {
|
||||||
email: 'author@example.org',
|
email: 'author@example.org',
|
||||||
password: '1234',
|
password: '1234',
|
||||||
id: 'author'
|
id: 'author',
|
||||||
}
|
}
|
||||||
await factory.create('User', authorParams)
|
await factory.create('User', authorParams)
|
||||||
authorHeaders = await login(authorParams)
|
authorHeaders = await login(authorParams)
|
||||||
@ -56,7 +56,8 @@ describe('currentUser { notifications }', () => {
|
|||||||
describe('who mentions me in a post', () => {
|
describe('who mentions me in a post', () => {
|
||||||
let post
|
let post
|
||||||
const title = 'Mentioning Al Capone'
|
const title = 'Mentioning Al Capone'
|
||||||
const content = 'Hey <a class="mention" href="/profile/you/al-capone">@al-capone</a> how do you do?'
|
const content =
|
||||||
|
'Hey <a class="mention" href="/profile/you/al-capone">@al-capone</a> how do you do?'
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const createPostMutation = `
|
const createPostMutation = `
|
||||||
@ -74,20 +75,21 @@ describe('currentUser { notifications }', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends you a notification', async () => {
|
it('sends you a notification', async () => {
|
||||||
const expectedContent = 'Hey <a href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do?'
|
const expectedContent =
|
||||||
|
'Hey <a href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do?'
|
||||||
const expected = {
|
const expected = {
|
||||||
currentUser: {
|
currentUser: {
|
||||||
notifications: [
|
notifications: [{ read: false, post: { content: expectedContent } }],
|
||||||
{ read: false, post: { content: expectedContent } }
|
},
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await expect(client.request(query, { read: false })).resolves.toEqual(expected)
|
await expect(client.request(query, { read: false })).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('who mentions me again', () => {
|
describe('who mentions me again', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const updatedContent = `${post.content} One more mention to <a href="/profile/you" class="mention">@al-capone</a>`
|
const updatedContent = `${
|
||||||
|
post.content
|
||||||
|
} One more mention to <a href="/profile/you" class="mention">@al-capone</a>`
|
||||||
// The response `post.content` contains a link but the XSSmiddleware
|
// The response `post.content` contains a link but the XSSmiddleware
|
||||||
// should have the `mention` CSS class removed. I discovered this
|
// should have the `mention` CSS class removed. I discovered this
|
||||||
// during development and thought: A feature not a bug! This way we
|
// during development and thought: A feature not a bug! This way we
|
||||||
@ -106,14 +108,15 @@ describe('currentUser { notifications }', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('creates exactly one more notification', async () => {
|
it('creates exactly one more notification', async () => {
|
||||||
const expectedContent = 'Hey <a href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do? One more mention to <a href="/profile/you" target="_blank">@al-capone</a>'
|
const expectedContent =
|
||||||
|
'Hey <a href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do? One more mention to <a href="/profile/you" target="_blank">@al-capone</a>'
|
||||||
const expected = {
|
const expected = {
|
||||||
currentUser: {
|
currentUser: {
|
||||||
notifications: [
|
notifications: [
|
||||||
{ read: false, post: { content: expectedContent } },
|
{ read: false, post: { content: expectedContent } },
|
||||||
{ read: false, post: { content: expectedContent } }
|
{ read: false, post: { content: expectedContent } },
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(query, { read: false })).resolves.toEqual(expected)
|
await expect(client.request(query, { read: false })).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -5,7 +5,7 @@ const defaultOrderBy = (resolve, root, args, context, resolveInfo) => {
|
|||||||
const newestFirst = {
|
const newestFirst = {
|
||||||
kind: 'Argument',
|
kind: 'Argument',
|
||||||
name: { kind: 'Name', value: 'orderBy' },
|
name: { kind: 'Name', value: 'orderBy' },
|
||||||
value: { kind: 'EnumValue', value: 'createdAt_desc' }
|
value: { kind: 'EnumValue', value: 'createdAt_desc' },
|
||||||
}
|
}
|
||||||
const [fieldNode] = copy.fieldNodes
|
const [fieldNode] = copy.fieldNodes
|
||||||
if (fieldNode) fieldNode.arguments.push(newestFirst)
|
if (fieldNode) fieldNode.arguments.push(newestFirst)
|
||||||
@ -14,6 +14,6 @@ const defaultOrderBy = (resolve, root, args, context, resolveInfo) => {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
Post: defaultOrderBy
|
Post: defaultOrderBy,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ describe('Query', () => {
|
|||||||
{ title: 'last' },
|
{ title: 'last' },
|
||||||
{ title: 'third' },
|
{ title: 'third' },
|
||||||
{ title: 'second' },
|
{ title: 'second' },
|
||||||
{ title: 'first' }
|
{ title: 'first' },
|
||||||
]
|
]
|
||||||
const expected = { Post: posts }
|
const expected = { Post: posts }
|
||||||
await expect(client.request(query)).resolves.toEqual(expected)
|
await expect(client.request(query)).resolves.toEqual(expected)
|
||||||
@ -51,7 +51,7 @@ describe('Query', () => {
|
|||||||
{ title: 'first' },
|
{ title: 'first' },
|
||||||
{ title: 'second' },
|
{ title: 'second' },
|
||||||
{ title: 'third' },
|
{ title: 'third' },
|
||||||
{ title: 'last' }
|
{ title: 'last' },
|
||||||
]
|
]
|
||||||
const expected = { Post: posts }
|
const expected = { Post: posts }
|
||||||
await expect(client.request(query)).resolves.toEqual(expected)
|
await expect(client.request(query)).resolves.toEqual(expected)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export default {
|
|||||||
const result = await resolve(root, args, context, info)
|
const result = await resolve(root, args, context, info)
|
||||||
result.password = '*****'
|
result.password = '*****'
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
Query: async (resolve, root, args, context, info) => {
|
Query: async (resolve, root, args, context, info) => {
|
||||||
let result = await resolve(root, args, context, info)
|
let result = await resolve(root, args, context, info)
|
||||||
@ -17,5 +17,5 @@ export default {
|
|||||||
return '*****'
|
return '*****'
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const isModerator = rule()(async (parent, args, { user }, info) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isAdmin = rule()(async (parent, args, { user }, info) => {
|
const isAdmin = rule()(async (parent, args, { user }, info) => {
|
||||||
return user && (user.role === 'admin')
|
return user && user.role === 'admin'
|
||||||
})
|
})
|
||||||
|
|
||||||
const isMyOwn = rule({ cache: 'no_cache' })(async (parent, args, context, info) => {
|
const isMyOwn = rule({ cache: 'no_cache' })(async (parent, args, context, info) => {
|
||||||
@ -21,14 +21,20 @@ const isMyOwn = rule({ cache: 'no_cache' })(async (parent, args, context, info)
|
|||||||
})
|
})
|
||||||
|
|
||||||
const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => {
|
const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => {
|
||||||
const { driver, user: { id: userId } } = context
|
const {
|
||||||
|
driver,
|
||||||
|
user: { id: userId },
|
||||||
|
} = context
|
||||||
const { id: notificationId } = args
|
const { id: notificationId } = args
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const result = await session.run(`
|
const result = await session.run(
|
||||||
|
`
|
||||||
MATCH (u:User {id: $userId})<-[:NOTIFIED]-(n:Notification {id: $notificationId})
|
MATCH (u:User {id: $userId})<-[:NOTIFIED]-(n:Notification {id: $notificationId})
|
||||||
RETURN n
|
RETURN n
|
||||||
`, { userId, notificationId })
|
`,
|
||||||
const [notification] = result.records.map((record) => {
|
{ userId, notificationId },
|
||||||
|
)
|
||||||
|
const [notification] = result.records.map(record => {
|
||||||
return record.get('n')
|
return record.get('n')
|
||||||
})
|
})
|
||||||
session.close()
|
session.close()
|
||||||
@ -44,14 +50,19 @@ const isAuthor = rule({ cache: 'no_cache' })(async (parent, args, { user, driver
|
|||||||
if (!user) return false
|
if (!user) return false
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const { id: postId } = args
|
const { id: postId } = args
|
||||||
const result = await session.run(`
|
const result = await session.run(
|
||||||
|
`
|
||||||
MATCH (post:Post {id: $postId})<-[:WROTE]-(author)
|
MATCH (post:Post {id: $postId})<-[:WROTE]-(author)
|
||||||
RETURN author
|
RETURN author
|
||||||
`, { postId })
|
`,
|
||||||
const [author] = result.records.map((record) => {
|
{ postId },
|
||||||
|
)
|
||||||
|
const [author] = result.records.map(record => {
|
||||||
return record.get('author')
|
return record.get('author')
|
||||||
})
|
})
|
||||||
const { properties: { id: authorId } } = author
|
const {
|
||||||
|
properties: { id: authorId },
|
||||||
|
} = author
|
||||||
session.close()
|
session.close()
|
||||||
return authorId === user.id
|
return authorId === user.id
|
||||||
})
|
})
|
||||||
@ -62,7 +73,7 @@ const permissions = shield({
|
|||||||
Notification: isAdmin,
|
Notification: isAdmin,
|
||||||
statistics: allow,
|
statistics: allow,
|
||||||
currentUser: allow,
|
currentUser: allow,
|
||||||
Post: or(onlyEnabledContent, isModerator)
|
Post: or(onlyEnabledContent, isModerator),
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
UpdateNotification: belongsToMe,
|
UpdateNotification: belongsToMe,
|
||||||
@ -88,14 +99,14 @@ const permissions = shield({
|
|||||||
changePassword: isAuthenticated,
|
changePassword: isAuthenticated,
|
||||||
enable: isModerator,
|
enable: isModerator,
|
||||||
disable: isModerator,
|
disable: isModerator,
|
||||||
CreateComment: isAuthenticated
|
CreateComment: isAuthenticated,
|
||||||
// CreateUser: allow,
|
// CreateUser: allow,
|
||||||
},
|
},
|
||||||
User: {
|
User: {
|
||||||
email: isMyOwn,
|
email: isMyOwn,
|
||||||
password: isMyOwn,
|
password: isMyOwn,
|
||||||
privateKey: isMyOwn
|
privateKey: isMyOwn,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default permissions
|
export default permissions
|
||||||
|
|||||||
@ -10,12 +10,12 @@ describe('authorization', () => {
|
|||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
email: 'owner@example.org',
|
email: 'owner@example.org',
|
||||||
name: 'Owner',
|
name: 'Owner',
|
||||||
password: 'iamtheowner'
|
password: 'iamtheowner',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
email: 'someone@example.org',
|
email: 'someone@example.org',
|
||||||
name: 'Someone else',
|
name: 'Someone else',
|
||||||
password: 'else'
|
password: 'else',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -39,14 +39,14 @@ describe('authorization', () => {
|
|||||||
await expect(action()).rejects.toThrow('Not Authorised!')
|
await expect(action()).rejects.toThrow('Not Authorised!')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not expose the owner\'s email address', async () => {
|
it("does not expose the owner's email address", async () => {
|
||||||
let response = {}
|
let response = {}
|
||||||
try {
|
try {
|
||||||
await action()
|
await action()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
response = error.response.data
|
response = error.response.data
|
||||||
} finally {
|
} finally {
|
||||||
expect(response).toEqual({ User: [ null ] })
|
expect(response).toEqual({ User: [null] })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -55,12 +55,12 @@ describe('authorization', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
loginCredentials = {
|
loginCredentials = {
|
||||||
email: 'owner@example.org',
|
email: 'owner@example.org',
|
||||||
password: 'iamtheowner'
|
password: 'iamtheowner',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('exposes the owner\'s email address', async () => {
|
it("exposes the owner's email address", async () => {
|
||||||
await expect(action()).resolves.toEqual({ User: [ { email: 'owner@example.org' } ] })
|
await expect(action()).resolves.toEqual({ User: [{ email: 'owner@example.org' }] })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ describe('authorization', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
loginCredentials = {
|
loginCredentials = {
|
||||||
email: 'someone@example.org',
|
email: 'someone@example.org',
|
||||||
password: 'else'
|
password: 'else',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -76,14 +76,14 @@ describe('authorization', () => {
|
|||||||
await expect(action()).rejects.toThrow('Not Authorised!')
|
await expect(action()).rejects.toThrow('Not Authorised!')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not expose the owner\'s email address', async () => {
|
it("does not expose the owner's email address", async () => {
|
||||||
let response
|
let response
|
||||||
try {
|
try {
|
||||||
await action()
|
await action()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
response = error.response.data
|
response = error.response.data
|
||||||
}
|
}
|
||||||
expect(response).toEqual({ User: [ null ] })
|
expect(response).toEqual({ User: [null] })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -3,12 +3,9 @@ import uniqueSlug from './slugify/uniqueSlug'
|
|||||||
const isUniqueFor = (context, type) => {
|
const isUniqueFor = (context, type) => {
|
||||||
return async slug => {
|
return async slug => {
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const response = await session.run(
|
const response = await session.run(`MATCH(p:${type} {slug: $slug }) return p.slug`, {
|
||||||
`MATCH(p:${type} {slug: $slug }) return p.slug`,
|
slug,
|
||||||
{
|
})
|
||||||
slug
|
|
||||||
}
|
|
||||||
)
|
|
||||||
session.close()
|
session.close()
|
||||||
return response.records.length === 0
|
return response.records.length === 0
|
||||||
}
|
}
|
||||||
@ -17,28 +14,20 @@ const isUniqueFor = (context, type) => {
|
|||||||
export default {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
CreatePost: async (resolve, root, args, context, info) => {
|
CreatePost: async (resolve, root, args, context, info) => {
|
||||||
args.slug =
|
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
||||||
args.slug ||
|
|
||||||
(await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
},
|
},
|
||||||
CreateUser: async (resolve, root, args, context, info) => {
|
CreateUser: async (resolve, root, args, context, info) => {
|
||||||
args.slug =
|
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'User')))
|
||||||
args.slug ||
|
|
||||||
(await uniqueSlug(args.name, isUniqueFor(context, 'User')))
|
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
},
|
},
|
||||||
CreateOrganization: async (resolve, root, args, context, info) => {
|
CreateOrganization: async (resolve, root, args, context, info) => {
|
||||||
args.slug =
|
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Organization')))
|
||||||
args.slug ||
|
|
||||||
(await uniqueSlug(args.name, isUniqueFor(context, 'Organization')))
|
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
},
|
},
|
||||||
CreateCategory: async (resolve, root, args, context, info) => {
|
CreateCategory: async (resolve, root, args, context, info) => {
|
||||||
args.slug =
|
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Category')))
|
||||||
args.slug ||
|
|
||||||
(await uniqueSlug(args.name, isUniqueFor(context, 'Category')))
|
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import slugify from 'slug'
|
import slugify from 'slug'
|
||||||
export default async function uniqueSlug (string, isUnique) {
|
export default async function uniqueSlug(string, isUnique) {
|
||||||
let slug = slugify(string || 'anonymous', {
|
let slug = slugify(string || 'anonymous', {
|
||||||
lower: true
|
lower: true,
|
||||||
})
|
})
|
||||||
if (await isUnique(slug)) return slug
|
if (await isUnique(slug)) return slug
|
||||||
|
|
||||||
@ -10,6 +10,6 @@ export default async function uniqueSlug (string, isUnique) {
|
|||||||
do {
|
do {
|
||||||
count += 1
|
count += 1
|
||||||
uniqueSlug = `${slug}-${count}`
|
uniqueSlug = `${slug}-${count}`
|
||||||
} while (!await isUnique(uniqueSlug))
|
} while (!(await isUnique(uniqueSlug)))
|
||||||
return uniqueSlug
|
return uniqueSlug
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,14 +3,14 @@ import uniqueSlug from './uniqueSlug'
|
|||||||
describe('uniqueSlug', () => {
|
describe('uniqueSlug', () => {
|
||||||
it('slugifies given string', () => {
|
it('slugifies given string', () => {
|
||||||
const string = 'Hello World'
|
const string = 'Hello World'
|
||||||
const isUnique = jest.fn()
|
const isUnique = jest.fn().mockResolvedValue(true)
|
||||||
.mockResolvedValue(true)
|
|
||||||
expect(uniqueSlug(string, isUnique)).resolves.toEqual('hello-world')
|
expect(uniqueSlug(string, isUnique)).resolves.toEqual('hello-world')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('increments slugified string until unique', () => {
|
it('increments slugified string until unique', () => {
|
||||||
const string = 'Hello World'
|
const string = 'Hello World'
|
||||||
const isUnique = jest.fn()
|
const isUnique = jest
|
||||||
|
.fn()
|
||||||
.mockResolvedValueOnce(false)
|
.mockResolvedValueOnce(false)
|
||||||
.mockResolvedValueOnce(true)
|
.mockResolvedValueOnce(true)
|
||||||
expect(uniqueSlug(string, isUnique)).resolves.toEqual('hello-world-1')
|
expect(uniqueSlug(string, isUnique)).resolves.toEqual('hello-world-1')
|
||||||
@ -18,8 +18,7 @@ describe('uniqueSlug', () => {
|
|||||||
|
|
||||||
it('slugify null string', () => {
|
it('slugify null string', () => {
|
||||||
const string = null
|
const string = null
|
||||||
const isUnique = jest.fn()
|
const isUnique = jest.fn().mockResolvedValue(true)
|
||||||
.mockResolvedValue(true)
|
|
||||||
expect(uniqueSlug(string, isUnique)).resolves.toEqual('anonymous')
|
expect(uniqueSlug(string, isUnique)).resolves.toEqual('anonymous')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -10,7 +10,7 @@ beforeEach(async () => {
|
|||||||
await factory.create('User', { email: 'user@example.org', password: '1234' })
|
await factory.create('User', { email: 'user@example.org', password: '1234' })
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
email: 'someone@example.org',
|
email: 'someone@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
headers = await login({ email: 'user@example.org', password: '1234' })
|
headers = await login({ email: 'user@example.org', password: '1234' })
|
||||||
authenticatedClient = new GraphQLClient(host, { headers })
|
authenticatedClient = new GraphQLClient(host, { headers })
|
||||||
@ -30,7 +30,7 @@ describe('slugify', () => {
|
|||||||
) { slug }
|
) { slug }
|
||||||
}`)
|
}`)
|
||||||
expect(response).toEqual({
|
expect(response).toEqual({
|
||||||
CreatePost: { slug: 'i-am-a-brand-new-post' }
|
CreatePost: { slug: 'i-am-a-brand-new-post' },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -38,11 +38,11 @@ describe('slugify', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const asSomeoneElse = await Factory().authenticateAs({
|
const asSomeoneElse = await Factory().authenticateAs({
|
||||||
email: 'someone@example.org',
|
email: 'someone@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await asSomeoneElse.create('Post', {
|
await asSomeoneElse.create('Post', {
|
||||||
title: 'Pre-existing post',
|
title: 'Pre-existing post',
|
||||||
slug: 'pre-existing-post'
|
slug: 'pre-existing-post',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ describe('slugify', () => {
|
|||||||
) { slug }
|
) { slug }
|
||||||
}`)
|
}`)
|
||||||
expect(response).toEqual({
|
expect(response).toEqual({
|
||||||
CreatePost: { slug: 'pre-existing-post-1' }
|
CreatePost: { slug: 'pre-existing-post-1' },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ describe('slugify', () => {
|
|||||||
content: "Some content",
|
content: "Some content",
|
||||||
slug: "pre-existing-post"
|
slug: "pre-existing-post"
|
||||||
) { slug }
|
) { slug }
|
||||||
}`)
|
}`),
|
||||||
).rejects.toThrow('already exists')
|
).rejects.toThrow('already exists')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -81,32 +81,26 @@ describe('slugify', () => {
|
|||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
it('generates a slug based on name', async () => {
|
it('generates a slug based on name', async () => {
|
||||||
await expect(
|
await expect(action('CreateUser', 'name: "I am a user"')).resolves.toEqual({
|
||||||
action('CreateUser', 'name: "I am a user"')
|
CreateUser: { slug: 'i-am-a-user' },
|
||||||
).resolves.toEqual({ CreateUser: { slug: 'i-am-a-user' } })
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('if slug exists', () => {
|
describe('if slug exists', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await action(
|
await action('CreateUser', 'name: "Pre-existing user", slug: "pre-existing-user"')
|
||||||
'CreateUser',
|
|
||||||
'name: "Pre-existing user", slug: "pre-existing-user"'
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('chooses another slug', async () => {
|
it('chooses another slug', async () => {
|
||||||
await expect(
|
await expect(action('CreateUser', 'name: "pre-existing-user"')).resolves.toEqual({
|
||||||
action('CreateUser', 'name: "pre-existing-user"')
|
CreateUser: { slug: 'pre-existing-user-1' },
|
||||||
).resolves.toEqual({ CreateUser: { slug: 'pre-existing-user-1' } })
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('but if the client specifies a slug', () => {
|
describe('but if the client specifies a slug', () => {
|
||||||
it('rejects CreateUser', async () => {
|
it('rejects CreateUser', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
action(
|
action('CreateUser', 'name: "Pre-existing user", slug: "pre-existing-user"'),
|
||||||
'CreateUser',
|
|
||||||
'name: "Pre-existing user", slug: "pre-existing-user"'
|
|
||||||
)
|
|
||||||
).rejects.toThrow('already exists')
|
).rejects.toThrow('already exists')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export default {
|
|||||||
Query: {
|
Query: {
|
||||||
Post: setDefaultFilters,
|
Post: setDefaultFilters,
|
||||||
Comment: setDefaultFilters,
|
Comment: setDefaultFilters,
|
||||||
User: setDefaultFilters
|
User: setDefaultFilters,
|
||||||
},
|
},
|
||||||
Mutation: async (resolve, root, args, context, info) => {
|
Mutation: async (resolve, root, args, context, info) => {
|
||||||
args.disabled = false
|
args.disabled = false
|
||||||
@ -42,5 +42,5 @@ export default {
|
|||||||
},
|
},
|
||||||
Post: obfuscateDisabled,
|
Post: obfuscateDisabled,
|
||||||
User: obfuscateDisabled,
|
User: obfuscateDisabled,
|
||||||
Comment: obfuscateDisabled
|
Comment: obfuscateDisabled,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,32 +11,51 @@ beforeAll(async () => {
|
|||||||
// For performance reasons we do this only once
|
// For performance reasons we do this only once
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
factory.create('User', { id: 'u1', role: 'user', email: 'user@example.org', password: '1234' }),
|
factory.create('User', { id: 'u1', role: 'user', email: 'user@example.org', password: '1234' }),
|
||||||
factory.create('User', { id: 'm1', role: 'moderator', email: 'moderator@example.org', password: '1234' }),
|
factory.create('User', {
|
||||||
factory.create('User', { id: 'u2', role: 'user', name: 'Offensive Name', avatar: '/some/offensive/avatar.jpg', about: 'This self description is very offensive', email: 'troll@example.org', password: '1234' })
|
id: 'm1',
|
||||||
|
role: 'moderator',
|
||||||
|
email: 'moderator@example.org',
|
||||||
|
password: '1234',
|
||||||
|
}),
|
||||||
|
factory.create('User', {
|
||||||
|
id: 'u2',
|
||||||
|
role: 'user',
|
||||||
|
name: 'Offensive Name',
|
||||||
|
avatar: '/some/offensive/avatar.jpg',
|
||||||
|
about: 'This self description is very offensive',
|
||||||
|
email: 'troll@example.org',
|
||||||
|
password: '1234',
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
await factory.authenticateAs({ email: 'user@example.org', password: '1234' })
|
await factory.authenticateAs({ email: 'user@example.org', password: '1234' })
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
factory.follow({ id: 'u2', type: 'User' }),
|
factory.follow({ id: 'u2', type: 'User' }),
|
||||||
factory.create('Post', { id: 'p1', title: 'Deleted post', deleted: true }),
|
factory.create('Post', { id: 'p1', title: 'Deleted post', deleted: true }),
|
||||||
factory.create('Post', { id: 'p3', title: 'Publicly visible post', deleted: false })
|
factory.create('Post', { id: 'p3', title: 'Publicly visible post', deleted: false }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
factory.create('Comment', { id: 'c2', postId: 'p3', content: 'Enabled comment on public post' })
|
factory.create('Comment', {
|
||||||
|
id: 'c2',
|
||||||
|
postId: 'p3',
|
||||||
|
content: 'Enabled comment on public post',
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([factory.relate('Comment', 'Author', { from: 'u1', to: 'c2' })])
|
||||||
factory.relate('Comment', 'Author', { from: 'u1', to: 'c2' })
|
|
||||||
])
|
|
||||||
|
|
||||||
const asTroll = Factory()
|
const asTroll = Factory()
|
||||||
await asTroll.authenticateAs({ email: 'troll@example.org', password: '1234' })
|
await asTroll.authenticateAs({ email: 'troll@example.org', password: '1234' })
|
||||||
await asTroll.create('Post', { id: 'p2', title: 'Disabled post', content: 'This is an offensive post content', image: '/some/offensive/image.jpg', deleted: false })
|
await asTroll.create('Post', {
|
||||||
|
id: 'p2',
|
||||||
|
title: 'Disabled post',
|
||||||
|
content: 'This is an offensive post content',
|
||||||
|
image: '/some/offensive/image.jpg',
|
||||||
|
deleted: false,
|
||||||
|
})
|
||||||
await asTroll.create('Comment', { id: 'c1', postId: 'p3', content: 'Disabled comment' })
|
await asTroll.create('Comment', { id: 'c1', postId: 'p3', content: 'Disabled comment' })
|
||||||
await Promise.all([
|
await Promise.all([asTroll.relate('Comment', 'Author', { from: 'u2', to: 'c1' })])
|
||||||
asTroll.relate('Comment', 'Author', { from: 'u2', to: 'c1' })
|
|
||||||
])
|
|
||||||
|
|
||||||
const asModerator = Factory()
|
const asModerator = Factory()
|
||||||
await asModerator.authenticateAs({ email: 'moderator@example.org', password: '1234' })
|
await asModerator.authenticateAs({ email: 'moderator@example.org', password: '1234' })
|
||||||
@ -65,7 +84,8 @@ describe('softDeleteMiddleware', () => {
|
|||||||
user = response.User[0].following[0]
|
user = response.User[0].following[0]
|
||||||
}
|
}
|
||||||
const beforePost = async () => {
|
const beforePost = async () => {
|
||||||
query = '{ User(id: "u1") { following { contributions { title image content contentExcerpt } } } }'
|
query =
|
||||||
|
'{ User(id: "u1") { following { contributions { title image content contentExcerpt } } } }'
|
||||||
const response = await action()
|
const response = await action()
|
||||||
post = response.User[0].following[0].contributions[0]
|
post = response.User[0].following[0].contributions[0]
|
||||||
}
|
}
|
||||||
@ -84,7 +104,8 @@ describe('softDeleteMiddleware', () => {
|
|||||||
beforeEach(beforeUser)
|
beforeEach(beforeUser)
|
||||||
|
|
||||||
it('displays name', () => expect(user.name).toEqual('Offensive Name'))
|
it('displays name', () => expect(user.name).toEqual('Offensive Name'))
|
||||||
it('displays about', () => expect(user.about).toEqual('This self description is very offensive'))
|
it('displays about', () =>
|
||||||
|
expect(user.about).toEqual('This self description is very offensive'))
|
||||||
it('displays avatar', () => expect(user.avatar).toEqual('/some/offensive/avatar.jpg'))
|
it('displays avatar', () => expect(user.avatar).toEqual('/some/offensive/avatar.jpg'))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -92,8 +113,10 @@ describe('softDeleteMiddleware', () => {
|
|||||||
beforeEach(beforePost)
|
beforeEach(beforePost)
|
||||||
|
|
||||||
it('displays title', () => expect(post.title).toEqual('Disabled post'))
|
it('displays title', () => expect(post.title).toEqual('Disabled post'))
|
||||||
it('displays content', () => expect(post.content).toEqual('This is an offensive post content'))
|
it('displays content', () =>
|
||||||
it('displays contentExcerpt', () => expect(post.contentExcerpt).toEqual('This is an offensive post content'))
|
expect(post.content).toEqual('This is an offensive post content'))
|
||||||
|
it('displays contentExcerpt', () =>
|
||||||
|
expect(post.contentExcerpt).toEqual('This is an offensive post content'))
|
||||||
it('displays image', () => expect(post.image).toEqual('/some/offensive/image.jpg'))
|
it('displays image', () => expect(post.image).toEqual('/some/offensive/image.jpg'))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -101,7 +124,8 @@ describe('softDeleteMiddleware', () => {
|
|||||||
beforeEach(beforeComment)
|
beforeEach(beforeComment)
|
||||||
|
|
||||||
it('displays content', () => expect(comment.content).toEqual('Disabled comment'))
|
it('displays content', () => expect(comment.content).toEqual('Disabled comment'))
|
||||||
it('displays contentExcerpt', () => expect(comment.contentExcerpt).toEqual('Disabled comment'))
|
it('displays contentExcerpt', () =>
|
||||||
|
expect(comment.contentExcerpt).toEqual('Disabled comment'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -162,10 +186,7 @@ describe('softDeleteMiddleware', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('shows disabled but hides deleted posts', async () => {
|
it('shows disabled but hides deleted posts', async () => {
|
||||||
const expected = [
|
const expected = [{ title: 'Disabled post' }, { title: 'Publicly visible post' }]
|
||||||
{ title: 'Disabled post' },
|
|
||||||
{ title: 'Publicly visible post' }
|
|
||||||
]
|
|
||||||
const { Post } = await action()
|
const { Post } = await action()
|
||||||
await expect(Post).toEqual(expect.arrayContaining(expected))
|
await expect(Post).toEqual(expect.arrayContaining(expected))
|
||||||
})
|
})
|
||||||
@ -185,9 +206,11 @@ describe('softDeleteMiddleware', () => {
|
|||||||
it('conceals disabled comments', async () => {
|
it('conceals disabled comments', async () => {
|
||||||
const expected = [
|
const expected = [
|
||||||
{ content: 'Enabled comment on public post' },
|
{ content: 'Enabled comment on public post' },
|
||||||
{ content: 'UNAVAILABLE' }
|
{ content: 'UNAVAILABLE' },
|
||||||
]
|
]
|
||||||
const { Post: [{ comments }] } = await action()
|
const {
|
||||||
|
Post: [{ comments }],
|
||||||
|
} = await action()
|
||||||
await expect(comments).toEqual(expect.arrayContaining(expected))
|
await expect(comments).toEqual(expect.arrayContaining(expected))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -201,9 +224,11 @@ describe('softDeleteMiddleware', () => {
|
|||||||
it('shows disabled comments', async () => {
|
it('shows disabled comments', async () => {
|
||||||
const expected = [
|
const expected = [
|
||||||
{ content: 'Enabled comment on public post' },
|
{ content: 'Enabled comment on public post' },
|
||||||
{ content: 'Disabled comment' }
|
{ content: 'Disabled comment' },
|
||||||
]
|
]
|
||||||
const { Post: [{ comments }] } = await action()
|
const {
|
||||||
|
Post: [{ comments }],
|
||||||
|
} = await action()
|
||||||
await expect(comments).toEqual(expect.arrayContaining(expected))
|
await expect(comments).toEqual(expect.arrayContaining(expected))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -15,6 +15,6 @@ export default {
|
|||||||
const result = await resolve(root, args, context, info)
|
const result = await resolve(root, args, context, info)
|
||||||
await createOrUpdateLocations(args.id, args.locationName, context.driver)
|
await createOrUpdateLocations(args.id, args.locationName, context.driver)
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,6 @@ export default {
|
|||||||
Mutation: {
|
Mutation: {
|
||||||
CreateUser: validateUsername,
|
CreateUser: validateUsername,
|
||||||
UpdateUser: validateUsername,
|
UpdateUser: validateUsername,
|
||||||
CreateSocialMedia: validateUrl
|
CreateSocialMedia: validateUrl,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import sanitizeHtml from 'sanitize-html'
|
|||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
import linkifyHtml from 'linkifyjs/html'
|
import linkifyHtml from 'linkifyjs/html'
|
||||||
|
|
||||||
const embedToAnchor = (content) => {
|
const embedToAnchor = content => {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
$('div[data-url-embed]').each((i, el) => {
|
$('div[data-url-embed]').each((i, el) => {
|
||||||
let url = el.attribs['data-url-embed']
|
let url = el.attribs['data-url-embed']
|
||||||
@ -15,7 +15,7 @@ const embedToAnchor = (content) => {
|
|||||||
return $('body').html()
|
return $('body').html()
|
||||||
}
|
}
|
||||||
|
|
||||||
function clean (dirty) {
|
function clean(dirty) {
|
||||||
if (!dirty) {
|
if (!dirty) {
|
||||||
return dirty
|
return dirty
|
||||||
}
|
}
|
||||||
@ -24,27 +24,48 @@ function clean (dirty) {
|
|||||||
dirty = embedToAnchor(dirty)
|
dirty = embedToAnchor(dirty)
|
||||||
dirty = linkifyHtml(dirty)
|
dirty = linkifyHtml(dirty)
|
||||||
dirty = sanitizeHtml(dirty, {
|
dirty = sanitizeHtml(dirty, {
|
||||||
allowedTags: ['iframe', 'img', 'p', 'h3', 'h4', 'br', 'hr', 'b', 'i', 'em', 'strong', 'a', 'pre', 'ul', 'li', 'ol', 's', 'strike', 'span', 'blockquote'],
|
allowedTags: [
|
||||||
|
'iframe',
|
||||||
|
'img',
|
||||||
|
'p',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'br',
|
||||||
|
'hr',
|
||||||
|
'b',
|
||||||
|
'i',
|
||||||
|
'em',
|
||||||
|
'strong',
|
||||||
|
'a',
|
||||||
|
'pre',
|
||||||
|
'ul',
|
||||||
|
'li',
|
||||||
|
'ol',
|
||||||
|
's',
|
||||||
|
'strike',
|
||||||
|
'span',
|
||||||
|
'blockquote',
|
||||||
|
],
|
||||||
allowedAttributes: {
|
allowedAttributes: {
|
||||||
a: ['href', 'class', 'target', 'data-*', 'contenteditable'],
|
a: ['href', 'class', 'target', 'data-*', 'contenteditable'],
|
||||||
span: ['contenteditable', 'class', 'data-*'],
|
span: ['contenteditable', 'class', 'data-*'],
|
||||||
img: ['src'],
|
img: ['src'],
|
||||||
iframe: ['src', 'class', 'frameborder', 'allowfullscreen']
|
iframe: ['src', 'class', 'frameborder', 'allowfullscreen'],
|
||||||
},
|
},
|
||||||
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
|
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
|
||||||
parser: {
|
parser: {
|
||||||
lowerCaseTags: true
|
lowerCaseTags: true,
|
||||||
},
|
},
|
||||||
transformTags: {
|
transformTags: {
|
||||||
iframe: function (tagName, attribs) {
|
iframe: function(tagName, attribs) {
|
||||||
return {
|
return {
|
||||||
tagName: 'a',
|
tagName: 'a',
|
||||||
text: attribs.src,
|
text: attribs.src,
|
||||||
attribs: {
|
attribs: {
|
||||||
href: attribs.src,
|
href: attribs.src,
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
'data-url-embed': ''
|
'data-url-embed': '',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
h1: 'h3',
|
h1: 'h3',
|
||||||
@ -53,19 +74,19 @@ function clean (dirty) {
|
|||||||
h4: 'h4',
|
h4: 'h4',
|
||||||
h5: 'strong',
|
h5: 'strong',
|
||||||
i: 'em',
|
i: 'em',
|
||||||
a: function (tagName, attribs) {
|
a: function(tagName, attribs) {
|
||||||
return {
|
return {
|
||||||
tagName: 'a',
|
tagName: 'a',
|
||||||
attribs: {
|
attribs: {
|
||||||
href: attribs.href,
|
href: attribs.href,
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
rel: 'noopener noreferrer nofollow'
|
rel: 'noopener noreferrer nofollow',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
b: 'strong',
|
b: 'strong',
|
||||||
s: 'strike',
|
s: 'strike',
|
||||||
img: function (tagName, attribs) {
|
img: function(tagName, attribs) {
|
||||||
let src = attribs.src
|
let src = attribs.src
|
||||||
|
|
||||||
if (!src) {
|
if (!src) {
|
||||||
@ -88,11 +109,11 @@ function clean (dirty) {
|
|||||||
tagName: 'img',
|
tagName: 'img',
|
||||||
attribs: {
|
attribs: {
|
||||||
// TODO: use environment variables
|
// TODO: use environment variables
|
||||||
src: `http://localhost:3050/images?url=${src}`
|
src: `http://localhost:3050/images?url=${src}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// remove empty html tags and duplicated linebreaks and returns
|
// remove empty html tags and duplicated linebreaks and returns
|
||||||
@ -100,10 +121,7 @@ function clean (dirty) {
|
|||||||
// remove all tags with "space only"
|
// remove all tags with "space only"
|
||||||
.replace(/<[a-z-]+>[\s]+<\/[a-z-]+>/gim, '')
|
.replace(/<[a-z-]+>[\s]+<\/[a-z-]+>/gim, '')
|
||||||
// remove all iframes
|
// remove all iframes
|
||||||
.replace(
|
.replace(/(<iframe(?!.*?src=(['"]).*?\2)[^>]*)(>)[^>]*\/*>/gim, '')
|
||||||
/(<iframe(?!.*?src=(['"]).*?\2)[^>]*)(>)[^>]*\/*>/gim,
|
|
||||||
''
|
|
||||||
)
|
|
||||||
.replace(/[\n]{3,}/gim, '\n\n')
|
.replace(/[\n]{3,}/gim, '\n\n')
|
||||||
.replace(/(\r\n|\n\r|\r|\n)/g, '<br>$1')
|
.replace(/(\r\n|\n\r|\r|\n)/g, '<br>$1')
|
||||||
|
|
||||||
@ -111,15 +129,9 @@ function clean (dirty) {
|
|||||||
// limit linebreaks to max 2 (equivalent to html "br" linebreak)
|
// limit linebreaks to max 2 (equivalent to html "br" linebreak)
|
||||||
.replace(/(<br ?\/?>\s*){2,}/gim, '<br>')
|
.replace(/(<br ?\/?>\s*){2,}/gim, '<br>')
|
||||||
// remove additional linebreaks after p tags
|
// remove additional linebreaks after p tags
|
||||||
.replace(
|
.replace(/<\/(p|div|th|tr)>\s*(<br ?\/?>\s*)+\s*<(p|div|th|tr)>/gim, '</p><p>')
|
||||||
/<\/(p|div|th|tr)>\s*(<br ?\/?>\s*)+\s*<(p|div|th|tr)>/gim,
|
|
||||||
'</p><p>'
|
|
||||||
)
|
|
||||||
// remove additional linebreaks inside p tags
|
// remove additional linebreaks inside p tags
|
||||||
.replace(
|
.replace(/<[a-z-]+>(<[a-z-]+>)*\s*(<br ?\/?>\s*)+\s*(<\/[a-z-]+>)*<\/[a-z-]+>/gim, '')
|
||||||
/<[a-z-]+>(<[a-z-]+>)*\s*(<br ?\/?>\s*)+\s*(<\/[a-z-]+>)*<\/[a-z-]+>/gim,
|
|
||||||
''
|
|
||||||
)
|
|
||||||
// remove additional linebreaks when first child inside p tags
|
// remove additional linebreaks when first child inside p tags
|
||||||
.replace(/<p>(\s*<br ?\/?>\s*)+/gim, '<p>')
|
.replace(/<p>(\s*<br ?\/?>\s*)+/gim, '<p>')
|
||||||
// remove additional linebreaks when last child inside p tags
|
// remove additional linebreaks when last child inside p tags
|
||||||
@ -138,5 +150,5 @@ export default {
|
|||||||
Query: async (resolve, root, args, context, info) => {
|
Query: async (resolve, root, args, context, info) => {
|
||||||
const result = await resolve(root, args, context, info)
|
const result = await resolve(root, args, context, info)
|
||||||
return walkRecursive(result, fields, clean)
|
return walkRecursive(result, fields, clean)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
|
|
||||||
import faker from 'faker'
|
import faker from 'faker'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
User: () => ({
|
User: () => ({
|
||||||
name: () => `${faker.name.firstName()} ${faker.name.lastName()}`,
|
name: () => `${faker.name.firstName()} ${faker.name.lastName()}`,
|
||||||
email: () => `${faker.internet.email()}`
|
email: () => `${faker.internet.email()}`,
|
||||||
}),
|
}),
|
||||||
Post: () => ({
|
Post: () => ({
|
||||||
title: () => faker.lorem.lines(1),
|
title: () => faker.lorem.lines(1),
|
||||||
slug: () => faker.lorem.slug(3),
|
slug: () => faker.lorem.slug(3),
|
||||||
content: () => faker.lorem.paragraphs(5),
|
content: () => faker.lorem.paragraphs(5),
|
||||||
contentExcerpt: () => faker.lorem.paragraphs(1)
|
contentExcerpt: () => faker.lorem.paragraphs(1),
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,17 +10,17 @@ describe('badges', () => {
|
|||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
email: 'user@example.org',
|
email: 'user@example.org',
|
||||||
role: 'user',
|
role: 'user',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u2',
|
id: 'u2',
|
||||||
role: 'moderator',
|
role: 'moderator',
|
||||||
email: 'moderator@example.org'
|
email: 'moderator@example.org',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u3',
|
id: 'u3',
|
||||||
role: 'admin',
|
role: 'admin',
|
||||||
email: 'admin@example.org'
|
email: 'admin@example.org',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ describe('badges', () => {
|
|||||||
key: 'indiegogo_en_racoon',
|
key: 'indiegogo_en_racoon',
|
||||||
type: 'crowdfunding',
|
type: 'crowdfunding',
|
||||||
status: 'permanent',
|
status: 'permanent',
|
||||||
icon: '/img/badges/indiegogo_en_racoon.svg'
|
icon: '/img/badges/indiegogo_en_racoon.svg',
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutation = `
|
const mutation = `
|
||||||
@ -58,9 +58,7 @@ describe('badges', () => {
|
|||||||
describe('unauthenticated', () => {
|
describe('unauthenticated', () => {
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -76,8 +74,8 @@ describe('badges', () => {
|
|||||||
id: 'b1',
|
id: 'b1',
|
||||||
key: 'indiegogo_en_racoon',
|
key: 'indiegogo_en_racoon',
|
||||||
status: 'permanent',
|
status: 'permanent',
|
||||||
type: 'crowdfunding'
|
type: 'crowdfunding',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -90,9 +88,7 @@ describe('badges', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -104,7 +100,7 @@ describe('badges', () => {
|
|||||||
})
|
})
|
||||||
const variables = {
|
const variables = {
|
||||||
id: 'b1',
|
id: 'b1',
|
||||||
key: 'whatever'
|
key: 'whatever',
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutation = `
|
const mutation = `
|
||||||
@ -119,9 +115,7 @@ describe('badges', () => {
|
|||||||
describe('unauthenticated', () => {
|
describe('unauthenticated', () => {
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -132,9 +126,7 @@ describe('badges', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -147,8 +139,8 @@ describe('badges', () => {
|
|||||||
const expected = {
|
const expected = {
|
||||||
UpdateBadge: {
|
UpdateBadge: {
|
||||||
id: 'b1',
|
id: 'b1',
|
||||||
key: 'whatever'
|
key: 'whatever',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -161,7 +153,7 @@ describe('badges', () => {
|
|||||||
await factory.create('Badge', { id: 'b1' })
|
await factory.create('Badge', { id: 'b1' })
|
||||||
})
|
})
|
||||||
const variables = {
|
const variables = {
|
||||||
id: 'b1'
|
id: 'b1',
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutation = `
|
const mutation = `
|
||||||
@ -175,9 +167,7 @@ describe('badges', () => {
|
|||||||
describe('unauthenticated', () => {
|
describe('unauthenticated', () => {
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -188,9 +178,7 @@ describe('badges', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -202,8 +190,8 @@ describe('badges', () => {
|
|||||||
it('deletes a badge', async () => {
|
it('deletes a badge', async () => {
|
||||||
const expected = {
|
const expected = {
|
||||||
DeleteBadge: {
|
DeleteBadge: {
|
||||||
id: 'b1'
|
id: 'b1',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -23,11 +23,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const postQueryRes = await session.run(`
|
const postQueryRes = await session.run(
|
||||||
|
`
|
||||||
MATCH (post:Post {id: $postId})
|
MATCH (post:Post {id: $postId})
|
||||||
RETURN post`, {
|
RETURN post`,
|
||||||
postId
|
{
|
||||||
}
|
postId,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
const [post] = postQueryRes.records.map(record => {
|
const [post] = postQueryRes.records.map(record => {
|
||||||
return record.get('post')
|
return record.get('post')
|
||||||
@ -38,18 +40,20 @@ export default {
|
|||||||
}
|
}
|
||||||
const comment = await neo4jgraphql(object, params, context, resolveInfo, false)
|
const comment = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
|
|
||||||
await session.run(`
|
await session.run(
|
||||||
|
`
|
||||||
MATCH (post:Post {id: $postId}), (comment:Comment {id: $commentId}), (author:User {id: $userId})
|
MATCH (post:Post {id: $postId}), (comment:Comment {id: $commentId}), (author:User {id: $userId})
|
||||||
MERGE (post)<-[:COMMENTS]-(comment)<-[:WROTE]-(author)
|
MERGE (post)<-[:COMMENTS]-(comment)<-[:WROTE]-(author)
|
||||||
RETURN post`, {
|
RETURN post`,
|
||||||
userId: context.user.id,
|
{
|
||||||
postId,
|
userId: context.user.id,
|
||||||
commentId: comment.id
|
postId,
|
||||||
}
|
commentId: comment.id,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return comment
|
return comment
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ let createCommentVariablesWithNonExistentPost
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -47,10 +47,12 @@ describe('CreateComment', () => {
|
|||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
postId: 'p1',
|
postId: 'p1',
|
||||||
content: 'I\'m not authorised to comment'
|
content: "I'm not authorised to comment",
|
||||||
}
|
}
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow('Not Authorised')
|
await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow(
|
||||||
|
'Not Authorised',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -61,12 +63,12 @@ describe('CreateComment', () => {
|
|||||||
client = new GraphQLClient(host, { headers })
|
client = new GraphQLClient(host, { headers })
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
postId: 'p1',
|
postId: 'p1',
|
||||||
content: 'I\'m authorised to comment'
|
content: "I'm authorised to comment",
|
||||||
}
|
}
|
||||||
createPostVariables = {
|
createPostVariables = {
|
||||||
id: 'p1',
|
id: 'p1',
|
||||||
title: 'post to comment on',
|
title: 'post to comment on',
|
||||||
content: 'please comment on me'
|
content: 'please comment on me',
|
||||||
}
|
}
|
||||||
await client.request(createPostMutation, createPostVariables)
|
await client.request(createPostMutation, createPostVariables)
|
||||||
})
|
})
|
||||||
@ -74,11 +76,13 @@ describe('CreateComment', () => {
|
|||||||
it('creates a comment', async () => {
|
it('creates a comment', async () => {
|
||||||
const expected = {
|
const expected = {
|
||||||
CreateComment: {
|
CreateComment: {
|
||||||
content: 'I\'m authorised to comment'
|
content: "I'm authorised to comment",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariables)).resolves.toMatchObject(expected)
|
await expect(
|
||||||
|
client.request(createCommentMutation, createCommentVariables),
|
||||||
|
).resolves.toMatchObject(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('assigns the authenticated user as author', async () => {
|
it('assigns the authenticated user as author', async () => {
|
||||||
@ -92,86 +96,96 @@ describe('CreateComment', () => {
|
|||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
expect(User).toEqual([ { comments: [ { content: 'I\'m authorised to comment' } ] } ])
|
expect(User).toEqual([{ comments: [{ content: "I'm authorised to comment" }] }])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throw an error if an empty string is sent from the editor as content', async () => {
|
it('throw an error if an empty string is sent from the editor as content', async () => {
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
postId: 'p1',
|
postId: 'p1',
|
||||||
content: '<p></p>'
|
content: '<p></p>',
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariables))
|
await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow(
|
||||||
.rejects.toThrow('Comment must be at least 1 character long!')
|
'Comment must be at least 1 character long!',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error if a comment sent from the editor does not contain a single character', async () => {
|
it('throws an error if a comment sent from the editor does not contain a single character', async () => {
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
postId: 'p1',
|
postId: 'p1',
|
||||||
content: '<p> </p>'
|
content: '<p> </p>',
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariables))
|
await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow(
|
||||||
.rejects.toThrow('Comment must be at least 1 character long!')
|
'Comment must be at least 1 character long!',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error if postId is sent as an empty string', async () => {
|
it('throws an error if postId is sent as an empty string', async () => {
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
postId: 'p1',
|
postId: 'p1',
|
||||||
content: ''
|
content: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariables))
|
await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow(
|
||||||
.rejects.toThrow('Comment must be at least 1 character long!')
|
'Comment must be at least 1 character long!',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error if content is sent as an string of empty characters', async () => {
|
it('throws an error if content is sent as an string of empty characters', async () => {
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
postId: 'p1',
|
postId: 'p1',
|
||||||
content: ' '
|
content: ' ',
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariables))
|
await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow(
|
||||||
.rejects.toThrow('Comment must be at least 1 character long!')
|
'Comment must be at least 1 character long!',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error if postId is sent as an empty string', async () => {
|
it('throws an error if postId is sent as an empty string', async () => {
|
||||||
createCommentVariablesSansPostId = {
|
createCommentVariablesSansPostId = {
|
||||||
postId: '',
|
postId: '',
|
||||||
content: 'this comment should not be created'
|
content: 'this comment should not be created',
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariablesSansPostId))
|
await expect(
|
||||||
.rejects.toThrow('Comment cannot be created without a post!')
|
client.request(createCommentMutation, createCommentVariablesSansPostId),
|
||||||
|
).rejects.toThrow('Comment cannot be created without a post!')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error if postId is sent as an string of empty characters', async () => {
|
it('throws an error if postId is sent as an string of empty characters', async () => {
|
||||||
createCommentVariablesSansPostId = {
|
createCommentVariablesSansPostId = {
|
||||||
postId: ' ',
|
postId: ' ',
|
||||||
content: 'this comment should not be created'
|
content: 'this comment should not be created',
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariablesSansPostId))
|
await expect(
|
||||||
.rejects.toThrow('Comment cannot be created without a post!')
|
client.request(createCommentMutation, createCommentVariablesSansPostId),
|
||||||
|
).rejects.toThrow('Comment cannot be created without a post!')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error if the post does not exist in the database', async () => {
|
it('throws an error if the post does not exist in the database', async () => {
|
||||||
createCommentVariablesWithNonExistentPost = {
|
createCommentVariablesWithNonExistentPost = {
|
||||||
postId: 'p2',
|
postId: 'p2',
|
||||||
content: 'comment should not be created cause the post doesn\'t exist'
|
content: "comment should not be created cause the post doesn't exist",
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(client.request(createCommentMutation, createCommentVariablesWithNonExistentPost))
|
await expect(
|
||||||
.rejects.toThrow('Comment cannot be created without a post!')
|
client.request(createCommentMutation, createCommentVariablesWithNonExistentPost),
|
||||||
|
).rejects.toThrow('Comment cannot be created without a post!')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not create the comment with the postId as an attribute', async () => {
|
it('does not create the comment with the postId as an attribute', async () => {
|
||||||
const commentQueryVariablesByContent = {
|
const commentQueryVariablesByContent = {
|
||||||
content: 'I\'m authorised to comment'
|
content: "I'm authorised to comment",
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.request(createCommentMutation, createCommentVariables)
|
await client.request(createCommentMutation, createCommentVariables)
|
||||||
const { Comment } = await client.request(commentQueryForPostId, commentQueryVariablesByContent)
|
const { Comment } = await client.request(
|
||||||
|
commentQueryForPostId,
|
||||||
|
commentQueryVariablesByContent,
|
||||||
|
)
|
||||||
expect(Comment).toEqual([{ postId: null }])
|
expect(Comment).toEqual([{ postId: null }])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,8 +12,8 @@ export default {
|
|||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
userId: context.user.id
|
userId: context.user.id,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const [isFollowed] = transactionRes.records.map(record => {
|
const [isFollowed] = transactionRes.records.map(record => {
|
||||||
@ -37,8 +37,8 @@ export default {
|
|||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
userId: context.user.id
|
userId: context.user.id,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
const [isFollowed] = transactionRes.records.map(record => {
|
const [isFollowed] = transactionRes.records.map(record => {
|
||||||
return record.get('isFollowed')
|
return record.get('isFollowed')
|
||||||
@ -46,6 +46,6 @@ export default {
|
|||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return isFollowed
|
return isFollowed
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,12 @@ const factory = Factory()
|
|||||||
let clientUser1
|
let clientUser1
|
||||||
let headersUser1
|
let headersUser1
|
||||||
|
|
||||||
const mutationFollowUser = (id) => `
|
const mutationFollowUser = id => `
|
||||||
mutation {
|
mutation {
|
||||||
follow(id: "${id}", type: User)
|
follow(id: "${id}", type: User)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const mutationUnfollowUser = (id) => `
|
const mutationUnfollowUser = id => `
|
||||||
mutation {
|
mutation {
|
||||||
unfollow(id: "${id}", type: User)
|
unfollow(id: "${id}", type: User)
|
||||||
}
|
}
|
||||||
@ -21,12 +21,12 @@ beforeEach(async () => {
|
|||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u1',
|
id: 'u1',
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u2',
|
id: 'u2',
|
||||||
email: 'test2@example.org',
|
email: 'test2@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
|
|
||||||
headersUser1 = await login({ email: 'test@example.org', password: '1234' })
|
headersUser1 = await login({ email: 'test@example.org', password: '1234' })
|
||||||
@ -43,18 +43,14 @@ describe('follow', () => {
|
|||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
let client
|
let client
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutationFollowUser('u2'))).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutationFollowUser('u2'))
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('I can follow another user', async () => {
|
it('I can follow another user', async () => {
|
||||||
const res = await clientUser1.request(
|
const res = await clientUser1.request(mutationFollowUser('u2'))
|
||||||
mutationFollowUser('u2')
|
|
||||||
)
|
|
||||||
const expected = {
|
const expected = {
|
||||||
follow: true
|
follow: true,
|
||||||
}
|
}
|
||||||
expect(res).toMatchObject(expected)
|
expect(res).toMatchObject(expected)
|
||||||
|
|
||||||
@ -65,20 +61,16 @@ describe('follow', () => {
|
|||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
followedBy: [
|
followedBy: [{ id: 'u1' }],
|
||||||
{ id: 'u1' }
|
followedByCurrentUser: true,
|
||||||
],
|
|
||||||
followedByCurrentUser: true
|
|
||||||
}
|
}
|
||||||
expect(User[0]).toMatchObject(expected2)
|
expect(User[0]).toMatchObject(expected2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('I can`t follow myself', async () => {
|
it('I can`t follow myself', async () => {
|
||||||
const res = await clientUser1.request(
|
const res = await clientUser1.request(mutationFollowUser('u1'))
|
||||||
mutationFollowUser('u1')
|
|
||||||
)
|
|
||||||
const expected = {
|
const expected = {
|
||||||
follow: false
|
follow: false,
|
||||||
}
|
}
|
||||||
expect(res).toMatchObject(expected)
|
expect(res).toMatchObject(expected)
|
||||||
|
|
||||||
@ -90,7 +82,7 @@ describe('follow', () => {
|
|||||||
}`)
|
}`)
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
followedBy: [],
|
followedBy: [],
|
||||||
followedByCurrentUser: false
|
followedByCurrentUser: false,
|
||||||
}
|
}
|
||||||
expect(User[0]).toMatchObject(expected2)
|
expect(User[0]).toMatchObject(expected2)
|
||||||
})
|
})
|
||||||
@ -99,26 +91,20 @@ describe('follow', () => {
|
|||||||
describe('unauthenticated follow', () => {
|
describe('unauthenticated follow', () => {
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
// follow
|
// follow
|
||||||
await clientUser1.request(
|
await clientUser1.request(mutationFollowUser('u2'))
|
||||||
mutationFollowUser('u2')
|
|
||||||
)
|
|
||||||
// unfollow
|
// unfollow
|
||||||
let client
|
let client
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutationUnfollowUser('u2'))).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutationUnfollowUser('u2'))
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('I can unfollow a user', async () => {
|
it('I can unfollow a user', async () => {
|
||||||
// follow
|
// follow
|
||||||
await clientUser1.request(
|
await clientUser1.request(mutationFollowUser('u2'))
|
||||||
mutationFollowUser('u2')
|
|
||||||
)
|
|
||||||
// unfollow
|
// unfollow
|
||||||
const expected = {
|
const expected = {
|
||||||
unfollow: true
|
unfollow: true,
|
||||||
}
|
}
|
||||||
const res = await clientUser1.request(mutationUnfollowUser('u2'))
|
const res = await clientUser1.request(mutationUnfollowUser('u2'))
|
||||||
expect(res).toMatchObject(expected)
|
expect(res).toMatchObject(expected)
|
||||||
@ -131,7 +117,7 @@ describe('follow', () => {
|
|||||||
}`)
|
}`)
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
followedBy: [],
|
followedBy: [],
|
||||||
followedByCurrentUser: false
|
followedByCurrentUser: false,
|
||||||
}
|
}
|
||||||
expect(User[0]).toMatchObject(expected2)
|
expect(User[0]).toMatchObject(expected2)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export default {
|
|||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const res = await session.run(cypher, { id, userId })
|
const res = await session.run(cypher, { id, userId })
|
||||||
session.close()
|
session.close()
|
||||||
const [resource] = res.records.map((record) => {
|
const [resource] = res.records.map(record => {
|
||||||
return record.get('resource')
|
return record.get('resource')
|
||||||
})
|
})
|
||||||
if (!resource) return null
|
if (!resource) return null
|
||||||
@ -31,11 +31,11 @@ export default {
|
|||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const res = await session.run(cypher, { id })
|
const res = await session.run(cypher, { id })
|
||||||
session.close()
|
session.close()
|
||||||
const [resource] = res.records.map((record) => {
|
const [resource] = res.records.map(record => {
|
||||||
return record.get('resource')
|
return record.get('resource')
|
||||||
})
|
})
|
||||||
if (!resource) return null
|
if (!resource) return null
|
||||||
return resource.id
|
return resource.id
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { host, login } from '../jest/helpers'
|
|||||||
const factory = Factory()
|
const factory = Factory()
|
||||||
let client
|
let client
|
||||||
|
|
||||||
const setupAuthenticateClient = (params) => {
|
const setupAuthenticateClient = params => {
|
||||||
const authenticateClient = async () => {
|
const authenticateClient = async () => {
|
||||||
await factory.create('User', params)
|
await factory.create('User', params)
|
||||||
const headers = await login(params)
|
const headers = await login(params)
|
||||||
@ -46,7 +46,7 @@ describe('disable', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// our defaul set of variables
|
// our defaul set of variables
|
||||||
variables = {
|
variables = {
|
||||||
id: 'blabla'
|
id: 'blabla',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ describe('disable', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
authenticateClient = setupAuthenticateClient({
|
authenticateClient = setupAuthenticateClient({
|
||||||
email: 'user@example.org',
|
email: 'user@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -78,19 +78,17 @@ describe('disable', () => {
|
|||||||
id: 'u7',
|
id: 'u7',
|
||||||
email: 'moderator@example.org',
|
email: 'moderator@example.org',
|
||||||
password: '1234',
|
password: '1234',
|
||||||
role: 'moderator'
|
role: 'moderator',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('on something that is not a (Comment|Post|User) ', () => {
|
describe('on something that is not a (Comment|Post|User) ', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
variables = {
|
variables = {
|
||||||
id: 't23'
|
id: 't23',
|
||||||
}
|
}
|
||||||
createResource = () => {
|
createResource = () => {
|
||||||
return Promise.all([
|
return Promise.all([factory.create('Tag', { id: 't23' })])
|
||||||
factory.create('Tag', { id: 't23' })
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -104,21 +102,28 @@ describe('disable', () => {
|
|||||||
describe('on a comment', () => {
|
describe('on a comment', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
variables = {
|
variables = {
|
||||||
id: 'c47'
|
id: 'c47',
|
||||||
}
|
}
|
||||||
createPostVariables = {
|
createPostVariables = {
|
||||||
id: 'p3',
|
id: 'p3',
|
||||||
title: 'post to comment on',
|
title: 'post to comment on',
|
||||||
content: 'please comment on me'
|
content: 'please comment on me',
|
||||||
}
|
}
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
id: 'c47',
|
id: 'c47',
|
||||||
postId: 'p3',
|
postId: 'p3',
|
||||||
content: 'this comment was created for this post'
|
content: 'this comment was created for this post',
|
||||||
}
|
}
|
||||||
createResource = async () => {
|
createResource = async () => {
|
||||||
await factory.create('User', { id: 'u45', email: 'commenter@example.org', password: '1234' })
|
await factory.create('User', {
|
||||||
const asAuthenticatedUser = await factory.authenticateAs({ email: 'commenter@example.org', password: '1234' })
|
id: 'u45',
|
||||||
|
email: 'commenter@example.org',
|
||||||
|
password: '1234',
|
||||||
|
})
|
||||||
|
const asAuthenticatedUser = await factory.authenticateAs({
|
||||||
|
email: 'commenter@example.org',
|
||||||
|
password: '1234',
|
||||||
|
})
|
||||||
await asAuthenticatedUser.create('Post', createPostVariables)
|
await asAuthenticatedUser.create('Post', createPostVariables)
|
||||||
await asAuthenticatedUser.create('Comment', createCommentVariables)
|
await asAuthenticatedUser.create('Comment', createCommentVariables)
|
||||||
}
|
}
|
||||||
@ -135,41 +140,39 @@ describe('disable', () => {
|
|||||||
const expected = { Comment: [{ id: 'c47', disabledBy: { id: 'u7' } }] }
|
const expected = { Comment: [{ id: 'c47', disabledBy: { id: 'u7' } }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Comment { id, disabledBy { id } } }')).resolves.toEqual(
|
||||||
'{ Comment { id, disabledBy { id } } }'
|
before,
|
||||||
)).resolves.toEqual(before)
|
)
|
||||||
await action()
|
await action()
|
||||||
await expect(client.request(
|
await expect(
|
||||||
'{ Comment(disabled: true) { id, disabledBy { id } } }'
|
client.request('{ Comment(disabled: true) { id, disabledBy { id } } }'),
|
||||||
)).resolves.toEqual(expected)
|
).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates .disabled on comment', async () => {
|
it('updates .disabled on comment', async () => {
|
||||||
const before = { Comment: [ { id: 'c47', disabled: false } ] }
|
const before = { Comment: [{ id: 'c47', disabled: false }] }
|
||||||
const expected = { Comment: [ { id: 'c47', disabled: true } ] }
|
const expected = { Comment: [{ id: 'c47', disabled: true }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Comment { id disabled } }')).resolves.toEqual(before)
|
||||||
'{ Comment { id disabled } }'
|
|
||||||
)).resolves.toEqual(before)
|
|
||||||
await action()
|
await action()
|
||||||
await expect(client.request(
|
await expect(
|
||||||
'{ Comment(disabled: true) { id disabled } }'
|
client.request('{ Comment(disabled: true) { id disabled } }'),
|
||||||
)).resolves.toEqual(expected)
|
).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('on a post', () => {
|
describe('on a post', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
variables = {
|
variables = {
|
||||||
id: 'p9'
|
id: 'p9',
|
||||||
}
|
}
|
||||||
|
|
||||||
createResource = async () => {
|
createResource = async () => {
|
||||||
await factory.create('User', { email: 'author@example.org', password: '1234' })
|
await factory.create('User', { email: 'author@example.org', password: '1234' })
|
||||||
await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
|
await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
|
||||||
await factory.create('Post', {
|
await factory.create('Post', {
|
||||||
id: 'p9' // that's the ID we will look for
|
id: 'p9', // that's the ID we will look for
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -185,27 +188,25 @@ describe('disable', () => {
|
|||||||
const expected = { Post: [{ id: 'p9', disabledBy: { id: 'u7' } }] }
|
const expected = { Post: [{ id: 'p9', disabledBy: { id: 'u7' } }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Post { id, disabledBy { id } } }')).resolves.toEqual(
|
||||||
'{ Post { id, disabledBy { id } } }'
|
before,
|
||||||
)).resolves.toEqual(before)
|
)
|
||||||
await action()
|
await action()
|
||||||
await expect(client.request(
|
await expect(
|
||||||
'{ Post(disabled: true) { id, disabledBy { id } } }'
|
client.request('{ Post(disabled: true) { id, disabledBy { id } } }'),
|
||||||
)).resolves.toEqual(expected)
|
).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates .disabled on post', async () => {
|
it('updates .disabled on post', async () => {
|
||||||
const before = { Post: [ { id: 'p9', disabled: false } ] }
|
const before = { Post: [{ id: 'p9', disabled: false }] }
|
||||||
const expected = { Post: [ { id: 'p9', disabled: true } ] }
|
const expected = { Post: [{ id: 'p9', disabled: true }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Post { id disabled } }')).resolves.toEqual(before)
|
||||||
'{ Post { id disabled } }'
|
|
||||||
)).resolves.toEqual(before)
|
|
||||||
await action()
|
await action()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Post(disabled: true) { id disabled } }')).resolves.toEqual(
|
||||||
'{ Post(disabled: true) { id disabled } }'
|
expected,
|
||||||
)).resolves.toEqual(expected)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -227,7 +228,7 @@ describe('enable', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// our defaul set of variables
|
// our defaul set of variables
|
||||||
variables = {
|
variables = {
|
||||||
id: 'blabla'
|
id: 'blabla',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -240,7 +241,7 @@ describe('enable', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
authenticateClient = setupAuthenticateClient({
|
authenticateClient = setupAuthenticateClient({
|
||||||
email: 'user@example.org',
|
email: 'user@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -254,20 +255,18 @@ describe('enable', () => {
|
|||||||
authenticateClient = setupAuthenticateClient({
|
authenticateClient = setupAuthenticateClient({
|
||||||
role: 'moderator',
|
role: 'moderator',
|
||||||
email: 'someUser@example.org',
|
email: 'someUser@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('on something that is not a (Comment|Post|User) ', () => {
|
describe('on something that is not a (Comment|Post|User) ', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
variables = {
|
variables = {
|
||||||
id: 't23'
|
id: 't23',
|
||||||
}
|
}
|
||||||
createResource = () => {
|
createResource = () => {
|
||||||
// we cannot create a :DISABLED relationship here
|
// we cannot create a :DISABLED relationship here
|
||||||
return Promise.all([
|
return Promise.all([factory.create('Tag', { id: 't23' })])
|
||||||
factory.create('Tag', { id: 't23' })
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -281,21 +280,28 @@ describe('enable', () => {
|
|||||||
describe('on a comment', () => {
|
describe('on a comment', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
variables = {
|
variables = {
|
||||||
id: 'c456'
|
id: 'c456',
|
||||||
}
|
}
|
||||||
createPostVariables = {
|
createPostVariables = {
|
||||||
id: 'p9',
|
id: 'p9',
|
||||||
title: 'post to comment on',
|
title: 'post to comment on',
|
||||||
content: 'please comment on me'
|
content: 'please comment on me',
|
||||||
}
|
}
|
||||||
createCommentVariables = {
|
createCommentVariables = {
|
||||||
id: 'c456',
|
id: 'c456',
|
||||||
postId: 'p9',
|
postId: 'p9',
|
||||||
content: 'this comment was created for this post'
|
content: 'this comment was created for this post',
|
||||||
}
|
}
|
||||||
createResource = async () => {
|
createResource = async () => {
|
||||||
await factory.create('User', { id: 'u123', email: 'author@example.org', password: '1234' })
|
await factory.create('User', {
|
||||||
const asAuthenticatedUser = await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
|
id: 'u123',
|
||||||
|
email: 'author@example.org',
|
||||||
|
password: '1234',
|
||||||
|
})
|
||||||
|
const asAuthenticatedUser = await factory.authenticateAs({
|
||||||
|
email: 'author@example.org',
|
||||||
|
password: '1234',
|
||||||
|
})
|
||||||
await asAuthenticatedUser.create('Post', createPostVariables)
|
await asAuthenticatedUser.create('Post', createPostVariables)
|
||||||
await asAuthenticatedUser.create('Comment', createCommentVariables)
|
await asAuthenticatedUser.create('Comment', createCommentVariables)
|
||||||
|
|
||||||
@ -319,41 +325,43 @@ describe('enable', () => {
|
|||||||
const expected = { Comment: [{ id: 'c456', disabledBy: null }] }
|
const expected = { Comment: [{ id: 'c456', disabledBy: null }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(
|
||||||
'{ Comment(disabled: true) { id, disabledBy { id } } }'
|
client.request('{ Comment(disabled: true) { id, disabledBy { id } } }'),
|
||||||
)).resolves.toEqual(before)
|
).resolves.toEqual(before)
|
||||||
await action()
|
await action()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Comment { id, disabledBy { id } } }')).resolves.toEqual(
|
||||||
'{ Comment { id, disabledBy { id } } }'
|
expected,
|
||||||
)).resolves.toEqual(expected)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates .disabled on post', async () => {
|
it('updates .disabled on post', async () => {
|
||||||
const before = { Comment: [ { id: 'c456', disabled: true } ] }
|
const before = { Comment: [{ id: 'c456', disabled: true }] }
|
||||||
const expected = { Comment: [ { id: 'c456', disabled: false } ] }
|
const expected = { Comment: [{ id: 'c456', disabled: false }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(
|
||||||
'{ Comment(disabled: true) { id disabled } }'
|
client.request('{ Comment(disabled: true) { id disabled } }'),
|
||||||
)).resolves.toEqual(before)
|
).resolves.toEqual(before)
|
||||||
await action() // this updates .disabled
|
await action() // this updates .disabled
|
||||||
await expect(client.request(
|
await expect(client.request('{ Comment { id disabled } }')).resolves.toEqual(expected)
|
||||||
'{ Comment { id disabled } }'
|
|
||||||
)).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('on a post', () => {
|
describe('on a post', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
variables = {
|
variables = {
|
||||||
id: 'p9'
|
id: 'p9',
|
||||||
}
|
}
|
||||||
|
|
||||||
createResource = async () => {
|
createResource = async () => {
|
||||||
await factory.create('User', { id: 'u123', email: 'author@example.org', password: '1234' })
|
await factory.create('User', {
|
||||||
|
id: 'u123',
|
||||||
|
email: 'author@example.org',
|
||||||
|
password: '1234',
|
||||||
|
})
|
||||||
await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
|
await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
|
||||||
await factory.create('Post', {
|
await factory.create('Post', {
|
||||||
id: 'p9' // that's the ID we will look for
|
id: 'p9', // that's the ID we will look for
|
||||||
})
|
})
|
||||||
|
|
||||||
const disableMutation = `
|
const disableMutation = `
|
||||||
@ -376,27 +384,25 @@ describe('enable', () => {
|
|||||||
const expected = { Post: [{ id: 'p9', disabledBy: null }] }
|
const expected = { Post: [{ id: 'p9', disabledBy: null }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(
|
||||||
'{ Post(disabled: true) { id, disabledBy { id } } }'
|
client.request('{ Post(disabled: true) { id, disabledBy { id } } }'),
|
||||||
)).resolves.toEqual(before)
|
).resolves.toEqual(before)
|
||||||
await action()
|
await action()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Post { id, disabledBy { id } } }')).resolves.toEqual(
|
||||||
'{ Post { id, disabledBy { id } } }'
|
expected,
|
||||||
)).resolves.toEqual(expected)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates .disabled on post', async () => {
|
it('updates .disabled on post', async () => {
|
||||||
const before = { Post: [ { id: 'p9', disabled: true } ] }
|
const before = { Post: [{ id: 'p9', disabled: true }] }
|
||||||
const expected = { Post: [ { id: 'p9', disabled: false } ] }
|
const expected = { Post: [{ id: 'p9', disabled: false }] }
|
||||||
|
|
||||||
await setup()
|
await setup()
|
||||||
await expect(client.request(
|
await expect(client.request('{ Post(disabled: true) { id disabled } }')).resolves.toEqual(
|
||||||
'{ Post(disabled: true) { id disabled } }'
|
before,
|
||||||
)).resolves.toEqual(before)
|
)
|
||||||
await action() // this updates .disabled
|
await action() // this updates .disabled
|
||||||
await expect(client.request(
|
await expect(client.request('{ Post { id disabled } }')).resolves.toEqual(expected)
|
||||||
'{ Post { id disabled } }'
|
|
||||||
)).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,11 +4,11 @@ export default {
|
|||||||
Query: {
|
Query: {
|
||||||
Notification: (object, params, context, resolveInfo) => {
|
Notification: (object, params, context, resolveInfo) => {
|
||||||
return neo4jgraphql(object, params, context, resolveInfo, false)
|
return neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
UpdateNotification: (object, params, context, resolveInfo) => {
|
UpdateNotification: (object, params, context, resolveInfo) => {
|
||||||
return neo4jgraphql(object, params, context, resolveInfo, false)
|
return neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import Factory from '../seed/factories'
|
import Factory from '../seed/factories'
|
||||||
import { GraphQLClient } from 'graphql-request'
|
import { GraphQLClient } from 'graphql-request'
|
||||||
import { host, login } from '../jest/helpers'
|
import { host, login } from '../jest/helpers'
|
||||||
@ -8,7 +7,7 @@ let client
|
|||||||
let userParams = {
|
let userParams = {
|
||||||
id: 'you',
|
id: 'you',
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -49,12 +48,12 @@ describe('currentUser { notifications }', () => {
|
|||||||
const neighborParams = {
|
const neighborParams = {
|
||||||
email: 'neighbor@example.org',
|
email: 'neighbor@example.org',
|
||||||
password: '1234',
|
password: '1234',
|
||||||
id: 'neighbor'
|
id: 'neighbor',
|
||||||
}
|
}
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
factory.create('User', neighborParams),
|
factory.create('User', neighborParams),
|
||||||
factory.create('Notification', { id: 'not-for-you' }),
|
factory.create('Notification', { id: 'not-for-you' }),
|
||||||
factory.create('Notification', { id: 'already-seen', read: true })
|
factory.create('Notification', { id: 'already-seen', read: true }),
|
||||||
])
|
])
|
||||||
await factory.create('Notification', { id: 'unseen' })
|
await factory.create('Notification', { id: 'unseen' })
|
||||||
await factory.authenticateAs(neighborParams)
|
await factory.authenticateAs(neighborParams)
|
||||||
@ -65,7 +64,7 @@ describe('currentUser { notifications }', () => {
|
|||||||
factory.relate('Notification', 'User', { from: 'unseen', to: 'you' }),
|
factory.relate('Notification', 'User', { from: 'unseen', to: 'you' }),
|
||||||
factory.relate('Notification', 'Post', { from: 'p1', to: 'unseen' }),
|
factory.relate('Notification', 'Post', { from: 'p1', to: 'unseen' }),
|
||||||
factory.relate('Notification', 'User', { from: 'already-seen', to: 'you' }),
|
factory.relate('Notification', 'User', { from: 'already-seen', to: 'you' }),
|
||||||
factory.relate('Notification', 'Post', { from: 'p1', to: 'already-seen' })
|
factory.relate('Notification', 'Post', { from: 'p1', to: 'already-seen' }),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -84,10 +83,8 @@ describe('currentUser { notifications }', () => {
|
|||||||
it('returns only unread notifications of current user', async () => {
|
it('returns only unread notifications of current user', async () => {
|
||||||
const expected = {
|
const expected = {
|
||||||
currentUser: {
|
currentUser: {
|
||||||
notifications: [
|
notifications: [{ id: 'unseen', post: { id: 'p1' } }],
|
||||||
{ id: 'unseen', post: { id: 'p1' } }
|
},
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await expect(client.request(query, variables)).resolves.toEqual(expected)
|
await expect(client.request(query, variables)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -109,9 +106,9 @@ describe('currentUser { notifications }', () => {
|
|||||||
currentUser: {
|
currentUser: {
|
||||||
notifications: [
|
notifications: [
|
||||||
{ id: 'unseen', post: { id: 'p1' } },
|
{ id: 'unseen', post: { id: 'p1' } },
|
||||||
{ id: 'already-seen', post: { id: 'p1' } }
|
{ id: 'already-seen', post: { id: 'p1' } },
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(query, variables)).resolves.toEqual(expected)
|
await expect(client.request(query, variables)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -136,7 +133,7 @@ describe('UpdateNotification', () => {
|
|||||||
id: 'mentioned-1',
|
id: 'mentioned-1',
|
||||||
email: 'mentioned@example.org',
|
email: 'mentioned@example.org',
|
||||||
password: '1234',
|
password: '1234',
|
||||||
slug: 'mentioned'
|
slug: 'mentioned',
|
||||||
}
|
}
|
||||||
await factory.create('User', mentionedParams)
|
await factory.create('User', mentionedParams)
|
||||||
await factory.create('Notification', { id: 'to-be-updated' })
|
await factory.create('Notification', { id: 'to-be-updated' })
|
||||||
@ -144,7 +141,7 @@ describe('UpdateNotification', () => {
|
|||||||
await factory.create('Post', { id: 'p1' })
|
await factory.create('Post', { id: 'p1' })
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
factory.relate('Notification', 'User', { from: 'to-be-updated', to: 'mentioned-1' }),
|
factory.relate('Notification', 'User', { from: 'to-be-updated', to: 'mentioned-1' }),
|
||||||
factory.relate('Notification', 'Post', { from: 'p1', to: 'to-be-updated' })
|
factory.relate('Notification', 'Post', { from: 'p1', to: 'to-be-updated' }),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -8,15 +8,16 @@ export default {
|
|||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
await session.run(
|
await session.run(
|
||||||
'MATCH (author:User {id: $userId}), (post:Post {id: $postId}) ' +
|
'MATCH (author:User {id: $userId}), (post:Post {id: $postId}) ' +
|
||||||
'MERGE (post)<-[:WROTE]-(author) ' +
|
'MERGE (post)<-[:WROTE]-(author) ' +
|
||||||
'RETURN author', {
|
'RETURN author',
|
||||||
|
{
|
||||||
userId: context.user.id,
|
userId: context.user.id,
|
||||||
postId: result.id
|
postId: result.id,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ let client
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -47,22 +47,25 @@ describe('CreatePost', () => {
|
|||||||
const expected = {
|
const expected = {
|
||||||
CreatePost: {
|
CreatePost: {
|
||||||
title: 'I am a title',
|
title: 'I am a title',
|
||||||
content: 'Some content'
|
content: 'Some content',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(mutation)).resolves.toMatchObject(expected)
|
await expect(client.request(mutation)).resolves.toMatchObject(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('assigns the authenticated user as author', async () => {
|
it('assigns the authenticated user as author', async () => {
|
||||||
await client.request(mutation)
|
await client.request(mutation)
|
||||||
const { User } = await client.request(`{
|
const { User } = await client.request(
|
||||||
|
`{
|
||||||
User(email:"test@example.org") {
|
User(email:"test@example.org") {
|
||||||
contributions {
|
contributions {
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`, { headers })
|
}`,
|
||||||
expect(User).toEqual([ { contributions: [ { title: 'I am a title' } ] } ])
|
{ headers },
|
||||||
|
)
|
||||||
|
expect(User).toEqual([{ contributions: [{ title: 'I am a title' }] }])
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('disabled and deleted', () => {
|
describe('disabled and deleted', () => {
|
||||||
@ -86,22 +89,22 @@ describe('UpdatePost', () => {
|
|||||||
|
|
||||||
let variables = {
|
let variables = {
|
||||||
id: 'p1',
|
id: 'p1',
|
||||||
content: 'New content'
|
content: 'New content',
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const asAuthor = Factory()
|
const asAuthor = Factory()
|
||||||
await asAuthor.create('User', {
|
await asAuthor.create('User', {
|
||||||
email: 'author@example.org',
|
email: 'author@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await asAuthor.authenticateAs({
|
await asAuthor.authenticateAs({
|
||||||
email: 'author@example.org',
|
email: 'author@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await asAuthor.create('Post', {
|
await asAuthor.create('Post', {
|
||||||
id: 'p1',
|
id: 'p1',
|
||||||
content: 'Old content'
|
content: 'Old content',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -149,22 +152,22 @@ describe('DeletePost', () => {
|
|||||||
`
|
`
|
||||||
|
|
||||||
let variables = {
|
let variables = {
|
||||||
id: 'p1'
|
id: 'p1',
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const asAuthor = Factory()
|
const asAuthor = Factory()
|
||||||
await asAuthor.create('User', {
|
await asAuthor.create('User', {
|
||||||
email: 'author@example.org',
|
email: 'author@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await asAuthor.authenticateAs({
|
await asAuthor.authenticateAs({
|
||||||
email: 'author@example.org',
|
email: 'author@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await asAuthor.create('Post', {
|
await asAuthor.create('Post', {
|
||||||
id: 'p1',
|
id: 'p1',
|
||||||
content: 'To be deleted'
|
content: 'To be deleted',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -7,11 +7,12 @@ export default {
|
|||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const reportData = {
|
const reportData = {
|
||||||
id: reportId,
|
id: reportId,
|
||||||
createdAt: (new Date()).toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
description: description
|
description: description,
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await session.run(`
|
const res = await session.run(
|
||||||
|
`
|
||||||
MATCH (submitter:User {id: $userId})
|
MATCH (submitter:User {id: $userId})
|
||||||
MATCH (resource {id: $resourceId})
|
MATCH (resource {id: $resourceId})
|
||||||
WHERE resource:User OR resource:Comment OR resource:Post
|
WHERE resource:User OR resource:Comment OR resource:Post
|
||||||
@ -19,11 +20,12 @@ export default {
|
|||||||
MERGE (resource)<-[:REPORTED]-(report)
|
MERGE (resource)<-[:REPORTED]-(report)
|
||||||
MERGE (report)<-[:REPORTED]-(submitter)
|
MERGE (report)<-[:REPORTED]-(submitter)
|
||||||
RETURN report, submitter, resource, labels(resource)[0] as type
|
RETURN report, submitter, resource, labels(resource)[0] as type
|
||||||
`, {
|
`,
|
||||||
resourceId: id,
|
{
|
||||||
userId: user.id,
|
resourceId: id,
|
||||||
reportData
|
userId: user.id,
|
||||||
}
|
reportData,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ export default {
|
|||||||
report: r.get('report'),
|
report: r.get('report'),
|
||||||
submitter: r.get('submitter'),
|
submitter: r.get('submitter'),
|
||||||
resource: r.get('resource'),
|
resource: r.get('resource'),
|
||||||
type: r.get('type')
|
type: r.get('type'),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!dbResponse) return null
|
if (!dbResponse) return null
|
||||||
@ -44,20 +46,20 @@ export default {
|
|||||||
comment: null,
|
comment: null,
|
||||||
user: null,
|
user: null,
|
||||||
submitter: submitter.properties,
|
submitter: submitter.properties,
|
||||||
type
|
type,
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Post':
|
case 'Post':
|
||||||
response.post = resource.properties
|
response.post = resource.properties
|
||||||
break
|
break
|
||||||
case 'Comment':
|
case 'Comment':
|
||||||
response.comment = resource.properties
|
response.comment = resource.properties
|
||||||
break
|
break
|
||||||
case 'User':
|
case 'User':
|
||||||
response.user = resource.properties
|
response.user = resource.properties
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,13 +18,13 @@ describe('report', () => {
|
|||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u1',
|
id: 'u1',
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u2',
|
id: 'u2',
|
||||||
name: 'abusive-user',
|
name: 'abusive-user',
|
||||||
role: 'user',
|
role: 'user',
|
||||||
email: 'abusive-user@example.org'
|
email: 'abusive-user@example.org',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ describe('report', () => {
|
|||||||
describe('invalid resource id', () => {
|
describe('invalid resource id', () => {
|
||||||
it('returns null', async () => {
|
it('returns null', async () => {
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: null
|
report: null,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -71,14 +71,14 @@ describe('report', () => {
|
|||||||
|
|
||||||
it('creates a report', async () => {
|
it('creates a report', async () => {
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { description: 'Violates code of conduct' }
|
report: { description: 'Violates code of conduct' },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns the submitter', async () => {
|
it('returns the submitter', async () => {
|
||||||
returnedObject = '{ submitter { email } }'
|
returnedObject = '{ submitter { email } }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { submitter: { email: 'test@example.org' } }
|
report: { submitter: { email: 'test@example.org' } },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -86,14 +86,14 @@ describe('report', () => {
|
|||||||
it('returns type "User"', async () => {
|
it('returns type "User"', async () => {
|
||||||
returnedObject = '{ type }'
|
returnedObject = '{ type }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { type: 'User' }
|
report: { type: 'User' },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns resource in user attribute', async () => {
|
it('returns resource in user attribute', async () => {
|
||||||
returnedObject = '{ user { name } }'
|
returnedObject = '{ user { name } }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { user: { name: 'abusive-user' } }
|
report: { user: { name: 'abusive-user' } },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -101,28 +101,31 @@ describe('report', () => {
|
|||||||
describe('reported resource is a post', () => {
|
describe('reported resource is a post', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await factory.authenticateAs({ email: 'test@example.org', password: '1234' })
|
await factory.authenticateAs({ email: 'test@example.org', password: '1234' })
|
||||||
await factory.create('Post', { id: 'p23', title: 'Matt and Robert having a pair-programming' })
|
await factory.create('Post', {
|
||||||
|
id: 'p23',
|
||||||
|
title: 'Matt and Robert having a pair-programming',
|
||||||
|
})
|
||||||
variables = { id: 'p23' }
|
variables = { id: 'p23' }
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns type "Post"', async () => {
|
it('returns type "Post"', async () => {
|
||||||
returnedObject = '{ type }'
|
returnedObject = '{ type }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { type: 'Post' }
|
report: { type: 'Post' },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns resource in post attribute', async () => {
|
it('returns resource in post attribute', async () => {
|
||||||
returnedObject = '{ post { title } }'
|
returnedObject = '{ post { title } }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { post: { title: 'Matt and Robert having a pair-programming' } }
|
report: { post: { title: 'Matt and Robert having a pair-programming' } },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns null in user attribute', async () => {
|
it('returns null in user attribute', async () => {
|
||||||
returnedObject = '{ user { name } }'
|
returnedObject = '{ user { name } }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { user: null }
|
report: { user: null },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -132,25 +135,32 @@ describe('report', () => {
|
|||||||
createPostVariables = {
|
createPostVariables = {
|
||||||
id: 'p1',
|
id: 'p1',
|
||||||
title: 'post to comment on',
|
title: 'post to comment on',
|
||||||
content: 'please comment on me'
|
content: 'please comment on me',
|
||||||
}
|
}
|
||||||
const asAuthenticatedUser = await factory.authenticateAs({ email: 'test@example.org', password: '1234' })
|
const asAuthenticatedUser = await factory.authenticateAs({
|
||||||
|
email: 'test@example.org',
|
||||||
|
password: '1234',
|
||||||
|
})
|
||||||
await asAuthenticatedUser.create('Post', createPostVariables)
|
await asAuthenticatedUser.create('Post', createPostVariables)
|
||||||
await asAuthenticatedUser.create('Comment', { postId: 'p1', id: 'c34', content: 'Robert getting tired.' })
|
await asAuthenticatedUser.create('Comment', {
|
||||||
|
postId: 'p1',
|
||||||
|
id: 'c34',
|
||||||
|
content: 'Robert getting tired.',
|
||||||
|
})
|
||||||
variables = { id: 'c34' }
|
variables = { id: 'c34' }
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns type "Comment"', async () => {
|
it('returns type "Comment"', async () => {
|
||||||
returnedObject = '{ type }'
|
returnedObject = '{ type }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { type: 'Comment' }
|
report: { type: 'Comment' },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns resource in comment attribute', async () => {
|
it('returns resource in comment attribute', async () => {
|
||||||
returnedObject = '{ comment { content } }'
|
returnedObject = '{ comment { content } }'
|
||||||
await expect(action()).resolves.toEqual({
|
await expect(action()).resolves.toEqual({
|
||||||
report: { comment: { content: 'Robert getting tired.' } }
|
report: { comment: { content: 'Robert getting tired.' } },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -10,8 +10,8 @@ export default {
|
|||||||
RETURN rewardedUser {.id}`,
|
RETURN rewardedUser {.id}`,
|
||||||
{
|
{
|
||||||
badgeId: fromBadgeId,
|
badgeId: fromBadgeId,
|
||||||
rewardedUserId: toUserId
|
rewardedUserId: toUserId,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const [rewardedUser] = transactionRes.records.map(record => {
|
const [rewardedUser] = transactionRes.records.map(record => {
|
||||||
@ -33,8 +33,8 @@ export default {
|
|||||||
RETURN rewardedUser {.id}`,
|
RETURN rewardedUser {.id}`,
|
||||||
{
|
{
|
||||||
badgeId: fromBadgeId,
|
badgeId: fromBadgeId,
|
||||||
rewardedUserId: toUserId
|
rewardedUserId: toUserId,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
const [rewardedUser] = transactionRes.records.map(record => {
|
const [rewardedUser] = transactionRes.records.map(record => {
|
||||||
return record.get('rewardedUser')
|
return record.get('rewardedUser')
|
||||||
@ -42,6 +42,6 @@ export default {
|
|||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return rewardedUser.id
|
return rewardedUser.id
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,24 +10,24 @@ describe('rewards', () => {
|
|||||||
id: 'u1',
|
id: 'u1',
|
||||||
role: 'user',
|
role: 'user',
|
||||||
email: 'user@example.org',
|
email: 'user@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u2',
|
id: 'u2',
|
||||||
role: 'moderator',
|
role: 'moderator',
|
||||||
email: 'moderator@example.org'
|
email: 'moderator@example.org',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u3',
|
id: 'u3',
|
||||||
role: 'admin',
|
role: 'admin',
|
||||||
email: 'admin@example.org'
|
email: 'admin@example.org',
|
||||||
})
|
})
|
||||||
await factory.create('Badge', {
|
await factory.create('Badge', {
|
||||||
id: 'b6',
|
id: 'b6',
|
||||||
key: 'indiegogo_en_rhino',
|
key: 'indiegogo_en_rhino',
|
||||||
type: 'crowdfunding',
|
type: 'crowdfunding',
|
||||||
status: 'permanent',
|
status: 'permanent',
|
||||||
icon: '/img/badges/indiegogo_en_rhino.svg'
|
icon: '/img/badges/indiegogo_en_rhino.svg',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -48,15 +48,13 @@ describe('rewards', () => {
|
|||||||
describe('unauthenticated', () => {
|
describe('unauthenticated', () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
from: 'b6',
|
from: 'b6',
|
||||||
to: 'u1'
|
to: 'u1',
|
||||||
}
|
}
|
||||||
let client
|
let client
|
||||||
|
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -70,14 +68,12 @@ describe('rewards', () => {
|
|||||||
it('rewards a badge to user', async () => {
|
it('rewards a badge to user', async () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
from: 'b6',
|
from: 'b6',
|
||||||
to: 'u1'
|
to: 'u1',
|
||||||
}
|
}
|
||||||
const expected = {
|
const expected = {
|
||||||
reward: 'u1'
|
reward: 'u1',
|
||||||
}
|
}
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
client.request(mutation, variables)
|
|
||||||
).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
it('rewards a second different badge to same user', async () => {
|
it('rewards a second different badge to same user', async () => {
|
||||||
await factory.create('Badge', {
|
await factory.create('Badge', {
|
||||||
@ -85,41 +81,37 @@ describe('rewards', () => {
|
|||||||
key: 'indiegogo_en_racoon',
|
key: 'indiegogo_en_racoon',
|
||||||
type: 'crowdfunding',
|
type: 'crowdfunding',
|
||||||
status: 'permanent',
|
status: 'permanent',
|
||||||
icon: '/img/badges/indiegogo_en_racoon.svg'
|
icon: '/img/badges/indiegogo_en_racoon.svg',
|
||||||
})
|
})
|
||||||
const variables = {
|
const variables = {
|
||||||
from: 'b1',
|
from: 'b1',
|
||||||
to: 'u1'
|
to: 'u1',
|
||||||
}
|
}
|
||||||
const expected = {
|
const expected = {
|
||||||
reward: 'u1'
|
reward: 'u1',
|
||||||
}
|
}
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
client.request(mutation, variables)
|
|
||||||
).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
it('rewards the same badge as well to another user', async () => {
|
it('rewards the same badge as well to another user', async () => {
|
||||||
const variables1 = {
|
const variables1 = {
|
||||||
from: 'b6',
|
from: 'b6',
|
||||||
to: 'u1'
|
to: 'u1',
|
||||||
}
|
}
|
||||||
await client.request(mutation, variables1)
|
await client.request(mutation, variables1)
|
||||||
|
|
||||||
const variables2 = {
|
const variables2 = {
|
||||||
from: 'b6',
|
from: 'b6',
|
||||||
to: 'u2'
|
to: 'u2',
|
||||||
}
|
}
|
||||||
const expected = {
|
const expected = {
|
||||||
reward: 'u2'
|
reward: 'u2',
|
||||||
}
|
}
|
||||||
await expect(
|
await expect(client.request(mutation, variables2)).resolves.toEqual(expected)
|
||||||
client.request(mutation, variables2)
|
|
||||||
).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
it('returns the original reward if a reward is attempted a second time', async () => {
|
it('returns the original reward if a reward is attempted a second time', async () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
from: 'b6',
|
from: 'b6',
|
||||||
to: 'u1'
|
to: 'u1',
|
||||||
}
|
}
|
||||||
await client.request(mutation, variables)
|
await client.request(mutation, variables)
|
||||||
await client.request(mutation, variables)
|
await client.request(mutation, variables)
|
||||||
@ -132,16 +124,14 @@ describe('rewards', () => {
|
|||||||
`
|
`
|
||||||
const expected = { User: [{ badgesCount: 1 }] }
|
const expected = { User: [{ badgesCount: 1 }] }
|
||||||
|
|
||||||
await expect(
|
await expect(client.request(query)).resolves.toEqual(expected)
|
||||||
client.request(query)
|
|
||||||
).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('authenticated moderator', () => {
|
describe('authenticated moderator', () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
from: 'b6',
|
from: 'b6',
|
||||||
to: 'u1'
|
to: 'u1',
|
||||||
}
|
}
|
||||||
let client
|
let client
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -151,9 +141,7 @@ describe('rewards', () => {
|
|||||||
|
|
||||||
describe('rewards bage to user', () => {
|
describe('rewards bage to user', () => {
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -165,10 +153,10 @@ describe('rewards', () => {
|
|||||||
})
|
})
|
||||||
const variables = {
|
const variables = {
|
||||||
from: 'b6',
|
from: 'b6',
|
||||||
to: 'u1'
|
to: 'u1',
|
||||||
}
|
}
|
||||||
const expected = {
|
const expected = {
|
||||||
unreward: 'u1'
|
unreward: 'u1',
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutation = `
|
const mutation = `
|
||||||
@ -185,9 +173,7 @@ describe('rewards', () => {
|
|||||||
|
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -199,17 +185,15 @@ describe('rewards', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('removes a badge from user', async () => {
|
it('removes a badge from user', async () => {
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
client.request(mutation, variables)
|
|
||||||
).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails to remove a not existing badge from user', async () => {
|
it('fails to remove a not existing badge from user', async () => {
|
||||||
await client.request(mutation, variables)
|
await client.request(mutation, variables)
|
||||||
|
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow(
|
||||||
client.request(mutation, variables)
|
"Cannot read property 'id' of undefined",
|
||||||
).rejects.toThrow('Cannot read property \'id\' of undefined')
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -222,9 +206,7 @@ describe('rewards', () => {
|
|||||||
|
|
||||||
describe('removes bage from user', () => {
|
describe('removes bage from user', () => {
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
await expect(
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutation, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,8 +12,8 @@ export default {
|
|||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
userId: context.user.id
|
userId: context.user.id,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const [isShouted] = transactionRes.records.map(record => {
|
const [isShouted] = transactionRes.records.map(record => {
|
||||||
@ -37,8 +37,8 @@ export default {
|
|||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
userId: context.user.id
|
userId: context.user.id,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
const [isShouted] = transactionRes.records.map(record => {
|
const [isShouted] = transactionRes.records.map(record => {
|
||||||
return record.get('isShouted')
|
return record.get('isShouted')
|
||||||
@ -46,6 +46,6 @@ export default {
|
|||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return isShouted
|
return isShouted
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,12 @@ const factory = Factory()
|
|||||||
let clientUser1, clientUser2
|
let clientUser1, clientUser2
|
||||||
let headersUser1, headersUser2
|
let headersUser1, headersUser2
|
||||||
|
|
||||||
const mutationShoutPost = (id) => `
|
const mutationShoutPost = id => `
|
||||||
mutation {
|
mutation {
|
||||||
shout(id: "${id}", type: Post)
|
shout(id: "${id}", type: Post)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const mutationUnshoutPost = (id) => `
|
const mutationUnshoutPost = id => `
|
||||||
mutation {
|
mutation {
|
||||||
unshout(id: "${id}", type: Post)
|
unshout(id: "${id}", type: Post)
|
||||||
}
|
}
|
||||||
@ -21,12 +21,12 @@ beforeEach(async () => {
|
|||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u1',
|
id: 'u1',
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
await factory.create('User', {
|
await factory.create('User', {
|
||||||
id: 'u2',
|
id: 'u2',
|
||||||
email: 'test2@example.org',
|
email: 'test2@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
|
|
||||||
headersUser1 = await login({ email: 'test@example.org', password: '1234' })
|
headersUser1 = await login({ email: 'test@example.org', password: '1234' })
|
||||||
@ -62,18 +62,14 @@ describe('shout', () => {
|
|||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
let client
|
let client
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutationShoutPost('p1'))).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutationShoutPost('p1'))
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('I shout a post of another user', async () => {
|
it('I shout a post of another user', async () => {
|
||||||
const res = await clientUser1.request(
|
const res = await clientUser1.request(mutationShoutPost('p2'))
|
||||||
mutationShoutPost('p2')
|
|
||||||
)
|
|
||||||
const expected = {
|
const expected = {
|
||||||
shout: true
|
shout: true,
|
||||||
}
|
}
|
||||||
expect(res).toMatchObject(expected)
|
expect(res).toMatchObject(expected)
|
||||||
|
|
||||||
@ -83,17 +79,15 @@ describe('shout', () => {
|
|||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
shoutedByCurrentUser: true
|
shoutedByCurrentUser: true,
|
||||||
}
|
}
|
||||||
expect(Post[0]).toMatchObject(expected2)
|
expect(Post[0]).toMatchObject(expected2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('I can`t shout my own post', async () => {
|
it('I can`t shout my own post', async () => {
|
||||||
const res = await clientUser1.request(
|
const res = await clientUser1.request(mutationShoutPost('p1'))
|
||||||
mutationShoutPost('p1')
|
|
||||||
)
|
|
||||||
const expected = {
|
const expected = {
|
||||||
shout: false
|
shout: false,
|
||||||
}
|
}
|
||||||
expect(res).toMatchObject(expected)
|
expect(res).toMatchObject(expected)
|
||||||
|
|
||||||
@ -103,7 +97,7 @@ describe('shout', () => {
|
|||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
shoutedByCurrentUser: false
|
shoutedByCurrentUser: false,
|
||||||
}
|
}
|
||||||
expect(Post[0]).toMatchObject(expected2)
|
expect(Post[0]).toMatchObject(expected2)
|
||||||
})
|
})
|
||||||
@ -113,25 +107,19 @@ describe('shout', () => {
|
|||||||
describe('unauthenticated shout', () => {
|
describe('unauthenticated shout', () => {
|
||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
// shout
|
// shout
|
||||||
await clientUser1.request(
|
await clientUser1.request(mutationShoutPost('p2'))
|
||||||
mutationShoutPost('p2')
|
|
||||||
)
|
|
||||||
// unshout
|
// unshout
|
||||||
let client
|
let client
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
await expect(
|
await expect(client.request(mutationUnshoutPost('p2'))).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutationUnshoutPost('p2'))
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('I unshout a post of another user', async () => {
|
it('I unshout a post of another user', async () => {
|
||||||
// shout
|
// shout
|
||||||
await clientUser1.request(
|
await clientUser1.request(mutationShoutPost('p2'))
|
||||||
mutationShoutPost('p2')
|
|
||||||
)
|
|
||||||
const expected = {
|
const expected = {
|
||||||
unshout: true
|
unshout: true,
|
||||||
}
|
}
|
||||||
// unshout
|
// unshout
|
||||||
const res = await clientUser1.request(mutationUnshoutPost('p2'))
|
const res = await clientUser1.request(mutationUnshoutPost('p2'))
|
||||||
@ -143,7 +131,7 @@ describe('shout', () => {
|
|||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
shoutedByCurrentUser: false
|
shoutedByCurrentUser: false,
|
||||||
}
|
}
|
||||||
expect(Post[0]).toMatchObject(expected2)
|
expect(Post[0]).toMatchObject(expected2)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -5,16 +5,17 @@ export default {
|
|||||||
CreateSocialMedia: async (object, params, context, resolveInfo) => {
|
CreateSocialMedia: async (object, params, context, resolveInfo) => {
|
||||||
/**
|
/**
|
||||||
* TODO?: Creates double Nodes!
|
* TODO?: Creates double Nodes!
|
||||||
*/
|
*/
|
||||||
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
await session.run(
|
await session.run(
|
||||||
`MATCH (owner:User {id: $userId}), (socialMedia:SocialMedia {id: $socialMediaId})
|
`MATCH (owner:User {id: $userId}), (socialMedia:SocialMedia {id: $socialMediaId})
|
||||||
MERGE (socialMedia)<-[:OWNED]-(owner)
|
MERGE (socialMedia)<-[:OWNED]-(owner)
|
||||||
RETURN owner`, {
|
RETURN owner`,
|
||||||
|
{
|
||||||
userId: context.user.id,
|
userId: context.user.id,
|
||||||
socialMediaId: socialMedia.id
|
socialMediaId: socialMedia.id,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
@ -24,6 +25,6 @@ export default {
|
|||||||
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
|
|
||||||
return socialMedia
|
return socialMedia
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ describe('CreateSocialMedia', () => {
|
|||||||
slug: 'matilde-hermiston',
|
slug: 'matilde-hermiston',
|
||||||
role: 'user',
|
role: 'user',
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -43,9 +43,7 @@ describe('CreateSocialMedia', () => {
|
|||||||
it('throws authorization error', async () => {
|
it('throws authorization error', async () => {
|
||||||
client = new GraphQLClient(host)
|
client = new GraphQLClient(host)
|
||||||
const variables = { url: 'http://nsosp.org' }
|
const variables = { url: 'http://nsosp.org' }
|
||||||
await expect(
|
await expect(client.request(mutationC, variables)).rejects.toThrow('Not Authorised')
|
||||||
client.request(mutationC, variables)
|
|
||||||
).rejects.toThrow('Not Authorised')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -57,14 +55,14 @@ describe('CreateSocialMedia', () => {
|
|||||||
|
|
||||||
it('creates social media with correct URL', async () => {
|
it('creates social media with correct URL', async () => {
|
||||||
const variables = { url: 'http://nsosp.org' }
|
const variables = { url: 'http://nsosp.org' }
|
||||||
await expect(
|
await expect(client.request(mutationC, variables)).resolves.toEqual(
|
||||||
client.request(mutationC, variables)
|
expect.objectContaining({
|
||||||
).resolves.toEqual(expect.objectContaining({
|
CreateSocialMedia: {
|
||||||
CreateSocialMedia: {
|
id: expect.any(String),
|
||||||
id: expect.any(String),
|
url: 'http://nsosp.org',
|
||||||
url: 'http://nsosp.org'
|
},
|
||||||
}
|
}),
|
||||||
}))
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('deletes social media', async () => {
|
it('deletes social media', async () => {
|
||||||
@ -76,26 +74,20 @@ describe('CreateSocialMedia', () => {
|
|||||||
const expected = {
|
const expected = {
|
||||||
DeleteSocialMedia: {
|
DeleteSocialMedia: {
|
||||||
id: id,
|
id: id,
|
||||||
url: 'http://nsosp.org'
|
url: 'http://nsosp.org',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(
|
await expect(client.request(mutationD, deletionVariables)).resolves.toEqual(expected)
|
||||||
client.request(mutationD, deletionVariables)
|
|
||||||
).resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('rejects empty string', async () => {
|
it('rejects empty string', async () => {
|
||||||
const variables = { url: '' }
|
const variables = { url: '' }
|
||||||
await expect(
|
await expect(client.request(mutationC, variables)).rejects.toThrow('Input is not a URL')
|
||||||
client.request(mutationC, variables)
|
|
||||||
).rejects.toThrow('Input is not a URL')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('validates URLs', async () => {
|
it('validates URLs', async () => {
|
||||||
const variables = { url: 'not-a-url' }
|
const variables = { url: 'not-a-url' }
|
||||||
await expect(
|
await expect(client.request(mutationC, variables)).rejects.toThrow('Input is not a URL')
|
||||||
client.request(mutationC, variables)
|
|
||||||
).rejects.toThrow('Input is not a URL')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,24 +1,22 @@
|
|||||||
export const query = (cypher, session) => {
|
export const query = (cypher, session) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let data = []
|
let data = []
|
||||||
session
|
session.run(cypher).subscribe({
|
||||||
.run(cypher)
|
onNext: function(record) {
|
||||||
.subscribe({
|
let item = {}
|
||||||
onNext: function (record) {
|
record.keys.forEach(key => {
|
||||||
let item = {}
|
item[key] = record.get(key)
|
||||||
record.keys.forEach(key => {
|
})
|
||||||
item[key] = record.get(key)
|
data.push(item)
|
||||||
})
|
},
|
||||||
data.push(item)
|
onCompleted: function() {
|
||||||
},
|
session.close()
|
||||||
onCompleted: function () {
|
resolve(data)
|
||||||
session.close()
|
},
|
||||||
resolve(data)
|
onError: function(error) {
|
||||||
},
|
reject(error)
|
||||||
onError: function (error) {
|
},
|
||||||
reject(error)
|
})
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const queryOne = (cypher, session) => {
|
const queryOne = (cypher, session) => {
|
||||||
@ -36,32 +34,41 @@ const queryOne = (cypher, session) => {
|
|||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
statistics: async (parent, args, { driver, user }) => {
|
statistics: async (parent, args, { driver, user }) => {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async resolve => {
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const queries = {
|
const queries = {
|
||||||
countUsers: 'MATCH (r:User) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countUsers',
|
countUsers:
|
||||||
countPosts: 'MATCH (r:Post) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countPosts',
|
'MATCH (r:User) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countUsers',
|
||||||
countComments: 'MATCH (r:Comment) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countComments',
|
countPosts:
|
||||||
countNotifications: 'MATCH (r:Notification) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countNotifications',
|
'MATCH (r:Post) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countPosts',
|
||||||
countOrganizations: 'MATCH (r:Organization) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countOrganizations',
|
countComments:
|
||||||
countProjects: 'MATCH (r:Project) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countProjects',
|
'MATCH (r:Comment) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countComments',
|
||||||
countInvites: 'MATCH (r:Invite) WHERE r.wasUsed <> true OR NOT exists(r.wasUsed) RETURN COUNT(r) AS countInvites',
|
countNotifications:
|
||||||
|
'MATCH (r:Notification) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countNotifications',
|
||||||
|
countOrganizations:
|
||||||
|
'MATCH (r:Organization) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countOrganizations',
|
||||||
|
countProjects:
|
||||||
|
'MATCH (r:Project) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countProjects',
|
||||||
|
countInvites:
|
||||||
|
'MATCH (r:Invite) WHERE r.wasUsed <> true OR NOT exists(r.wasUsed) RETURN COUNT(r) AS countInvites',
|
||||||
countFollows: 'MATCH (:User)-[r:FOLLOWS]->(:User) RETURN COUNT(r) AS countFollows',
|
countFollows: 'MATCH (:User)-[r:FOLLOWS]->(:User) RETURN COUNT(r) AS countFollows',
|
||||||
countShouts: 'MATCH (:User)-[r:SHOUTED]->(:Post) RETURN COUNT(r) AS countShouts'
|
countShouts: 'MATCH (:User)-[r:SHOUTED]->(:Post) RETURN COUNT(r) AS countShouts',
|
||||||
}
|
}
|
||||||
let data = {
|
let data = {
|
||||||
countUsers: (await queryOne(queries.countUsers, session)).countUsers.low,
|
countUsers: (await queryOne(queries.countUsers, session)).countUsers.low,
|
||||||
countPosts: (await queryOne(queries.countPosts, session)).countPosts.low,
|
countPosts: (await queryOne(queries.countPosts, session)).countPosts.low,
|
||||||
countComments: (await queryOne(queries.countComments, session)).countComments.low,
|
countComments: (await queryOne(queries.countComments, session)).countComments.low,
|
||||||
countNotifications: (await queryOne(queries.countNotifications, session)).countNotifications.low,
|
countNotifications: (await queryOne(queries.countNotifications, session))
|
||||||
countOrganizations: (await queryOne(queries.countOrganizations, session)).countOrganizations.low,
|
.countNotifications.low,
|
||||||
|
countOrganizations: (await queryOne(queries.countOrganizations, session))
|
||||||
|
.countOrganizations.low,
|
||||||
countProjects: (await queryOne(queries.countProjects, session)).countProjects.low,
|
countProjects: (await queryOne(queries.countProjects, session)).countProjects.low,
|
||||||
countInvites: (await queryOne(queries.countInvites, session)).countInvites.low,
|
countInvites: (await queryOne(queries.countInvites, session)).countInvites.low,
|
||||||
countFollows: (await queryOne(queries.countFollows, session)).countFollows.low,
|
countFollows: (await queryOne(queries.countFollows, session)).countFollows.low,
|
||||||
countShouts: (await queryOne(queries.countShouts, session)).countShouts.low
|
countShouts: (await queryOne(queries.countShouts, session)).countShouts.low,
|
||||||
}
|
}
|
||||||
resolve(data)
|
resolve(data)
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export default {
|
|||||||
const { user } = ctx
|
const { user } = ctx
|
||||||
if (!user) return null
|
if (!user) return null
|
||||||
return neo4jgraphql(object, { id: user.id }, ctx, resolveInfo, false)
|
return neo4jgraphql(object, { id: user.id }, ctx, resolveInfo, false)
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
signup: async (parent, { email, password }, { req }) => {
|
signup: async (parent, { email, password }, { req }) => {
|
||||||
@ -34,12 +34,12 @@ export default {
|
|||||||
'MATCH (user:User {email: $userEmail}) ' +
|
'MATCH (user:User {email: $userEmail}) ' +
|
||||||
'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role, .disabled} as user LIMIT 1',
|
'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role, .disabled} as user LIMIT 1',
|
||||||
{
|
{
|
||||||
userEmail: email
|
userEmail: email,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
session.close()
|
session.close()
|
||||||
const [currentUser] = await result.records.map(function (record) {
|
const [currentUser] = await result.records.map(function(record) {
|
||||||
return record.get('user')
|
return record.get('user')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -50,29 +50,23 @@ export default {
|
|||||||
) {
|
) {
|
||||||
delete currentUser.password
|
delete currentUser.password
|
||||||
return encode(currentUser)
|
return encode(currentUser)
|
||||||
} else if (currentUser &&
|
} else if (currentUser && currentUser.disabled) {
|
||||||
currentUser.disabled
|
|
||||||
) {
|
|
||||||
throw new AuthenticationError('Your account has been disabled.')
|
throw new AuthenticationError('Your account has been disabled.')
|
||||||
} else {
|
} else {
|
||||||
throw new AuthenticationError('Incorrect email address or password.')
|
throw new AuthenticationError('Incorrect email address or password.')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changePassword: async (
|
changePassword: async (_, { oldPassword, newPassword }, { driver, user }) => {
|
||||||
_,
|
|
||||||
{ oldPassword, newPassword },
|
|
||||||
{ driver, user }
|
|
||||||
) => {
|
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
let result = await session.run(
|
let result = await session.run(
|
||||||
`MATCH (user:User {email: $userEmail})
|
`MATCH (user:User {email: $userEmail})
|
||||||
RETURN user {.id, .email, .password}`,
|
RETURN user {.id, .email, .password}`,
|
||||||
{
|
{
|
||||||
userEmail: user.email
|
userEmail: user.email,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const [currentUser] = result.records.map(function (record) {
|
const [currentUser] = result.records.map(function(record) {
|
||||||
return record.get('user')
|
return record.get('user')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -81,9 +75,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (await bcrypt.compareSync(newPassword, currentUser.password)) {
|
if (await bcrypt.compareSync(newPassword, currentUser.password)) {
|
||||||
throw new AuthenticationError(
|
throw new AuthenticationError('Old password and new password should be different')
|
||||||
'Old password and new password should be different'
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
const newHashedPassword = await bcrypt.hashSync(newPassword, 10)
|
const newHashedPassword = await bcrypt.hashSync(newPassword, 10)
|
||||||
session.run(
|
session.run(
|
||||||
@ -93,13 +85,13 @@ export default {
|
|||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
userEmail: user.email,
|
userEmail: user.email,
|
||||||
newHashedPassword
|
newHashedPassword,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return encode(currentUser)
|
return encode(currentUser)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,10 +24,10 @@ const factory = Factory()
|
|||||||
// }
|
// }
|
||||||
const jennyRostocksHeaders = {
|
const jennyRostocksHeaders = {
|
||||||
authorization:
|
authorization:
|
||||||
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImxvY2F0aW9uTmFtZSI6bnVsbCwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJhYm91dCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9zYXNoYV9zaGVzdGFrb3YvMTI4LmpwZyIsImlkIjoidTMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5vcmciLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTU1MDg0NjY4MCwiZXhwIjoxNjM3MjQ2NjgwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.eZ_mVKas4Wzoc_JrQTEWXyRn7eY64cdIg4vqQ-F_7Jc'
|
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImxvY2F0aW9uTmFtZSI6bnVsbCwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJhYm91dCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9zYXNoYV9zaGVzdGFrb3YvMTI4LmpwZyIsImlkIjoidTMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5vcmciLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTU1MDg0NjY4MCwiZXhwIjoxNjM3MjQ2NjgwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.eZ_mVKas4Wzoc_JrQTEWXyRn7eY64cdIg4vqQ-F_7Jc',
|
||||||
}
|
}
|
||||||
|
|
||||||
const disable = async (id) => {
|
const disable = async id => {
|
||||||
const moderatorParams = { email: 'moderator@example.org', role: 'moderator', password: '1234' }
|
const moderatorParams = { email: 'moderator@example.org', role: 'moderator', password: '1234' }
|
||||||
const asModerator = Factory()
|
const asModerator = Factory()
|
||||||
await asModerator.create('User', moderatorParams)
|
await asModerator.create('User', moderatorParams)
|
||||||
@ -43,7 +43,7 @@ beforeEach(async () => {
|
|||||||
slug: 'matilde-hermiston',
|
slug: 'matilde-hermiston',
|
||||||
role: 'user',
|
role: 'user',
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ describe('isLoggedIn', () => {
|
|||||||
describe('unauthenticated', () => {
|
describe('unauthenticated', () => {
|
||||||
it('returns false', async () => {
|
it('returns false', async () => {
|
||||||
await expect(request(host, query)).resolves.toEqual({
|
await expect(request(host, query)).resolves.toEqual({
|
||||||
isLoggedIn: false
|
isLoggedIn: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -67,7 +67,7 @@ describe('isLoggedIn', () => {
|
|||||||
|
|
||||||
it('returns false', async () => {
|
it('returns false', async () => {
|
||||||
await expect(client.request(query)).resolves.toEqual({
|
await expect(client.request(query)).resolves.toEqual({
|
||||||
isLoggedIn: false
|
isLoggedIn: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -77,7 +77,7 @@ describe('isLoggedIn', () => {
|
|||||||
|
|
||||||
it('returns false', async () => {
|
it('returns false', async () => {
|
||||||
await expect(client.request(query)).resolves.toEqual({
|
await expect(client.request(query)).resolves.toEqual({
|
||||||
isLoggedIn: false
|
isLoggedIn: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ describe('isLoggedIn', () => {
|
|||||||
// see the decoded token above
|
// see the decoded token above
|
||||||
await factory.create('User', { id: 'u3' })
|
await factory.create('User', { id: 'u3' })
|
||||||
await expect(client.request(query)).resolves.toEqual({
|
await expect(client.request(query)).resolves.toEqual({
|
||||||
isLoggedIn: true
|
isLoggedIn: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -100,7 +100,7 @@ describe('isLoggedIn', () => {
|
|||||||
|
|
||||||
it('returns false', async () => {
|
it('returns false', async () => {
|
||||||
await expect(client.request(query)).resolves.toEqual({
|
await expect(client.request(query)).resolves.toEqual({
|
||||||
isLoggedIn: false
|
isLoggedIn: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -156,8 +156,8 @@ describe('currentUser', () => {
|
|||||||
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
||||||
name: 'Matilde Hermiston',
|
name: 'Matilde Hermiston',
|
||||||
slug: 'matilde-hermiston',
|
slug: 'matilde-hermiston',
|
||||||
role: 'user'
|
role: 'user',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(query)).resolves.toEqual(expected)
|
await expect(client.request(query)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -181,8 +181,8 @@ describe('login', () => {
|
|||||||
host,
|
host,
|
||||||
mutation({
|
mutation({
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
const token = data.login
|
const token = data.login
|
||||||
jwt.verify(token, process.env.JWT_SECRET, (err, data) => {
|
jwt.verify(token, process.env.JWT_SECRET, (err, data) => {
|
||||||
@ -200,9 +200,9 @@ describe('login', () => {
|
|||||||
host,
|
host,
|
||||||
mutation({
|
mutation({
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: '1234'
|
password: '1234',
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
).rejects.toThrow('Your account has been disabled.')
|
).rejects.toThrow('Your account has been disabled.')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -214,9 +214,9 @@ describe('login', () => {
|
|||||||
host,
|
host,
|
||||||
mutation({
|
mutation({
|
||||||
email: 'test@example.org',
|
email: 'test@example.org',
|
||||||
password: 'wrong'
|
password: 'wrong',
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
).rejects.toThrow('Incorrect email address or password.')
|
).rejects.toThrow('Incorrect email address or password.')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -228,9 +228,9 @@ describe('login', () => {
|
|||||||
host,
|
host,
|
||||||
mutation({
|
mutation({
|
||||||
email: 'non-existent@example.org',
|
email: 'non-existent@example.org',
|
||||||
password: 'wrong'
|
password: 'wrong',
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
).rejects.toThrow('Incorrect email address or password.')
|
).rejects.toThrow('Incorrect email address or password.')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -261,9 +261,9 @@ describe('change password', () => {
|
|||||||
host,
|
host,
|
||||||
mutation({
|
mutation({
|
||||||
oldPassword: '1234',
|
oldPassword: '1234',
|
||||||
newPassword: '1234'
|
newPassword: '1234',
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
).rejects.toThrow('Not Authorised!')
|
).rejects.toThrow('Not Authorised!')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -274,9 +274,9 @@ describe('change password', () => {
|
|||||||
client.request(
|
client.request(
|
||||||
mutation({
|
mutation({
|
||||||
oldPassword: '1234',
|
oldPassword: '1234',
|
||||||
newPassword: '1234'
|
newPassword: '1234',
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
).rejects.toThrow('Old password and new password should be different')
|
).rejects.toThrow('Old password and new password should be different')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -287,9 +287,9 @@ describe('change password', () => {
|
|||||||
client.request(
|
client.request(
|
||||||
mutation({
|
mutation({
|
||||||
oldPassword: 'notOldPassword',
|
oldPassword: 'notOldPassword',
|
||||||
newPassword: '12345'
|
newPassword: '12345',
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
).rejects.toThrow('Old password is not correct')
|
).rejects.toThrow('Old password is not correct')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -299,14 +299,14 @@ describe('change password', () => {
|
|||||||
let response = await client.request(
|
let response = await client.request(
|
||||||
mutation({
|
mutation({
|
||||||
oldPassword: '1234',
|
oldPassword: '1234',
|
||||||
newPassword: '12345'
|
newPassword: '12345',
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
await expect(response).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
changePassword: expect.any(String),
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
await expect(
|
|
||||||
response
|
|
||||||
).toEqual(expect.objectContaining({
|
|
||||||
changePassword: expect.any(String)
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -320,14 +320,16 @@ describe('do not expose private RSA key', () => {
|
|||||||
id
|
id
|
||||||
publicKey
|
publicKey
|
||||||
}
|
}
|
||||||
}`
|
}
|
||||||
|
`
|
||||||
const queryUserPrivateKey = gql`
|
const queryUserPrivateKey = gql`
|
||||||
query($queriedUserSlug: String) {
|
query($queriedUserSlug: String) {
|
||||||
User(slug: $queriedUserSlug) {
|
User(slug: $queriedUserSlug) {
|
||||||
id
|
id
|
||||||
privateKey
|
privateKey
|
||||||
}
|
}
|
||||||
}`
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const actionGenUserWithKeys = async () => {
|
const actionGenUserWithKeys = async () => {
|
||||||
// Generate user with "privateKey" via 'CreateUser' mutation instead of using the factories "factory.create('User', {...})", see above.
|
// Generate user with "privateKey" via 'CreateUser' mutation instead of using the factories "factory.create('User', {...})", see above.
|
||||||
@ -336,14 +338,17 @@ describe('do not expose private RSA key', () => {
|
|||||||
password: 'xYz',
|
password: 'xYz',
|
||||||
slug: 'apfel-strudel',
|
slug: 'apfel-strudel',
|
||||||
name: 'Apfel Strudel',
|
name: 'Apfel Strudel',
|
||||||
email: 'apfel-strudel@test.org'
|
email: 'apfel-strudel@test.org',
|
||||||
}
|
}
|
||||||
await client.request(gql`
|
await client.request(
|
||||||
mutation($id: ID, $password: String!, $slug: String, $name: String, $email: String!) {
|
gql`
|
||||||
CreateUser(id: $id, password: $password, slug: $slug, name: $name, email: $email) {
|
mutation($id: ID, $password: String!, $slug: String, $name: String, $email: String!) {
|
||||||
id
|
CreateUser(id: $id, password: $password, slug: $slug, name: $name, email: $email) {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}`, variables
|
`,
|
||||||
|
variables,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,13 +361,17 @@ describe('do not expose private RSA key', () => {
|
|||||||
it('returns publicKey', async () => {
|
it('returns publicKey', async () => {
|
||||||
await actionGenUserWithKeys()
|
await actionGenUserWithKeys()
|
||||||
await expect(
|
await expect(
|
||||||
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' })
|
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||||
).toEqual(expect.objectContaining({
|
).toEqual(
|
||||||
User: [{
|
expect.objectContaining({
|
||||||
id: 'bcb2d923-f3af-479e-9f00-61b12e864667',
|
User: [
|
||||||
publicKey: expect.any(String)
|
{
|
||||||
}]
|
id: 'bcb2d923-f3af-479e-9f00-61b12e864667',
|
||||||
}))
|
publicKey: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -370,7 +379,7 @@ describe('do not expose private RSA key', () => {
|
|||||||
it('throws "Not Authorised!"', async () => {
|
it('throws "Not Authorised!"', async () => {
|
||||||
await actionGenUserWithKeys()
|
await actionGenUserWithKeys()
|
||||||
await expect(
|
await expect(
|
||||||
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' })
|
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||||
).rejects.toThrow('Not Authorised')
|
).rejects.toThrow('Not Authorised')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -385,13 +394,17 @@ describe('do not expose private RSA key', () => {
|
|||||||
it('returns publicKey', async () => {
|
it('returns publicKey', async () => {
|
||||||
await actionGenUserWithKeys()
|
await actionGenUserWithKeys()
|
||||||
await expect(
|
await expect(
|
||||||
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' })
|
await client.request(queryUserPuplicKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||||
).toEqual(expect.objectContaining({
|
).toEqual(
|
||||||
User: [{
|
expect.objectContaining({
|
||||||
id: 'bcb2d923-f3af-479e-9f00-61b12e864667',
|
User: [
|
||||||
publicKey: expect.any(String)
|
{
|
||||||
}]
|
id: 'bcb2d923-f3af-479e-9f00-61b12e864667',
|
||||||
}))
|
publicKey: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -399,7 +412,7 @@ describe('do not expose private RSA key', () => {
|
|||||||
it('throws "Not Authorised!"', async () => {
|
it('throws "Not Authorised!"', async () => {
|
||||||
await actionGenUserWithKeys()
|
await actionGenUserWithKeys()
|
||||||
await expect(
|
await expect(
|
||||||
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' })
|
client.request(queryUserPrivateKey, { queriedUserSlug: 'apfel-strudel' }),
|
||||||
).rejects.toThrow('Not Authorised')
|
).rejects.toThrow('Not Authorised')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -24,15 +24,14 @@ describe('users', () => {
|
|||||||
const variables = {
|
const variables = {
|
||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
password: '123',
|
password: '123',
|
||||||
email: '123@123.de'
|
email: '123@123.de',
|
||||||
}
|
}
|
||||||
const expected = {
|
const expected = {
|
||||||
CreateUser: {
|
CreateUser: {
|
||||||
id: expect.any(String)
|
id: expect.any(String),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(mutation, variables))
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
.resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -54,35 +53,32 @@ describe('users', () => {
|
|||||||
it('name within specifications', async () => {
|
it('name within specifications', async () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
id: 'u47',
|
id: 'u47',
|
||||||
name: 'James Doe'
|
name: 'James Doe',
|
||||||
}
|
}
|
||||||
const expected = {
|
const expected = {
|
||||||
UpdateUser: {
|
UpdateUser: {
|
||||||
id: 'u47',
|
id: 'u47',
|
||||||
name: 'James Doe'
|
name: 'James Doe',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
await expect(client.request(mutation, variables))
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
.resolves.toEqual(expected)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('with no name', async () => {
|
it('with no name', async () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
id: 'u47'
|
id: 'u47',
|
||||||
}
|
}
|
||||||
const expected = 'Username must be at least 3 characters long!'
|
const expected = 'Username must be at least 3 characters long!'
|
||||||
await expect(client.request(mutation, variables))
|
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||||
.rejects.toThrow(expected)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('with too short name', async () => {
|
it('with too short name', async () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
id: 'u47',
|
id: 'u47',
|
||||||
name: ' '
|
name: ' ',
|
||||||
}
|
}
|
||||||
const expected = 'Username must be at least 3 characters long!'
|
const expected = 'Username must be at least 3 characters long!'
|
||||||
await expect(client.request(mutation, variables))
|
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||||
.rejects.toThrow(expected)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function (params) {
|
export default function(params) {
|
||||||
const {
|
const {
|
||||||
id = uuid(),
|
id = uuid(),
|
||||||
key = '',
|
key = '',
|
||||||
type = 'crowdfunding',
|
type = 'crowdfunding',
|
||||||
status = 'permanent',
|
status = 'permanent',
|
||||||
icon = '/img/badges/indiegogo_en_panda.svg'
|
icon = '/img/badges/indiegogo_en_panda.svg',
|
||||||
} = params
|
} = params
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -23,6 +23,6 @@ export default function (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, key, type, status, icon }
|
variables: { id, key, type, status, icon },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,7 @@
|
|||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function (params) {
|
export default function(params) {
|
||||||
const {
|
const { id = uuid(), name, slug, icon } = params
|
||||||
id = uuid(),
|
|
||||||
name,
|
|
||||||
slug,
|
|
||||||
icon
|
|
||||||
} = params
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mutation: `
|
mutation: `
|
||||||
@ -17,6 +12,6 @@ export default function (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, name, slug, icon }
|
variables: { id, name, slug, icon },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
import faker from 'faker'
|
import faker from 'faker'
|
||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function (params) {
|
export default function(params) {
|
||||||
const {
|
const {
|
||||||
id = uuid(),
|
id = uuid(),
|
||||||
postId = 'p6',
|
postId = 'p6',
|
||||||
content = [
|
content = [faker.lorem.sentence(), faker.lorem.sentence()].join('. '),
|
||||||
faker.lorem.sentence(),
|
|
||||||
faker.lorem.sentence()
|
|
||||||
].join('. ')
|
|
||||||
} = params
|
} = params
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -19,6 +16,6 @@ export default function (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, postId, content }
|
variables: { id, postId, content },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ const authenticatedHeaders = async ({ email, password }, host) => {
|
|||||||
}`
|
}`
|
||||||
const response = await request(host, mutation)
|
const response = await request(host, mutation)
|
||||||
return {
|
return {
|
||||||
authorization: `Bearer ${response.login}`
|
authorization: `Bearer ${response.login}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const factories = {
|
const factories = {
|
||||||
@ -31,7 +31,7 @@ const factories = {
|
|||||||
Category: createCategory,
|
Category: createCategory,
|
||||||
Tag: createTag,
|
Tag: createTag,
|
||||||
Report: createReport,
|
Report: createReport,
|
||||||
Notification: createNotification
|
Notification: createNotification,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cleanDatabase = async (options = {}) => {
|
export const cleanDatabase = async (options = {}) => {
|
||||||
@ -47,11 +47,8 @@ export const cleanDatabase = async (options = {}) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Factory (options = {}) {
|
export default function Factory(options = {}) {
|
||||||
const {
|
const { neo4jDriver = getDriver(), seedServerHost = 'http://127.0.0.1:4001' } = options
|
||||||
neo4jDriver = getDriver(),
|
|
||||||
seedServerHost = 'http://127.0.0.1:4001'
|
|
||||||
} = options
|
|
||||||
|
|
||||||
const graphQLClient = new GraphQLClient(seedServerHost)
|
const graphQLClient = new GraphQLClient(seedServerHost)
|
||||||
|
|
||||||
@ -61,21 +58,18 @@ export default function Factory (options = {}) {
|
|||||||
graphQLClient,
|
graphQLClient,
|
||||||
factories,
|
factories,
|
||||||
lastResponse: null,
|
lastResponse: null,
|
||||||
async authenticateAs ({ email, password }) {
|
async authenticateAs({ email, password }) {
|
||||||
const headers = await authenticatedHeaders(
|
const headers = await authenticatedHeaders({ email, password }, seedServerHost)
|
||||||
{ email, password },
|
|
||||||
seedServerHost
|
|
||||||
)
|
|
||||||
this.lastResponse = headers
|
this.lastResponse = headers
|
||||||
this.graphQLClient = new GraphQLClient(seedServerHost, { headers })
|
this.graphQLClient = new GraphQLClient(seedServerHost, { headers })
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
async create (node, properties) {
|
async create(node, properties) {
|
||||||
const { mutation, variables } = this.factories[node](properties)
|
const { mutation, variables } = this.factories[node](properties)
|
||||||
this.lastResponse = await this.graphQLClient.request(mutation, variables)
|
this.lastResponse = await this.graphQLClient.request(mutation, variables)
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
async relate (node, relationship, properties) {
|
async relate(node, relationship, properties) {
|
||||||
const { from, to } = properties
|
const { from, to } = properties
|
||||||
const mutation = `
|
const mutation = `
|
||||||
mutation {
|
mutation {
|
||||||
@ -88,11 +82,11 @@ export default function Factory (options = {}) {
|
|||||||
this.lastResponse = await this.graphQLClient.request(mutation)
|
this.lastResponse = await this.graphQLClient.request(mutation)
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
async mutate (mutation, variables) {
|
async mutate(mutation, variables) {
|
||||||
this.lastResponse = await this.graphQLClient.request(mutation, variables)
|
this.lastResponse = await this.graphQLClient.request(mutation, variables)
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
async shout (properties) {
|
async shout(properties) {
|
||||||
const { id, type } = properties
|
const { id, type } = properties
|
||||||
const mutation = `
|
const mutation = `
|
||||||
mutation {
|
mutation {
|
||||||
@ -105,7 +99,7 @@ export default function Factory (options = {}) {
|
|||||||
this.lastResponse = await this.graphQLClient.request(mutation)
|
this.lastResponse = await this.graphQLClient.request(mutation)
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
async follow (properties) {
|
async follow(properties) {
|
||||||
const { id, type } = properties
|
const { id, type } = properties
|
||||||
const mutation = `
|
const mutation = `
|
||||||
mutation {
|
mutation {
|
||||||
@ -118,10 +112,10 @@ export default function Factory (options = {}) {
|
|||||||
this.lastResponse = await this.graphQLClient.request(mutation)
|
this.lastResponse = await this.graphQLClient.request(mutation)
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
async cleanDatabase () {
|
async cleanDatabase() {
|
||||||
this.lastResponse = await cleanDatabase({ driver: this.neo4jDriver })
|
this.lastResponse = await cleanDatabase({ driver: this.neo4jDriver })
|
||||||
return this
|
return this
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
result.authenticateAs.bind(result)
|
result.authenticateAs.bind(result)
|
||||||
result.create.bind(result)
|
result.create.bind(result)
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function (params) {
|
export default function(params) {
|
||||||
const {
|
const { id = uuid(), read = false } = params
|
||||||
id = uuid(),
|
|
||||||
read = false
|
|
||||||
} = params
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mutation: `
|
mutation: `
|
||||||
@ -15,6 +12,6 @@ export default function (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, read }
|
variables: { id, read },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import faker from 'faker'
|
import faker from 'faker'
|
||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function create (params) {
|
export default function create(params) {
|
||||||
const {
|
const {
|
||||||
id = uuid(),
|
id = uuid(),
|
||||||
name = faker.company.companyName(),
|
name = faker.company.companyName(),
|
||||||
description = faker.company.catchPhrase()
|
description = faker.company.catchPhrase(),
|
||||||
} = params
|
} = params
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -16,6 +16,6 @@ export default function create (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, name, description }
|
variables: { id, name, description },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import faker from 'faker'
|
import faker from 'faker'
|
||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function (params) {
|
export default function(params) {
|
||||||
const {
|
const {
|
||||||
id = uuid(),
|
id = uuid(),
|
||||||
slug = '',
|
slug = '',
|
||||||
@ -11,11 +11,11 @@ export default function (params) {
|
|||||||
faker.lorem.sentence(),
|
faker.lorem.sentence(),
|
||||||
faker.lorem.sentence(),
|
faker.lorem.sentence(),
|
||||||
faker.lorem.sentence(),
|
faker.lorem.sentence(),
|
||||||
faker.lorem.sentence()
|
faker.lorem.sentence(),
|
||||||
].join('. '),
|
].join('. '),
|
||||||
image = faker.image.image(),
|
image = faker.image.image(),
|
||||||
visibility = 'public',
|
visibility = 'public',
|
||||||
deleted = false
|
deleted = false,
|
||||||
} = params
|
} = params
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -43,6 +43,6 @@ export default function (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, slug, title, content, image, visibility, deleted }
|
variables: { id, slug, title, content, image, visibility, deleted },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
import faker from 'faker'
|
import faker from 'faker'
|
||||||
|
|
||||||
export default function create (params) {
|
export default function create(params) {
|
||||||
const {
|
const { description = faker.lorem.sentence(), id } = params
|
||||||
description = faker.lorem.sentence(),
|
|
||||||
id
|
|
||||||
} = params
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mutation: `
|
mutation: `
|
||||||
@ -15,6 +12,6 @@ export default function create (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, description }
|
variables: { id, description },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function (params) {
|
export default function(params) {
|
||||||
const {
|
const { id = uuid(), name = '#human-connection' } = params
|
||||||
id = uuid(),
|
|
||||||
name = '#human-connection'
|
|
||||||
} = params
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mutation: `
|
mutation: `
|
||||||
@ -14,6 +11,6 @@ export default function (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, name }
|
variables: { id, name },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import faker from 'faker'
|
import faker from 'faker'
|
||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
|
|
||||||
export default function create (params) {
|
export default function create(params) {
|
||||||
const {
|
const {
|
||||||
id = uuid(),
|
id = uuid(),
|
||||||
name = faker.name.findName(),
|
name = faker.name.findName(),
|
||||||
@ -10,7 +10,7 @@ export default function create (params) {
|
|||||||
password = '1234',
|
password = '1234',
|
||||||
role = 'user',
|
role = 'user',
|
||||||
avatar = faker.internet.avatar(),
|
avatar = faker.internet.avatar(),
|
||||||
about = faker.lorem.paragraph()
|
about = faker.lorem.paragraph(),
|
||||||
} = params
|
} = params
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -46,6 +46,6 @@ export default function create (params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
variables: { id, name, slug, password, email, avatar, about, role }
|
variables: { id, name, slug, password, email, avatar, about, role },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,13 @@ if (process.env.NODE_ENV === 'production') {
|
|||||||
throw new Error(`YOU CAN'T CLEAN THE DATABASE WITH NODE_ENV=${process.env.NODE_ENV}`)
|
throw new Error(`YOU CAN'T CLEAN THE DATABASE WITH NODE_ENV=${process.env.NODE_ENV}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
(async function () {
|
;(async function() {
|
||||||
try {
|
try {
|
||||||
await cleanDatabase()
|
await cleanDatabase()
|
||||||
console.log('Successfully deleted all nodes and relations!')
|
console.log('Successfully deleted all nodes and relations!') // eslint-disable-line no-console
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${err}`)
|
console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${err}`) // eslint-disable-line no-console
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|||||||
@ -2,126 +2,247 @@ import faker from 'faker'
|
|||||||
import Factory from './factories'
|
import Factory from './factories'
|
||||||
|
|
||||||
/* eslint-disable no-multi-spaces */
|
/* eslint-disable no-multi-spaces */
|
||||||
(async function () {
|
;(async function() {
|
||||||
try {
|
try {
|
||||||
const f = Factory()
|
const f = Factory()
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.create('Badge', { id: 'b1', key: 'indiegogo_en_racoon', type: 'crowdfunding', status: 'permanent', icon: '/img/badges/indiegogo_en_racoon.svg' }),
|
f.create('Badge', {
|
||||||
f.create('Badge', { id: 'b2', key: 'indiegogo_en_rabbit', type: 'crowdfunding', status: 'permanent', icon: '/img/badges/indiegogo_en_rabbit.svg' }),
|
id: 'b1',
|
||||||
f.create('Badge', { id: 'b3', key: 'indiegogo_en_wolf', type: 'crowdfunding', status: 'permanent', icon: '/img/badges/indiegogo_en_wolf.svg' }),
|
key: 'indiegogo_en_racoon',
|
||||||
f.create('Badge', { id: 'b4', key: 'indiegogo_en_bear', type: 'crowdfunding', status: 'permanent', icon: '/img/badges/indiegogo_en_bear.svg' }),
|
type: 'crowdfunding',
|
||||||
f.create('Badge', { id: 'b5', key: 'indiegogo_en_turtle', type: 'crowdfunding', status: 'permanent', icon: '/img/badges/indiegogo_en_turtle.svg' }),
|
status: 'permanent',
|
||||||
f.create('Badge', { id: 'b6', key: 'indiegogo_en_rhino', type: 'crowdfunding', status: 'permanent', icon: '/img/badges/indiegogo_en_rhino.svg' })
|
icon: '/img/badges/indiegogo_en_racoon.svg',
|
||||||
|
}),
|
||||||
|
f.create('Badge', {
|
||||||
|
id: 'b2',
|
||||||
|
key: 'indiegogo_en_rabbit',
|
||||||
|
type: 'crowdfunding',
|
||||||
|
status: 'permanent',
|
||||||
|
icon: '/img/badges/indiegogo_en_rabbit.svg',
|
||||||
|
}),
|
||||||
|
f.create('Badge', {
|
||||||
|
id: 'b3',
|
||||||
|
key: 'indiegogo_en_wolf',
|
||||||
|
type: 'crowdfunding',
|
||||||
|
status: 'permanent',
|
||||||
|
icon: '/img/badges/indiegogo_en_wolf.svg',
|
||||||
|
}),
|
||||||
|
f.create('Badge', {
|
||||||
|
id: 'b4',
|
||||||
|
key: 'indiegogo_en_bear',
|
||||||
|
type: 'crowdfunding',
|
||||||
|
status: 'permanent',
|
||||||
|
icon: '/img/badges/indiegogo_en_bear.svg',
|
||||||
|
}),
|
||||||
|
f.create('Badge', {
|
||||||
|
id: 'b5',
|
||||||
|
key: 'indiegogo_en_turtle',
|
||||||
|
type: 'crowdfunding',
|
||||||
|
status: 'permanent',
|
||||||
|
icon: '/img/badges/indiegogo_en_turtle.svg',
|
||||||
|
}),
|
||||||
|
f.create('Badge', {
|
||||||
|
id: 'b6',
|
||||||
|
key: 'indiegogo_en_rhino',
|
||||||
|
type: 'crowdfunding',
|
||||||
|
status: 'permanent',
|
||||||
|
icon: '/img/badges/indiegogo_en_rhino.svg',
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.create('User', { id: 'u1', name: 'Peter Lustig', role: 'admin', email: 'admin@example.org' }),
|
f.create('User', {
|
||||||
f.create('User', { id: 'u2', name: 'Bob der Baumeister', role: 'moderator', email: 'moderator@example.org' }),
|
id: 'u1',
|
||||||
f.create('User', { id: 'u3', name: 'Jenny Rostock', role: 'user', email: 'user@example.org' }),
|
name: 'Peter Lustig',
|
||||||
f.create('User', { id: 'u4', name: 'Tick', role: 'user', email: 'tick@example.org' }),
|
role: 'admin',
|
||||||
f.create('User', { id: 'u5', name: 'Trick', role: 'user', email: 'trick@example.org' }),
|
email: 'admin@example.org',
|
||||||
f.create('User', { id: 'u6', name: 'Track', role: 'user', email: 'track@example.org' }),
|
}),
|
||||||
f.create('User', { id: 'u7', name: 'Dagobert', role: 'user', email: 'dagobert@example.org' })
|
f.create('User', {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Bob der Baumeister',
|
||||||
|
role: 'moderator',
|
||||||
|
email: 'moderator@example.org',
|
||||||
|
}),
|
||||||
|
f.create('User', {
|
||||||
|
id: 'u3',
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
role: 'user',
|
||||||
|
email: 'user@example.org',
|
||||||
|
}),
|
||||||
|
f.create('User', { id: 'u4', name: 'Tick', role: 'user', email: 'tick@example.org' }),
|
||||||
|
f.create('User', { id: 'u5', name: 'Trick', role: 'user', email: 'trick@example.org' }),
|
||||||
|
f.create('User', { id: 'u6', name: 'Track', role: 'user', email: 'track@example.org' }),
|
||||||
|
f.create('User', { id: 'u7', name: 'Dagobert', role: 'user', email: 'dagobert@example.org' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
const [ asAdmin, asModerator, asUser, asTick, asTrick, asTrack ] = await Promise.all([
|
const [asAdmin, asModerator, asUser, asTick, asTrick, asTrack] = await Promise.all([
|
||||||
Factory().authenticateAs({ email: 'admin@example.org', password: '1234' }),
|
Factory().authenticateAs({ email: 'admin@example.org', password: '1234' }),
|
||||||
Factory().authenticateAs({ email: 'moderator@example.org', password: '1234' }),
|
Factory().authenticateAs({ email: 'moderator@example.org', password: '1234' }),
|
||||||
Factory().authenticateAs({ email: 'user@example.org', password: '1234' }),
|
Factory().authenticateAs({ email: 'user@example.org', password: '1234' }),
|
||||||
Factory().authenticateAs({ email: 'tick@example.org', password: '1234' }),
|
Factory().authenticateAs({ email: 'tick@example.org', password: '1234' }),
|
||||||
Factory().authenticateAs({ email: 'trick@example.org', password: '1234' }),
|
Factory().authenticateAs({ email: 'trick@example.org', password: '1234' }),
|
||||||
Factory().authenticateAs({ email: 'track@example.org', password: '1234' })
|
Factory().authenticateAs({ email: 'track@example.org', password: '1234' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.relate('User', 'Badges', { from: 'b6', to: 'u1' }),
|
f.relate('User', 'Badges', { from: 'b6', to: 'u1' }),
|
||||||
f.relate('User', 'Badges', { from: 'b5', to: 'u2' }),
|
f.relate('User', 'Badges', { from: 'b5', to: 'u2' }),
|
||||||
f.relate('User', 'Badges', { from: 'b4', to: 'u3' }),
|
f.relate('User', 'Badges', { from: 'b4', to: 'u3' }),
|
||||||
f.relate('User', 'Badges', { from: 'b3', to: 'u4' }),
|
f.relate('User', 'Badges', { from: 'b3', to: 'u4' }),
|
||||||
f.relate('User', 'Badges', { from: 'b2', to: 'u5' }),
|
f.relate('User', 'Badges', { from: 'b2', to: 'u5' }),
|
||||||
f.relate('User', 'Badges', { from: 'b1', to: 'u6' }),
|
f.relate('User', 'Badges', { from: 'b1', to: 'u6' }),
|
||||||
f.relate('User', 'Friends', { from: 'u1', to: 'u2' }),
|
f.relate('User', 'Friends', { from: 'u1', to: 'u2' }),
|
||||||
f.relate('User', 'Friends', { from: 'u1', to: 'u3' }),
|
f.relate('User', 'Friends', { from: 'u1', to: 'u3' }),
|
||||||
f.relate('User', 'Friends', { from: 'u2', to: 'u3' }),
|
f.relate('User', 'Friends', { from: 'u2', to: 'u3' }),
|
||||||
f.relate('User', 'Blacklisted', { from: 'u7', to: 'u4' }),
|
f.relate('User', 'Blacklisted', { from: 'u7', to: 'u4' }),
|
||||||
f.relate('User', 'Blacklisted', { from: 'u7', to: 'u5' }),
|
f.relate('User', 'Blacklisted', { from: 'u7', to: 'u5' }),
|
||||||
f.relate('User', 'Blacklisted', { from: 'u7', to: 'u6' })
|
f.relate('User', 'Blacklisted', { from: 'u7', to: 'u6' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
asAdmin
|
asAdmin.follow({ id: 'u3', type: 'User' }),
|
||||||
.follow({ id: 'u3', type: 'User' }),
|
asModerator.follow({ id: 'u4', type: 'User' }),
|
||||||
asModerator
|
asUser.follow({ id: 'u4', type: 'User' }),
|
||||||
.follow({ id: 'u4', type: 'User' }),
|
asTick.follow({ id: 'u6', type: 'User' }),
|
||||||
asUser
|
asTrick.follow({ id: 'u4', type: 'User' }),
|
||||||
.follow({ id: 'u4', type: 'User' }),
|
asTrack.follow({ id: 'u3', type: 'User' }),
|
||||||
asTick
|
|
||||||
.follow({ id: 'u6', type: 'User' }),
|
|
||||||
asTrick
|
|
||||||
.follow({ id: 'u4', type: 'User' }),
|
|
||||||
asTrack
|
|
||||||
.follow({ id: 'u3', type: 'User' })
|
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.create('Category', { id: 'cat1', name: 'Just For Fun', slug: 'justforfun', icon: 'smile' }),
|
f.create('Category', { id: 'cat1', name: 'Just For Fun', slug: 'justforfun', icon: 'smile' }),
|
||||||
f.create('Category', { id: 'cat2', name: 'Happyness & Values', slug: 'happyness-values', icon: 'heart-o' }),
|
f.create('Category', {
|
||||||
f.create('Category', { id: 'cat3', name: 'Health & Wellbeing', slug: 'health-wellbeing', icon: 'medkit' }),
|
id: 'cat2',
|
||||||
f.create('Category', { id: 'cat4', name: 'Environment & Nature', slug: 'environment-nature', icon: 'tree' }),
|
name: 'Happyness & Values',
|
||||||
f.create('Category', { id: 'cat5', name: 'Animal Protection', slug: 'animalprotection', icon: 'paw' }),
|
slug: 'happyness-values',
|
||||||
f.create('Category', { id: 'cat6', name: 'Humanrights Justice', slug: 'humanrights-justice', icon: 'balance-scale' }),
|
icon: 'heart-o',
|
||||||
f.create('Category', { id: 'cat7', name: 'Education & Sciences', slug: 'education-sciences', icon: 'graduation-cap' }),
|
}),
|
||||||
f.create('Category', { id: 'cat8', name: 'Cooperation & Development', slug: 'cooperation-development', icon: 'users' }),
|
f.create('Category', {
|
||||||
f.create('Category', { id: 'cat9', name: 'Democracy & Politics', slug: 'democracy-politics', icon: 'university' }),
|
id: 'cat3',
|
||||||
f.create('Category', { id: 'cat10', name: 'Economy & Finances', slug: 'economy-finances', icon: 'money' }),
|
name: 'Health & Wellbeing',
|
||||||
f.create('Category', { id: 'cat11', name: 'Energy & Technology', slug: 'energy-technology', icon: 'flash' }),
|
slug: 'health-wellbeing',
|
||||||
f.create('Category', { id: 'cat12', name: 'IT, Internet & Data Privacy', slug: 'it-internet-dataprivacy', icon: 'mouse-pointer' }),
|
icon: 'medkit',
|
||||||
f.create('Category', { id: 'cat13', name: 'Art, Curlure & Sport', slug: 'art-culture-sport', icon: 'paint-brush' }),
|
}),
|
||||||
f.create('Category', { id: 'cat14', name: 'Freedom of Speech', slug: 'freedomofspeech', icon: 'bullhorn' }),
|
f.create('Category', {
|
||||||
f.create('Category', { id: 'cat15', name: 'Consumption & Sustainability', slug: 'consumption-sustainability', icon: 'shopping-cart' }),
|
id: 'cat4',
|
||||||
f.create('Category', { id: 'cat16', name: 'Global Peace & Nonviolence', slug: 'globalpeace-nonviolence', icon: 'angellist' })
|
name: 'Environment & Nature',
|
||||||
|
slug: 'environment-nature',
|
||||||
|
icon: 'tree',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat5',
|
||||||
|
name: 'Animal Protection',
|
||||||
|
slug: 'animalprotection',
|
||||||
|
icon: 'paw',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat6',
|
||||||
|
name: 'Humanrights Justice',
|
||||||
|
slug: 'humanrights-justice',
|
||||||
|
icon: 'balance-scale',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat7',
|
||||||
|
name: 'Education & Sciences',
|
||||||
|
slug: 'education-sciences',
|
||||||
|
icon: 'graduation-cap',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat8',
|
||||||
|
name: 'Cooperation & Development',
|
||||||
|
slug: 'cooperation-development',
|
||||||
|
icon: 'users',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat9',
|
||||||
|
name: 'Democracy & Politics',
|
||||||
|
slug: 'democracy-politics',
|
||||||
|
icon: 'university',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat10',
|
||||||
|
name: 'Economy & Finances',
|
||||||
|
slug: 'economy-finances',
|
||||||
|
icon: 'money',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat11',
|
||||||
|
name: 'Energy & Technology',
|
||||||
|
slug: 'energy-technology',
|
||||||
|
icon: 'flash',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat12',
|
||||||
|
name: 'IT, Internet & Data Privacy',
|
||||||
|
slug: 'it-internet-dataprivacy',
|
||||||
|
icon: 'mouse-pointer',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat13',
|
||||||
|
name: 'Art, Curlure & Sport',
|
||||||
|
slug: 'art-culture-sport',
|
||||||
|
icon: 'paint-brush',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat14',
|
||||||
|
name: 'Freedom of Speech',
|
||||||
|
slug: 'freedomofspeech',
|
||||||
|
icon: 'bullhorn',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat15',
|
||||||
|
name: 'Consumption & Sustainability',
|
||||||
|
slug: 'consumption-sustainability',
|
||||||
|
icon: 'shopping-cart',
|
||||||
|
}),
|
||||||
|
f.create('Category', {
|
||||||
|
id: 'cat16',
|
||||||
|
name: 'Global Peace & Nonviolence',
|
||||||
|
slug: 'globalpeace-nonviolence',
|
||||||
|
icon: 'angellist',
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.create('Tag', { id: 't1', name: 'Umwelt' }),
|
f.create('Tag', { id: 't1', name: 'Umwelt' }),
|
||||||
f.create('Tag', { id: 't2', name: 'Naturschutz' }),
|
f.create('Tag', { id: 't2', name: 'Naturschutz' }),
|
||||||
f.create('Tag', { id: 't3', name: 'Demokratie' }),
|
f.create('Tag', { id: 't3', name: 'Demokratie' }),
|
||||||
f.create('Tag', { id: 't4', name: 'Freiheit' })
|
f.create('Tag', { id: 't4', name: 'Freiheit' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
const mention1 = 'Hey <a class="mention" href="/profile/u3">@jenny-rostock</a>, what\'s up?'
|
const mention1 = 'Hey <a class="mention" href="/profile/u3">@jenny-rostock</a>, what\'s up?'
|
||||||
const mention2 = 'Hey <a class="mention" href="/profile/u3">@jenny-rostock</a>, here is another notification for you!'
|
const mention2 =
|
||||||
|
'Hey <a class="mention" href="/profile/u3">@jenny-rostock</a>, here is another notification for you!'
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
asAdmin.create('Post', { id: 'p0' }),
|
asAdmin.create('Post', { id: 'p0' }),
|
||||||
asModerator.create('Post', { id: 'p1' }),
|
asModerator.create('Post', { id: 'p1' }),
|
||||||
asUser.create('Post', { id: 'p2' }),
|
asUser.create('Post', { id: 'p2' }),
|
||||||
asTick.create('Post', { id: 'p3' }),
|
asTick.create('Post', { id: 'p3' }),
|
||||||
asTrick.create('Post', { id: 'p4' }),
|
asTrick.create('Post', { id: 'p4' }),
|
||||||
asTrack.create('Post', { id: 'p5' }),
|
asTrack.create('Post', { id: 'p5' }),
|
||||||
asAdmin.create('Post', { id: 'p6' }),
|
asAdmin.create('Post', { id: 'p6' }),
|
||||||
asModerator.create('Post', { id: 'p7', content: `${mention1} ${faker.lorem.paragraph()}` }),
|
asModerator.create('Post', { id: 'p7', content: `${mention1} ${faker.lorem.paragraph()}` }),
|
||||||
asUser.create('Post', { id: 'p8' }),
|
asUser.create('Post', { id: 'p8' }),
|
||||||
asTick.create('Post', { id: 'p9' }),
|
asTick.create('Post', { id: 'p9' }),
|
||||||
asTrick.create('Post', { id: 'p10' }),
|
asTrick.create('Post', { id: 'p10' }),
|
||||||
asTrack.create('Post', { id: 'p11' }),
|
asTrack.create('Post', { id: 'p11' }),
|
||||||
asAdmin.create('Post', { id: 'p12', content: `${mention2} ${faker.lorem.paragraph()}` }),
|
asAdmin.create('Post', { id: 'p12', content: `${mention2} ${faker.lorem.paragraph()}` }),
|
||||||
asModerator.create('Post', { id: 'p13' }),
|
asModerator.create('Post', { id: 'p13' }),
|
||||||
asUser.create('Post', { id: 'p14' }),
|
asUser.create('Post', { id: 'p14' }),
|
||||||
asTick.create('Post', { id: 'p15' })
|
asTick.create('Post', { id: 'p15' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.relate('Post', 'Categories', { from: 'p0', to: 'cat16' }),
|
f.relate('Post', 'Categories', { from: 'p0', to: 'cat16' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p1', to: 'cat1' }),
|
f.relate('Post', 'Categories', { from: 'p1', to: 'cat1' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p2', to: 'cat2' }),
|
f.relate('Post', 'Categories', { from: 'p2', to: 'cat2' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p3', to: 'cat3' }),
|
f.relate('Post', 'Categories', { from: 'p3', to: 'cat3' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p4', to: 'cat4' }),
|
f.relate('Post', 'Categories', { from: 'p4', to: 'cat4' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p5', to: 'cat5' }),
|
f.relate('Post', 'Categories', { from: 'p5', to: 'cat5' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p6', to: 'cat6' }),
|
f.relate('Post', 'Categories', { from: 'p6', to: 'cat6' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p7', to: 'cat7' }),
|
f.relate('Post', 'Categories', { from: 'p7', to: 'cat7' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p8', to: 'cat8' }),
|
f.relate('Post', 'Categories', { from: 'p8', to: 'cat8' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p9', to: 'cat9' }),
|
f.relate('Post', 'Categories', { from: 'p9', to: 'cat9' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p10', to: 'cat10' }),
|
f.relate('Post', 'Categories', { from: 'p10', to: 'cat10' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p11', to: 'cat11' }),
|
f.relate('Post', 'Categories', { from: 'p11', to: 'cat11' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p12', to: 'cat12' }),
|
f.relate('Post', 'Categories', { from: 'p12', to: 'cat12' }),
|
||||||
@ -129,63 +250,45 @@ import Factory from './factories'
|
|||||||
f.relate('Post', 'Categories', { from: 'p14', to: 'cat14' }),
|
f.relate('Post', 'Categories', { from: 'p14', to: 'cat14' }),
|
||||||
f.relate('Post', 'Categories', { from: 'p15', to: 'cat15' }),
|
f.relate('Post', 'Categories', { from: 'p15', to: 'cat15' }),
|
||||||
|
|
||||||
f.relate('Post', 'Tags', { from: 'p0', to: 't4' }),
|
f.relate('Post', 'Tags', { from: 'p0', to: 't4' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p1', to: 't1' }),
|
f.relate('Post', 'Tags', { from: 'p1', to: 't1' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p2', to: 't2' }),
|
f.relate('Post', 'Tags', { from: 'p2', to: 't2' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p3', to: 't3' }),
|
f.relate('Post', 'Tags', { from: 'p3', to: 't3' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p4', to: 't4' }),
|
f.relate('Post', 'Tags', { from: 'p4', to: 't4' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p5', to: 't1' }),
|
f.relate('Post', 'Tags', { from: 'p5', to: 't1' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p6', to: 't2' }),
|
f.relate('Post', 'Tags', { from: 'p6', to: 't2' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p7', to: 't3' }),
|
f.relate('Post', 'Tags', { from: 'p7', to: 't3' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p8', to: 't4' }),
|
f.relate('Post', 'Tags', { from: 'p8', to: 't4' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p9', to: 't1' }),
|
f.relate('Post', 'Tags', { from: 'p9', to: 't1' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p10', to: 't2' }),
|
f.relate('Post', 'Tags', { from: 'p10', to: 't2' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p11', to: 't3' }),
|
f.relate('Post', 'Tags', { from: 'p11', to: 't3' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p12', to: 't4' }),
|
f.relate('Post', 'Tags', { from: 'p12', to: 't4' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p13', to: 't1' }),
|
f.relate('Post', 'Tags', { from: 'p13', to: 't1' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p14', to: 't2' }),
|
f.relate('Post', 'Tags', { from: 'p14', to: 't2' }),
|
||||||
f.relate('Post', 'Tags', { from: 'p15', to: 't3' })
|
f.relate('Post', 'Tags', { from: 'p15', to: 't3' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
asAdmin
|
asAdmin.shout({ id: 'p2', type: 'Post' }),
|
||||||
.shout({ id: 'p2', type: 'Post' }),
|
asAdmin.shout({ id: 'p6', type: 'Post' }),
|
||||||
asAdmin
|
asModerator.shout({ id: 'p0', type: 'Post' }),
|
||||||
.shout({ id: 'p6', type: 'Post' }),
|
asModerator.shout({ id: 'p6', type: 'Post' }),
|
||||||
asModerator
|
asUser.shout({ id: 'p6', type: 'Post' }),
|
||||||
.shout({ id: 'p0', type: 'Post' }),
|
asUser.shout({ id: 'p7', type: 'Post' }),
|
||||||
asModerator
|
asTick.shout({ id: 'p8', type: 'Post' }),
|
||||||
.shout({ id: 'p6', type: 'Post' }),
|
asTick.shout({ id: 'p9', type: 'Post' }),
|
||||||
asUser
|
asTrack.shout({ id: 'p10', type: 'Post' }),
|
||||||
.shout({ id: 'p6', type: 'Post' }),
|
|
||||||
asUser
|
|
||||||
.shout({ id: 'p7', type: 'Post' }),
|
|
||||||
asTick
|
|
||||||
.shout({ id: 'p8', type: 'Post' }),
|
|
||||||
asTick
|
|
||||||
.shout({ id: 'p9', type: 'Post' }),
|
|
||||||
asTrack
|
|
||||||
.shout({ id: 'p10', type: 'Post' })
|
|
||||||
])
|
])
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
asAdmin
|
asAdmin.shout({ id: 'p2', type: 'Post' }),
|
||||||
.shout({ id: 'p2', type: 'Post' }),
|
asAdmin.shout({ id: 'p6', type: 'Post' }),
|
||||||
asAdmin
|
asModerator.shout({ id: 'p0', type: 'Post' }),
|
||||||
.shout({ id: 'p6', type: 'Post' }),
|
asModerator.shout({ id: 'p6', type: 'Post' }),
|
||||||
asModerator
|
asUser.shout({ id: 'p6', type: 'Post' }),
|
||||||
.shout({ id: 'p0', type: 'Post' }),
|
asUser.shout({ id: 'p7', type: 'Post' }),
|
||||||
asModerator
|
asTick.shout({ id: 'p8', type: 'Post' }),
|
||||||
.shout({ id: 'p6', type: 'Post' }),
|
asTick.shout({ id: 'p9', type: 'Post' }),
|
||||||
asUser
|
asTrack.shout({ id: 'p10', type: 'Post' }),
|
||||||
.shout({ id: 'p6', type: 'Post' }),
|
|
||||||
asUser
|
|
||||||
.shout({ id: 'p7', type: 'Post' }),
|
|
||||||
asTick
|
|
||||||
.shout({ id: 'p8', type: 'Post' }),
|
|
||||||
asTick
|
|
||||||
.shout({ id: 'p9', type: 'Post' }),
|
|
||||||
asTrack
|
|
||||||
.shout({ id: 'p10', type: 'Post' })
|
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -200,33 +303,49 @@ import Factory from './factories'
|
|||||||
asTrick.create('Comment', { id: 'c9', postId: 'p15' }),
|
asTrick.create('Comment', { id: 'c9', postId: 'p15' }),
|
||||||
asTrack.create('Comment', { id: 'c10', postId: 'p15' }),
|
asTrack.create('Comment', { id: 'c10', postId: 'p15' }),
|
||||||
asUser.create('Comment', { id: 'c11', postId: 'p15' }),
|
asUser.create('Comment', { id: 'c11', postId: 'p15' }),
|
||||||
asUser.create('Comment', { id: 'c12', postId: 'p15' })
|
asUser.create('Comment', { id: 'c12', postId: 'p15' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
const disableMutation = 'mutation($id: ID!) { disable(id: $id) }'
|
const disableMutation = 'mutation($id: ID!) { disable(id: $id) }'
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
asModerator.mutate(disableMutation, { id: 'p11' }),
|
asModerator.mutate(disableMutation, { id: 'p11' }),
|
||||||
asModerator.mutate(disableMutation, { id: 'c5' })
|
asModerator.mutate(disableMutation, { id: 'c5' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
asTick.create('Report', { description: 'I don\'t like this comment', id: 'c1' }),
|
asTick.create('Report', { description: "I don't like this comment", id: 'c1' }),
|
||||||
asTrick.create('Report', { description: 'I don\'t like this post', id: 'p1' }),
|
asTrick.create('Report', { description: "I don't like this post", id: 'p1' }),
|
||||||
asTrack.create('Report', { description: 'I don\'t like this user', id: 'u1' })
|
asTrack.create('Report', { description: "I don't like this user", id: 'u1' }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.create('Organization', { id: 'o1', name: 'Democracy Deutschland', description: 'Description for democracy-deutschland.' }),
|
f.create('Organization', {
|
||||||
f.create('Organization', { id: 'o2', name: 'Human-Connection', description: 'Description for human-connection.' }),
|
id: 'o1',
|
||||||
f.create('Organization', { id: 'o3', name: 'Pro Veg', description: 'Description for pro-veg.' }),
|
name: 'Democracy Deutschland',
|
||||||
f.create('Organization', { id: 'o4', name: 'Greenpeace', description: 'Description for greenpeace.' })
|
description: 'Description for democracy-deutschland.',
|
||||||
|
}),
|
||||||
|
f.create('Organization', {
|
||||||
|
id: 'o2',
|
||||||
|
name: 'Human-Connection',
|
||||||
|
description: 'Description for human-connection.',
|
||||||
|
}),
|
||||||
|
f.create('Organization', {
|
||||||
|
id: 'o3',
|
||||||
|
name: 'Pro Veg',
|
||||||
|
description: 'Description for pro-veg.',
|
||||||
|
}),
|
||||||
|
f.create('Organization', {
|
||||||
|
id: 'o4',
|
||||||
|
name: 'Greenpeace',
|
||||||
|
description: 'Description for greenpeace.',
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
f.relate('Organization', 'CreatedBy', { from: 'u1', to: 'o1' }),
|
f.relate('Organization', 'CreatedBy', { from: 'u1', to: 'o1' }),
|
||||||
f.relate('Organization', 'CreatedBy', { from: 'u1', to: 'o2' }),
|
f.relate('Organization', 'CreatedBy', { from: 'u1', to: 'o2' }),
|
||||||
f.relate('Organization', 'OwnedBy', { from: 'u2', to: 'o2' }),
|
f.relate('Organization', 'OwnedBy', { from: 'u2', to: 'o2' }),
|
||||||
f.relate('Organization', 'OwnedBy', { from: 'u2', to: 'o3' })
|
f.relate('Organization', 'OwnedBy', { from: 'u2', to: 'o3' }),
|
||||||
])
|
])
|
||||||
/* eslint-disable-next-line no-console */
|
/* eslint-disable-next-line no-console */
|
||||||
console.log('Seeded Data...')
|
console.log('Seeded Data...')
|
||||||
|
|||||||
@ -19,7 +19,7 @@ const unsplashTopics = [
|
|||||||
'face',
|
'face',
|
||||||
'people',
|
'people',
|
||||||
'portrait',
|
'portrait',
|
||||||
'amazing'
|
'amazing',
|
||||||
]
|
]
|
||||||
let unsplashTopicsTmp = []
|
let unsplashTopicsTmp = []
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ const ngoLogos = [
|
|||||||
'https://dcassetcdn.com/design_img/10133/25833/25833_303600_10133_image.jpg',
|
'https://dcassetcdn.com/design_img/10133/25833/25833_303600_10133_image.jpg',
|
||||||
'https://cdn.tutsplus.com/vector/uploads/legacy/articles/08bad_ngologos/20.jpg',
|
'https://cdn.tutsplus.com/vector/uploads/legacy/articles/08bad_ngologos/20.jpg',
|
||||||
'https://cdn.tutsplus.com/vector/uploads/legacy/articles/08bad_ngologos/33.jpg',
|
'https://cdn.tutsplus.com/vector/uploads/legacy/articles/08bad_ngologos/33.jpg',
|
||||||
null
|
null,
|
||||||
]
|
]
|
||||||
|
|
||||||
const difficulties = ['easy', 'medium', 'hard']
|
const difficulties = ['easy', 'medium', 'hard']
|
||||||
@ -38,8 +38,7 @@ const difficulties = ['easy', 'medium', 'hard']
|
|||||||
export default {
|
export default {
|
||||||
randomItem: (items, filter) => {
|
randomItem: (items, filter) => {
|
||||||
let ids = filter
|
let ids = filter
|
||||||
? Object.keys(items)
|
? Object.keys(items).filter(id => {
|
||||||
.filter(id => {
|
|
||||||
return filter(items[id])
|
return filter(items[id])
|
||||||
})
|
})
|
||||||
: _.keys(items)
|
: _.keys(items)
|
||||||
@ -61,7 +60,7 @@ export default {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
random: (items) => {
|
random: items => {
|
||||||
return _.shuffle(items).pop()
|
return _.shuffle(items).pop()
|
||||||
},
|
},
|
||||||
randomDifficulty: () => {
|
randomDifficulty: () => {
|
||||||
@ -78,7 +77,9 @@ export default {
|
|||||||
if (unsplashTopicsTmp.length < 2) {
|
if (unsplashTopicsTmp.length < 2) {
|
||||||
unsplashTopicsTmp = _.shuffle(unsplashTopics)
|
unsplashTopicsTmp = _.shuffle(unsplashTopics)
|
||||||
}
|
}
|
||||||
return 'https://source.unsplash.com/daily?' + unsplashTopicsTmp.pop() + ',' + unsplashTopicsTmp.pop()
|
return (
|
||||||
|
'https://source.unsplash.com/daily?' + unsplashTopicsTmp.pop() + ',' + unsplashTopicsTmp.pop()
|
||||||
|
)
|
||||||
},
|
},
|
||||||
randomCategories: (seederstore, allowEmpty = false) => {
|
randomCategories: (seederstore, allowEmpty = false) => {
|
||||||
let count = Math.round(Math.random() * 3)
|
let count = Math.round(Math.random() * 3)
|
||||||
@ -101,8 +102,8 @@ export default {
|
|||||||
zipCode: faker.address.zipCode(),
|
zipCode: faker.address.zipCode(),
|
||||||
street: faker.address.streetAddress(),
|
street: faker.address.streetAddress(),
|
||||||
country: faker.address.countryCode(),
|
country: faker.address.countryCode(),
|
||||||
lat: 54.032726 - (Math.random() * 10),
|
lat: 54.032726 - Math.random() * 10,
|
||||||
lng: 6.558838 + (Math.random() * 10)
|
lng: 6.558838 + Math.random() * 10,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return addresses
|
return addresses
|
||||||
@ -129,5 +130,5 @@ export default {
|
|||||||
code += chars.substr(n, 1)
|
code += chars.substr(n, 1)
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,17 +28,17 @@ let schema = makeAugmentedSchema({
|
|||||||
resolvers,
|
resolvers,
|
||||||
config: {
|
config: {
|
||||||
query: {
|
query: {
|
||||||
exclude: ['Notfication', 'Statistics', 'LoggedInUser']
|
exclude: ['Notfication', 'Statistics', 'LoggedInUser'],
|
||||||
},
|
},
|
||||||
mutation: {
|
mutation: {
|
||||||
exclude: ['Notfication', 'Statistics', 'LoggedInUser']
|
exclude: ['Notfication', 'Statistics', 'LoggedInUser'],
|
||||||
},
|
},
|
||||||
debug: debug
|
debug: debug,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
schema = applyScalars(applyDirectives(schema))
|
schema = applyScalars(applyDirectives(schema))
|
||||||
|
|
||||||
const createServer = (options) => {
|
const createServer = options => {
|
||||||
const defaults = {
|
const defaults = {
|
||||||
context: async ({ request }) => {
|
context: async ({ request }) => {
|
||||||
const authorizationHeader = request.headers.authorization || ''
|
const authorizationHeader = request.headers.authorization || ''
|
||||||
@ -48,15 +48,15 @@ const createServer = (options) => {
|
|||||||
user,
|
user,
|
||||||
req: request,
|
req: request,
|
||||||
cypherParams: {
|
cypherParams: {
|
||||||
currentUserId: user ? user.id : null
|
currentUserId: user ? user.id : null,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
schema: schema,
|
schema: schema,
|
||||||
debug: debug,
|
debug: debug,
|
||||||
tracing: debug,
|
tracing: debug,
|
||||||
middlewares: middleware(schema),
|
middlewares: middleware(schema),
|
||||||
mocks: (process.env.MOCK === 'true') ? mocks : false
|
mocks: process.env.MOCK === 'true' ? mocks : false,
|
||||||
}
|
}
|
||||||
const server = new GraphQLServer(Object.assign({}, defaults, options))
|
const server = new GraphQLServer(Object.assign({}, defaults, options))
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user