feat(comments): scaffolding migrations, resolvers, etc
This commit is contained in:
@@ -3,9 +3,27 @@ const { authorizeResolver, pubsub } = require( `${appRoot}/modules/shared` )
|
||||
const { ForbiddenError, UserInputError, ApolloError, withFilter } = require( 'apollo-server-express' )
|
||||
const { getStream } = require( '../../../core/services/streams' )
|
||||
|
||||
module.exports = {
|
||||
Query: {},
|
||||
Mutation:{
|
||||
module.exports = {
|
||||
Stream: {
|
||||
async comments( parent, args, context, info ) {
|
||||
// TODO
|
||||
},
|
||||
async comment( parent, args, context, info ) {
|
||||
// TODO
|
||||
}
|
||||
},
|
||||
Commit: {
|
||||
async comments( parent, args, context, info ) {
|
||||
// TODO
|
||||
}
|
||||
},
|
||||
Object: {
|
||||
async comments( parent, args, context, info ) {
|
||||
// TODO
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
// Used for broadcasting real time chat head bubbles and status.
|
||||
async userCommentActivityBroadcast( parent, args, context, info ) {
|
||||
let stream = await getStream( { streamId: args.streamId, userId: context.userId } )
|
||||
if ( !stream )
|
||||
@@ -25,16 +43,33 @@ module.exports = {
|
||||
} )
|
||||
return true
|
||||
},
|
||||
async userCommentCreate( parent, args, context, info ) {
|
||||
|
||||
async commentCreate( parent, args, context, info ) {
|
||||
// TODO: check perms, persist comment
|
||||
console.log( args )
|
||||
// TODO: persist comment
|
||||
|
||||
await pubsub.publish( 'COMMENT_CREATED', {
|
||||
userCommentCreated: args.comment,
|
||||
commentCreated: args.comment,
|
||||
streamId: args.streamId,
|
||||
resourceId: args.resourceId
|
||||
} )
|
||||
return true
|
||||
}
|
||||
},
|
||||
async commentEdit( parent, args, context, info ) {
|
||||
// TODO
|
||||
},
|
||||
async commentReply( parent, args, context, info ) {
|
||||
// TODO
|
||||
await pubsub.publish( 'COMMENT_REPLY_CREATED', {
|
||||
commentCreated: args.comment,
|
||||
streamId: args.streamId,
|
||||
resourceId: args.resourceId
|
||||
} )
|
||||
return true
|
||||
},
|
||||
async commentReplyEdit( parent, args, context, info ) {
|
||||
// TODO
|
||||
},
|
||||
},
|
||||
Subscription:{
|
||||
userCommentActivity: {
|
||||
@@ -43,11 +78,17 @@ module.exports = {
|
||||
return payload.streamId === variables.streamId && payload.resourceId === variables.resourceId
|
||||
} )
|
||||
},
|
||||
userCommentCreated: {
|
||||
commentCreated: {
|
||||
subscribe: withFilter( () => pubsub.asyncIterator( [ 'COMMENT_CREATED' ] ), async( payload, variables, context ) => {
|
||||
await authorizeResolver( context.userId, payload.streamId, 'stream:reviewer' )
|
||||
return payload.streamId === variables.streamId && payload.resourceId === variables.resourceId
|
||||
} )
|
||||
},
|
||||
commentReplyCreated: {
|
||||
subscribe: withFilter( () => pubsub.asyncIterator( [ 'COMMENT_REPLY_CREATED' ] ), async( payload, variables, context ) => {
|
||||
await authorizeResolver( context.userId, payload.streamId, 'stream:reviewer' )
|
||||
return payload.streamId === variables.streamId && payload.resourceId === variables.resourceId
|
||||
} )
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,97 @@
|
||||
type Comment {
|
||||
id: String!
|
||||
authorId: String!
|
||||
archived: Boolean!
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
text: String!
|
||||
data: JSONObject!
|
||||
resources: [String]!
|
||||
replies: CommentReplyCollection
|
||||
}
|
||||
|
||||
type CommentReply {
|
||||
id: String!
|
||||
authorId: String!
|
||||
archived: Boolean!
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
text: String!
|
||||
}
|
||||
|
||||
type CommentCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [Comment]
|
||||
}
|
||||
|
||||
type CommentReplyCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [CommentReply]
|
||||
}
|
||||
|
||||
input CommentCreateInput {
|
||||
streamId: String!
|
||||
resources: [String]!
|
||||
text: String!
|
||||
data: JSONObject!
|
||||
}
|
||||
|
||||
input CommentEditInput {
|
||||
streamId: String!
|
||||
id: String!
|
||||
text: String!
|
||||
data: JSONObject!
|
||||
}
|
||||
|
||||
input ReplyCreateInput {
|
||||
text: String!
|
||||
data: JSONObject!
|
||||
}
|
||||
|
||||
input ReplyEditInput {
|
||||
id: String!
|
||||
text: String!
|
||||
data: JSONObject!
|
||||
}
|
||||
|
||||
extend type Stream {
|
||||
comments(limit: Int! = 20, archived: Boolean = false, cursor: String): Boolean
|
||||
comment(id: String!): Comment
|
||||
}
|
||||
|
||||
extend type Commit {
|
||||
comments(limit: Int! = 20, cursor: String): Boolean
|
||||
}
|
||||
|
||||
extend type Object {
|
||||
comments(limit: Int! = 20, cursor: String): Boolean
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
# Used for broadcasting real time chat head bubbles and status.
|
||||
userCommentActivityBroadcast(
|
||||
streamId: String!
|
||||
resourceId: String!
|
||||
data: JSONObject
|
||||
): Boolean! @hasRole(role: "server:user") @hasScope(scope: "streams:read")
|
||||
|
||||
userCommentCreate(
|
||||
streamId: String!
|
||||
resourceId: String!
|
||||
comment: JSONObject!
|
||||
): Boolean! @hasRole(role: "server:user") @hasScope(scope: "streams:read")
|
||||
commentCreate(input: CommentCreateInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
commentEdit(streamId: String!, comment: JSONObject!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
commentReply(streamId: String!, commentReply: JSONObject!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
commentReplyEdit(streamId: String!, commentReply: JSONObject!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
@@ -17,14 +99,11 @@ extend type Subscription {
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
userCommentCreated(streamId: String!, resourceId: String!): JSONObject
|
||||
commentCreated(streamId: String!, resourceId: String!): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
commentReplyCreated(streamId: String!, commentId: String!): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
}
|
||||
|
||||
type Comment {
|
||||
id: String!
|
||||
text: String!
|
||||
authorId: String!
|
||||
# TBD: how do we index on streams, commits, objects, selected objects, etc.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// /* istanbul ignore file */
|
||||
exports.up = async ( knex ) => {
|
||||
await knex.schema.createTable( 'comments', table => {
|
||||
table.string( 'id', 10 ).primary( )
|
||||
table.string( 'authorId', 10 ).references( 'id' ).inTable( 'users' ).notNullable().index( )
|
||||
table.boolean( 'archived' ).defaultTo( false ).index( )
|
||||
table.timestamp( 'createdAt' ).defaultTo( knex.fn.now( ) )
|
||||
table.timestamp( 'updatedAt' ).defaultTo( knex.fn.now( ) )
|
||||
table.string( 'text' )
|
||||
table.jsonb( 'data' )
|
||||
} )
|
||||
|
||||
// Comments -< Replies
|
||||
await knex.schema.createTable( 'comment_replies', table => {
|
||||
table.string( 'id', 10 ).primary( )
|
||||
table.string( 'authorId', 10 ).references( 'id' ).inTable( 'users' ).notNullable().index( )
|
||||
table.boolean( 'archived' ).defaultTo( false ).index( )
|
||||
table.timestamp( 'createdAt' ).defaultTo( knex.fn.now( ) )
|
||||
table.timestamp( 'updatedAt' ).defaultTo( knex.fn.now( ) )
|
||||
table.string( 'text' )
|
||||
} )
|
||||
|
||||
// Streams >- -< Comments
|
||||
// Minor futureproofing: a comment can be written "on top of" multiple resources from multiple streams.
|
||||
await knex.schema.createTable( 'stream_comments', table => {
|
||||
table.string( 'stream', 10 ).references( 'id' ).inTable( 'streams' ).notNullable()
|
||||
table.string( 'comment', 10 ).references( 'id' ).inTable( 'comments' ).notNullable()
|
||||
} )
|
||||
|
||||
// Commits >- -< Comments
|
||||
await knex.schema.createTable( 'commit_comments', table => {
|
||||
table.string( 'commit', 10 ).references( 'id' ).inTable( 'commits' ).notNullable()
|
||||
table.string( 'comment', 10 ).references( 'id' ).inTable( 'comments' ).notNullable()
|
||||
} )
|
||||
|
||||
// Objects >- -< Comments
|
||||
await knex.schema.createTable( 'object_comments', table => {
|
||||
table.string( 'object' ) // note: not a FK because we don't enforce uniqeness to speed things up
|
||||
table.string( 'comment', 10 ).references( 'id' ).inTable( 'comments' ).notNullable()
|
||||
table.index( [ 'object', 'comment' ] )
|
||||
} )
|
||||
}
|
||||
|
||||
exports.down = async ( knex ) => {
|
||||
await knex.schema.dropTableIfExists( 'object_comments' )
|
||||
await knex.schema.dropTableIfExists( 'commit_comments' )
|
||||
await knex.schema.dropTableIfExists( 'stream_comments' )
|
||||
await knex.schema.dropTableIfExists( 'comment_replies' )
|
||||
await knex.schema.dropTableIfExists( 'comments' )
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
'use strict'
|
||||
|
||||
const appRoot = require( 'app-root-path' )
|
||||
const knex = require( `${appRoot}/db/knex` )
|
||||
|
||||
const Comments = () => knex( 'comments' )
|
||||
const CommentReplies = () => knex( 'comments' )
|
||||
|
||||
module.exports = {
|
||||
async createComment( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async editComment( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async archiveComment( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async createCommentReply( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async editCommentReply( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async archiveCommentReply( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async getStreamComments( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async getCommitComments( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async getObjectComments( {} ) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
async getCommentReplies( {} ) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,21 @@ extend type Query {
|
||||
"""
|
||||
Returns a specific stream.
|
||||
"""
|
||||
stream( id: String! ): Stream
|
||||
stream(id: String!): Stream
|
||||
|
||||
"""
|
||||
All the streams of the current user, pass in the `query` parameter to search by name, description or ID.
|
||||
"""
|
||||
streams( query: String, limit: Int = 25, cursor: String ): StreamCollection
|
||||
streams(query: String, limit: Int = 25, cursor: String): StreamCollection
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
adminStreams( offset: Int = 0, query: String, orderBy: String, visibility: String, limit: Int = 25 ): StreamCollection
|
||||
@hasRole(role: "server:admin")
|
||||
adminStreams(
|
||||
offset: Int = 0
|
||||
query: String
|
||||
orderBy: String
|
||||
visibility: String
|
||||
limit: Int = 25
|
||||
): StreamCollection @hasRole(role: "server:admin")
|
||||
}
|
||||
|
||||
type Stream {
|
||||
@@ -25,7 +30,7 @@ type Stream {
|
||||
role: String
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
collaborators: [ StreamCollaborator ]!
|
||||
collaborators: [StreamCollaborator]!
|
||||
size: String
|
||||
}
|
||||
|
||||
@@ -33,7 +38,7 @@ extend type User {
|
||||
"""
|
||||
All the streams that a user has access to.
|
||||
"""
|
||||
streams( limit: Int! = 25, cursor: String ): StreamCollection
|
||||
streams(limit: Int! = 25, cursor: String): StreamCollection
|
||||
}
|
||||
|
||||
type StreamCollaborator {
|
||||
@@ -47,48 +52,45 @@ type StreamCollaborator {
|
||||
type StreamCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [ Stream ]
|
||||
items: [Stream]
|
||||
}
|
||||
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
Creates a new stream.
|
||||
"""
|
||||
streamCreate( stream: StreamCreateInput! ): String
|
||||
streamCreate(stream: StreamCreateInput!): String
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
"""
|
||||
Updates an existing stream.
|
||||
"""
|
||||
streamUpdate( stream: StreamUpdateInput! ): Boolean!
|
||||
streamUpdate(stream: StreamUpdateInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
"""
|
||||
Deletes an existing stream.
|
||||
"""
|
||||
streamDelete( id: String! ): Boolean!
|
||||
streamDelete(id: String!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
|
||||
streamsDelete( ids: [String!] ): Boolean!
|
||||
@hasRole(role: "server:admin")
|
||||
streamsDelete(ids: [String!]): Boolean! @hasRole(role: "server:admin")
|
||||
"""
|
||||
Grants permissions to a user on a given stream.
|
||||
"""
|
||||
streamGrantPermission( permissionParams: StreamGrantPermissionInput! ): Boolean
|
||||
streamGrantPermission(permissionParams: StreamGrantPermissionInput!): Boolean
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
"""
|
||||
Revokes the permissions of a user on a given stream.
|
||||
"""
|
||||
streamRevokePermission( permissionParams: StreamRevokePermissionInput! ): Boolean
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
streamRevokePermission(
|
||||
permissionParams: StreamRevokePermissionInput!
|
||||
): Boolean @hasRole(role: "server:user") @hasScope(scope: "streams:write")
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
|
||||
#
|
||||
# User bound subscriptions that operate on the stream collection of an user
|
||||
# Example relevant view/usecase: updating the list of streams for a user.
|
||||
@@ -118,17 +120,16 @@ extend type Subscription {
|
||||
"""
|
||||
Subscribes to stream updated event. Use this in clients/components that pertain only to this stream.
|
||||
"""
|
||||
streamUpdated( streamId: String ): JSONObject
|
||||
streamUpdated(streamId: String): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
"""
|
||||
Subscribes to stream deleted event. Use this in clients/components that pertain only to this stream.
|
||||
"""
|
||||
streamDeleted( streamId: String ): JSONObject
|
||||
streamDeleted(streamId: String): JSONObject
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
}
|
||||
|
||||
input StreamCreateInput {
|
||||
@@ -145,12 +146,12 @@ input StreamUpdateInput {
|
||||
}
|
||||
|
||||
input StreamGrantPermissionInput {
|
||||
streamId: String!,
|
||||
userId: String!,
|
||||
streamId: String!
|
||||
userId: String!
|
||||
role: String!
|
||||
}
|
||||
|
||||
input StreamRevokePermissionInput {
|
||||
streamId: String!,
|
||||
streamId: String!
|
||||
userId: String!
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user