feat(server): add comment storage and retreive backedn
This commit is contained in:
@@ -66,6 +66,7 @@ export default {
|
||||
methods: {
|
||||
async addComment() {
|
||||
let commentInput = {
|
||||
streamId: this.$route.params.streamId,
|
||||
resources: [
|
||||
{ type: 'stream', id: this.$route.params.streamId },
|
||||
{
|
||||
|
||||
@@ -3,9 +3,33 @@ const { authorizeResolver, pubsub } = require( `${appRoot}/modules/shared` )
|
||||
const { ForbiddenError, UserInputError, ApolloError, withFilter } = require( 'apollo-server-express' )
|
||||
const { getStream } = require( `${appRoot}/modules/core/services/streams` )
|
||||
|
||||
const { createComment } = require( `${appRoot}/modules/comments/services` )
|
||||
const { getComment, getComments, createComment } = require( `${appRoot}/modules/comments/services` )
|
||||
|
||||
const authorizeStreamAccess = async ( { streamId, userId, auth } ) => {
|
||||
const stream = await getStream( { streamId, userId } )
|
||||
if ( !stream )
|
||||
throw new ApolloError( 'Stream not found' )
|
||||
|
||||
if ( !stream.isPublic && auth === false )
|
||||
throw new ForbiddenError( 'You are not authorized.' )
|
||||
|
||||
if ( !stream.isPublic ) {
|
||||
await authorizeResolver( userId, streamId, 'stream:reviewer' )
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async comment( parent, args, context, info ) {
|
||||
await authorizeStreamAccess( { streamId: args.streamId, userId: context.userId, auth: context.auth } )
|
||||
return await getComment( args.id )
|
||||
},
|
||||
|
||||
async comments( parent, args, context, info ) {
|
||||
await authorizeStreamAccess( { streamId: args.streamId, userId: context.userId, auth: context.auth } )
|
||||
return await getComments( args )
|
||||
}
|
||||
},
|
||||
Stream: {
|
||||
async comments( parent, args, context, info ) {
|
||||
// TODO
|
||||
@@ -32,16 +56,7 @@ module.exports = {
|
||||
Mutation: {
|
||||
// Used for broadcasting real time chat head bubbles and status. Does not persist anything!
|
||||
async userCommentActivityBroadcast( parent, args, context, info ) {
|
||||
let stream = await getStream( { streamId: args.streamId, userId: context.userId } )
|
||||
if ( !stream )
|
||||
throw new ApolloError( 'Stream not found' )
|
||||
|
||||
if ( !stream.isPublic && context.auth === false )
|
||||
throw new ForbiddenError( 'You are not authorized.' )
|
||||
|
||||
if ( !stream.isPublic ) {
|
||||
await authorizeResolver( context.userId, args.streamId, 'stream:reviewer' )
|
||||
}
|
||||
await authorizeStreamAccess( { streamId: args.streamId, userId: context.userId, auth: context.auth } )
|
||||
|
||||
await pubsub.publish( 'COMMENT_ACTIVITY', {
|
||||
userCommentActivity: args.data,
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
extend type Query {
|
||||
comment(id: String!, streamId: String!): Comment
|
||||
|
||||
comments(streamId: String!, resources: [ResourceIdentifierInput]!, limit: Int = 25, cursor: String): CommentCollection
|
||||
}
|
||||
|
||||
type Comment {
|
||||
id: String!
|
||||
authorId: String!
|
||||
@@ -13,8 +19,8 @@ type Comment {
|
||||
|
||||
type CommentCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [Comment]
|
||||
cursor: DateTime!
|
||||
items: [Comment]!
|
||||
}
|
||||
|
||||
type ResourceIdentifier {
|
||||
@@ -50,7 +56,7 @@ type CommentReplyCollection {
|
||||
}
|
||||
|
||||
input CommentCreateInput {
|
||||
# streamId: String!
|
||||
streamId: String!
|
||||
resources: [ResourceIdentifierInput]!
|
||||
text: String!
|
||||
data: JSONObject!
|
||||
|
||||
@@ -7,6 +7,7 @@ exports.up = async ( knex ) => {
|
||||
table.timestamp( 'updatedAt' ).defaultTo( knex.fn.now( ) )
|
||||
table.string( 'text' )
|
||||
table.jsonb( 'data' )
|
||||
table.boolean( 'archived' ).defaultTo( false ).notNullable()
|
||||
table.string( 'parentComment', 10 ).references( 'id' ).inTable( 'comments' ).defaultTo( null )
|
||||
} )
|
||||
|
||||
|
||||
@@ -7,24 +7,45 @@ const Comments = () => knex( 'comments' )
|
||||
const StreamComments = () => knex( 'stream_comments' )
|
||||
const CommitComments = () => knex( 'commit_comments' )
|
||||
const ObjectComments = () => knex( 'object_comments' )
|
||||
const CommentReplies = () => knex( 'comment_replies' )
|
||||
const intersection = require( 'lodash.intersection' )
|
||||
|
||||
const persistResourceLinks = async ( commentId, resources ) => {
|
||||
for ( const resource of resources ) {
|
||||
switch ( resource.type ) {
|
||||
// having the type as a string here, kinda makes having two N mapping tables useless
|
||||
case 'stream':
|
||||
return await StreamComments().insert( { stream: resource.id, comment: commentId } )
|
||||
await StreamComments().insert( { stream: resource.id, comment: commentId } )
|
||||
break
|
||||
case 'commit':
|
||||
return await CommitComments().insert( { commit: resource.id, comment: commentId } )
|
||||
await CommitComments().insert( { commit: resource.id, comment: commentId } )
|
||||
break
|
||||
case 'object':
|
||||
return await ObjectComments().insert( { commit: resource.id, comment: commentId } )
|
||||
await ObjectComments().insert( { object: resource.id, comment: commentId } )
|
||||
break
|
||||
default:
|
||||
throw Error( `resource type ${resource.type} is not supported as a comment target` )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getCommentsIdsForResource = async ( { id, type } ) => {
|
||||
let commentLinks
|
||||
switch ( type ) {
|
||||
case 'stream':
|
||||
commentLinks = await StreamComments().where( { stream: id } )
|
||||
break
|
||||
case 'commit':
|
||||
commentLinks = await CommitComments().where( { commit: id } )
|
||||
break
|
||||
case 'object':
|
||||
commentLinks = await ObjectComments().where( { object: id } )
|
||||
break
|
||||
default:
|
||||
throw Error( `No comments are supported for ${resource.type}` )
|
||||
}
|
||||
return commentLinks.map( l => l.comment )
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
async createComment( { userId, input } ) {
|
||||
let comment = { ...input }
|
||||
@@ -44,51 +65,62 @@ module.exports = {
|
||||
// TODO
|
||||
},
|
||||
|
||||
// async archiveComment( {} ) {
|
||||
// // TODO
|
||||
// },
|
||||
async archiveComment( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async createCommentReply( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async editCommentReply( {} ) {
|
||||
// TODO
|
||||
},
|
||||
// async editCommentReply( {} ) {
|
||||
// // TODO
|
||||
// },
|
||||
|
||||
// async archiveCommentReply( {} ) {
|
||||
// // TODO
|
||||
// },
|
||||
|
||||
async getComments({ resources }) {
|
||||
|
||||
async getComment( id ) {
|
||||
const [ comment ] = await Comments().where( { id } )
|
||||
return comment
|
||||
},
|
||||
|
||||
async getStreamComments( { streamId, limit, archived, cursor } ) {
|
||||
// TODO
|
||||
limit = limit || 25
|
||||
let raw = `SELECT * from stream_comments
|
||||
JOIN comments ON comments.id = stream_comments."comment"
|
||||
WHERE stream_comments.stream = 'a55537c38f'
|
||||
ORDER BY comments."createdAt" DESC
|
||||
LIMIT 25
|
||||
`
|
||||
let query = Comments()
|
||||
.columns( [ 'id', 'authorId', 'archived', 'createdAt', 'updatedAt', 'text', 'data' ] )
|
||||
.select()
|
||||
.join( 'stream_comments', 'comment.id', 'stream_comments.commit' )
|
||||
async getComments( { resources, limit, cursor } ) {
|
||||
// maybe since we are so streamId limited, asking for a streamId here would make sense
|
||||
// and not treat the stream as a resource
|
||||
const commentIds = await Promise.all( resources.map( r => getCommentsIdsForResource( r ) ) )
|
||||
|
||||
const relevantComments = intersection( ...commentIds )
|
||||
const items = await Comments().whereIn( 'id', relevantComments ).orderBy( 'createdAt' ).limit( limit )
|
||||
cursor = items[items.length - 1].createdAt
|
||||
return { items, cursor, totalCount: relevantComments.length }
|
||||
},
|
||||
|
||||
async getCommitComments( { commitId, limit, archived, cursor } ) {
|
||||
// TODO
|
||||
},
|
||||
// async getStreamComments( { streamId, limit, archived, cursor } ) {
|
||||
// // TODO
|
||||
// limit = limit || 25
|
||||
// let raw = `SELECT * from stream_comments
|
||||
// JOIN comments ON comments.id = stream_comments."comment"
|
||||
// WHERE stream_comments.stream = 'a55537c38f'
|
||||
// ORDER BY comments."createdAt" DESC
|
||||
// LIMIT 25
|
||||
// `
|
||||
// let query = Comments()
|
||||
// .columns( [ 'id', 'authorId', 'archived', 'createdAt', 'updatedAt', 'text', 'data' ] )
|
||||
// .select()
|
||||
// .join( 'stream_comments', 'comment.id', 'stream_comments.commit' )
|
||||
// },
|
||||
|
||||
async getObjectComments( { objectId, limit, archived, cursor } ) {
|
||||
// TODO
|
||||
},
|
||||
// async getCommitComments( { commitId, limit, archived, cursor } ) {
|
||||
// // TODO
|
||||
// },
|
||||
|
||||
async getCommentReplies( { commentId } ) {
|
||||
// TODO
|
||||
}
|
||||
// async getObjectComments( { objectId, limit, archived, cursor } ) {
|
||||
// // TODO
|
||||
// },
|
||||
|
||||
// async getCommentReplies( { commentId } ) {
|
||||
// // TODO
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
Generated
+11
@@ -37,6 +37,7 @@
|
||||
"lodash.chunk": "^4.2.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.intersection": "^4.4.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.values": "^4.3.0",
|
||||
@@ -15234,6 +15235,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"node_modules/lodash.intersection": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz",
|
||||
"integrity": "sha1-ChG6Yx0OlcI8fy9Mu5ppLtF45wU="
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
@@ -35100,6 +35106,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.intersection": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz",
|
||||
"integrity": "sha1-ChG6Yx0OlcI8fy9Mu5ppLtF45wU="
|
||||
},
|
||||
"lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"lodash.chunk": "^4.2.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.intersection": "^4.4.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.values": "^4.3.0",
|
||||
|
||||
Reference in New Issue
Block a user