Draft: Setup subsriptions for backend+frontend

Please clean up this commit and squash it later on.
This commit is contained in:
roschaefer 2020-01-24 17:25:45 +01:00
parent 1a93fcc023
commit 3be5dee08b
9 changed files with 102 additions and 16 deletions

View File

@ -96,6 +96,7 @@
"request": "~2.88.0",
"sanitize-html": "~1.21.1",
"slug": "~2.1.0",
"subscriptions-transport-ws": "^0.9.16",
"trunc-html": "~1.1.2",
"uuid": "~3.4.0",
"validator": "^12.1.0",

View File

@ -49,6 +49,7 @@ export const serverConfigs = {
CLIENT_URI,
GRAPHQL_URI,
PUBLIC_REGISTRATION: process.env.PUBLIC_REGISTRATION === 'true',
SUBSCRIPTIONS_PATH: '/subscriptions',
}
export const developmentConfigs = {

View File

@ -1,9 +1,10 @@
import createServer from './server'
import CONFIG from './config'
const { app } = createServer()
const { app, server, httpServer } = createServer()
const url = new URL(CONFIG.GRAPHQL_URI)
app.listen({ port: url.port }, () => {
httpServer.listen({ port: url.port }, () => {
/* eslint-disable-next-line no-console */
console.log(`GraphQLServer ready at ${CONFIG.GRAPHQL_URI} 🚀`)
console.log(`🚀 Server ready at http://localhost:${url.port}${server.graphqlPath}`)
console.log(`🚀 Subscriptions ready at ws://localhost:${url.port}${server.subscriptionsPath}`)
})

View File

@ -5,6 +5,11 @@ import { UserInputError } from 'apollo-server'
import fileUpload from './fileUpload'
import Resolver from './helpers/Resolver'
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
import { PubSub } from 'apollo-server'
const pubsub = new PubSub();
const POST_ADDED = 'POST_ADDED';
const maintainPinnedPosts = params => {
const pinnedPostFilter = { pinned: true }
@ -17,6 +22,12 @@ const maintainPinnedPosts = params => {
}
export default {
Subscription: {
postAdded: {
// Additional event labels can be passed to asyncIterator creation
subscribe: () => pubsub.asyncIterator([POST_ADDED]),
},
},
Query: {
Post: async (object, params, context, resolveInfo) => {
params = await filterForMutedUsers(params, context)
@ -102,6 +113,7 @@ export default {
})
try {
const [post] = await writeTxResultPromise
pubsub.publish(POST_ADDED, { postAdded: post });
return post
} catch (e) {
if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')

View File

@ -1,3 +1,7 @@
type Subscription {
postAdded: Post
}
type Mutation {
# Get a JWT Token for the given Email and password
login(email: String!, password: String!): String!

View File

@ -1,6 +1,9 @@
import express from 'express'
import http from 'http'
import helmet from 'helmet'
import { ApolloServer } from 'apollo-server-express'
import CONFIG from './config'
import middleware from './middleware'
import { getNeode, getDriver } from './db/neo4j'
@ -8,19 +11,28 @@ import decode from './jwt/decode'
import schema from './schema'
import webfinger from './activitypub/routes/webfinger'
const driver = getDriver()
const neode = getNeode()
export const context = async ({ req }) => {
const user = await decode(driver, req.headers.authorization)
return {
driver,
neode,
user,
req,
cypherParams: {
currentUserId: user ? user.id : null,
},
const getContext = async (req) => {
const user = await decode(driver, req.headers.authorization)
return {
driver,
neode,
user,
req,
cypherParams: {
currentUserId: user ? user.id : null,
},
}
}
export const context = async (options) => {
const { connection, req } = options
if (connection) {
return connection.context
} else {
return getContext(req)
}
}
@ -28,6 +40,12 @@ const createServer = options => {
const defaults = {
context,
schema: middleware(schema),
subscriptions: {
onConnect: (connectionParams, webSocket) => {
console.log('connectionParams', connectionParams)
return getContext(connectionParams)
},
},
debug: !!CONFIG.DEBUG,
tracing: !!CONFIG.DEBUG,
formatError: error => {
@ -46,8 +64,11 @@ const createServer = options => {
app.use('/.well-known/', webfinger())
app.use(express.static('public'))
server.applyMiddleware({ app, path: '/' })
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
return { server, app }
return { server, httpServer, app }
}
export default createServer

View File

@ -1175,7 +1175,7 @@
url-regex "~4.1.1"
video-extensions "~1.1.0"
"@metascraper/helpers@^5.10.5", "@metascraper/helpers@^5.10.6":
"@metascraper/helpers@^5.10.6":
version "5.10.6"
resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.10.6.tgz#0b786607212925a577926fd0cd0313a49de3499c"
integrity sha512-/jvhlM3RKGYMoUK8D8S1r3tN03/EYizCqWF7zDx0aBMC8Ihp33DRGs9oNdsgkgwzVF7O/YpDm55l9K+qVJlsyQ==
@ -7918,6 +7918,11 @@ serve-static@1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.17.1"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"

View File

@ -74,6 +74,16 @@ import { mapGetters, mapMutations } from 'vuex'
import { filterPosts } from '~/graphql/PostQuery.js'
import PostMutations from '~/graphql/PostMutations'
import UpdateQuery from '~/components/utils/UpdateQuery'
import gql from 'graphql-tag'
import {
userFragment,
postFragment,
commentFragment,
postCountsFragment,
userCountsFragment,
locationAndBadgesFragment,
tagsCategoriesAndPinnedFragment,
} from '~/graphql/Fragments'
export default {
components: {
@ -213,6 +223,36 @@ export default {
this.posts = Post
},
fetchPolicy: 'cache-and-network',
subscribeToMore: {
document: gql`
${userFragment}
${userCountsFragment}
${locationAndBadgesFragment('EN')}
${postFragment}
${postCountsFragment}
${tagsCategoriesAndPinnedFragment}
subscription Post {
postAdded {
...post
...postCounts
...tagsCategoriesAndPinned
author {
...user
...userCounts
...locationAndBadges
}
}
}`,
// Mutate the previous result
updateQuery: (previousResult, { subscriptionData }) => {
console.log('previousResult', previousResult)
console.log('subscriptionData', subscriptionData)
const { data: { postAdded: newPost } } = subscriptionData
return { Post: [newPost, ...previousResult.Post] }
// Here, return the new result from the previous with the new data
},
}
},
},
}

View File

@ -9,6 +9,7 @@ export default ({ app }) => {
const backendUrl = process.env.GRAPHQL_URI || 'http://localhost:4000'
return {
wsEndpoint: 'ws://localhost:4000/graphql', // optional
httpEndpoint: process.server ? backendUrl : '/api',
httpLinkOptions: {
credentials: 'same-origin',
@ -16,7 +17,7 @@ export default ({ app }) => {
credentials: true,
tokenName: 'human-connection-token',
persisting: false,
websocketsOnly: false,
websocketsOnly: true,
cache: new InMemoryCache({ fragmentMatcher }),
}
}