feat(gql): adds streamSearch, adds pagination to userSearch

This commit is contained in:
Matteo Cominetti
2020-07-28 16:20:45 +01:00
parent d2db31381b
commit e51345cb9e
7 changed files with 84 additions and 37 deletions
+24
View File
@@ -22,6 +22,28 @@ module.exports = {
let stream = await getStream( { streamId: args.id } )
return stream
},
async streams( parent, args, context, info ) {
await validateScopes( context.scopes, 'streams:read' )
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 results.' )
let totalCount = await getUserStreamsCount( {userId: context.userId, publicOnly: false} )
let {cursor, streams} = await getUserStreams( {userId: context.userId, limit: args.limit, cursor: args.cursor, publicOnly: false} )
return {totalCount, cursor: cursor, items: streams}
},
async streamSearch( parent, args, context, info ) {
await validateScopes( context.scopes, 'streams:read' )
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 results.' )
let totalCount = await getUserStreamsCount( {userId: context.userId, publicOnly: false, searchQuery: args.query} )
let {cursor, streams} = await getUserStreams( {userId: context.userId, limit: args.limit, cursor: args.cursor, publicOnly: false, searchQuery: args.query} )
return {totalCount, cursor: cursor, items: streams}
}
},
Stream: {
@@ -35,6 +57,8 @@ module.exports = {
User: {
async streams( parent, args, context, info ) {
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 results.' )
// Return only the user's public streams if parent.id !== context.userId
let publicOnly = parent.id !== context.userId
let totalCount = await getUserStreamsCount( { userId: parent.id, publicOnly } )
@@ -29,24 +29,20 @@ module.exports = {
return await getUser( args.id || context.userId )
},
async userSearchResults( parent, args, context, info ) {
async userSearch( parent, args, context, info ) {
await validateServerRole( context, 'server:user' )
await validateScopes( context.scopes, 'profile:read' )
await validateScopes( context.scopes, 'users:read' )
if ( !args.query ) {
throw new UserInputError( 'You must provide a search query.' )
}
if ( args.query.length < 3 ) {
if ( args.query.length < 3 )
throw new UserInputError( 'Search query must be at least 3 carachters.' )
}
if ( args.limit && args.limit > 100 ) {
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 results.' )
}
return await searchUsers( args.query, args.limit )
let {cursor, users} = await searchUsers( args.query, args.limit, args.cursor )
return {cursor: cursor, items: users}
},
async userPwdStrength( parent, args, context, info ) {
+5 -2
View File
@@ -1,5 +1,7 @@
extend type Query {
stream( id: String! ): Stream
streams( limit: Int! = 100, cursor: String ): StreamCollection
streamSearch( query: String!, limit: Int! = 100, cursor: String ): StreamCollection
}
type Stream {
@@ -16,7 +18,7 @@ extend type User {
"""
All the streams that a user has access to.
"""
streams( limit: Int! = 20, cursor: String ): StreamCollectionUser
streams( limit: Int! = 20, cursor: String ): StreamCollection
}
type StreamCollaborator {
@@ -25,12 +27,13 @@ type StreamCollaborator {
role: String!
}
type StreamCollectionUser {
type StreamCollection {
totalCount: Int!
cursor: String
items: [ Stream ]
}
extend type Mutation {
"""
Creates a new stream.
+6 -1
View File
@@ -3,7 +3,7 @@ extend type Query {
Gets the profile of a user. If no id argument is provided, will return the current authenticated user's profile (as extracted from the authorization header).
"""
user( id: String ): User
userSearchResults(query: String, limit: Int! = 100): [UserSearchResult!]!
userSearch( query: String!, limit: Int! = 100, cursor: String ): UserSearchResultCollection
userPwdStrength( pwd: String! ): JSONObject
}
@@ -23,6 +23,11 @@ type User {
role: String
}
type UserSearchResultCollection {
cursor: String
items: [ UserSearchResult ]
}
type UserSearchResult {
id: String!
username: String
-1
View File
@@ -10,7 +10,6 @@ const BranchCommits = ( ) => knex( 'branch_commits' )
module.exports = {
async createBranch( { name, description, streamId, authorId } ) {
let branch = {}
branch.id = crs( { length: 10 } )
branch.streamId = streamId
+22 -9
View File
@@ -89,10 +89,10 @@ module.exports = {
return await Streams( ).where( { id: streamId } ).del( )
},
async getUserStreams( { userId, limit, cursor, publicOnly } ) {
async getUserStreams( {userId, limit, cursor, publicOnly, searchQuery } ) {
limit = limit || 100
publicOnly = publicOnly !== false //defaults to true if not provided
let likeQuery = "%" + searchQuery + "%"
let query = Acl( )
.columns( [ { id: 'streams.id' }, 'name', 'description', 'isPublic', 'createdAt', 'updatedAt', 'role' ] ).select( )
.join( 'streams', 'stream_acl.resourceId', 'streams.id' )
@@ -104,16 +104,22 @@ module.exports = {
if ( publicOnly )
query.andWhere( 'streams.isPublic', true )
if ( searchQuery )
query.andWhere( function () {
this.where( 'name', 'ILIKE', likeQuery )
.orWhere( 'description', 'ILIKE', likeQuery )
.orWhere( 'id', 'ILIKE', likeQuery ) //potentially useless?
} )
query.orderBy( 'streams.updatedAt', 'desc' ).limit( limit )
let rows = await query
return { streams: rows, cursor: rows.length > 0 ? rows[ rows.length - 1 ].updatedAt.toISOString( ) : null }
},
async getUserStreamsCount( { userId, publicOnly } ) {
async getUserStreamsCount( {userId, publicOnly, searchQuery } ) {
publicOnly = publicOnly !== false //defaults to true if not provided
let likeQuery = "%" + searchQuery + "%"
let query = Acl( ).count( )
.join( 'streams', 'stream_acl.resourceId', 'streams.id' )
.where( { userId: userId } )
@@ -121,6 +127,13 @@ module.exports = {
if ( publicOnly )
query.andWhere( 'streams.isPublic', true )
if ( searchQuery )
query.andWhere( function () {
this.where( 'name', 'ILIKE', likeQuery )
.orWhere( 'description', 'ILIKE', likeQuery )
.orWhere( 'id', 'ILIKE', likeQuery ) //potentially useless?
} )
let [ res ] = await query
return parseInt( res.count )
},
@@ -128,10 +141,10 @@ module.exports = {
async getStreamUsers( { streamId } ) {
let query =
Acl( ).columns( { role: 'stream_acl.role' }, 'id', 'name' ).select( )
.where( { resourceId: streamId } )
.rightJoin( 'users', { 'users.id': 'stream_acl.userId' } )
.select( 'stream_acl.role', 'username', 'name', 'id' )
.orderBy( 'stream_acl.role' )
.where( { resourceId: streamId } )
.rightJoin( 'users', { 'users.id': 'stream_acl.userId' } )
.select( 'stream_acl.role', 'username', 'name', 'id' )
.orderBy( 'stream_acl.role' )
return await query
}
+20 -13
View File
@@ -5,7 +5,7 @@ const appRoot = require( 'app-root-path' )
const knex = require( `${appRoot}/db/knex` )
const Users = () => knex( 'users' )
const ServerRoles = () => knex( 'server_acl' )
const Acl = () => knex( 'server_acl' )
module.exports = {
@@ -16,7 +16,7 @@ module.exports = {
*/
async createUser( user ) {
let [ {count} ] = await ServerRoles().where( {role: 'server:admin'} ).count()
let [ {count} ] = await Acl().where( {role: 'server:admin'} ).count()
user.id = crs( {length: 10} )
@@ -31,9 +31,9 @@ module.exports = {
let res = await Users().returning( 'id' ).insert( user )
if ( parseInt( count ) === 0 ) {
await ServerRoles().insert( {userId: res[0], role: 'server:admin'} )
await Acl().insert( {userId: res[0], role: 'server:admin'} )
} else {
await ServerRoles().insert( {userId: res[0], role: 'server:user'} )
await Acl().insert( {userId: res[0], role: 'server:user'} )
}
return res[0]
@@ -70,7 +70,7 @@ module.exports = {
},
async getUserRole( id ) {
let {role} = await ServerRoles().where( {userId: id} ).select( 'role' ).first()
let {role} = await Acl().where( {userId: id} ).select( 'role' ).first()
return role
},
@@ -82,16 +82,23 @@ module.exports = {
await Users().where( {id: id} ).update( user )
},
async searchUsers( query, limit ) {
async searchUsers( searchQuery, limit, cursor ) {
limit = limit || 100
let likeQuery = "%" + query + "%"
let users = await Users()
.where( {email: query} ) //match full email or partial username / name
.orWhere( 'username', 'like', likeQuery )
.orWhere( 'name', 'like', likeQuery )
.limit( limit )
let likeQuery = "%" + searchQuery + "%"
let query = Users()
.where( function () {
this.where( {email: searchQuery} ) //match full email or partial username / name
.orWhere( 'username', 'ILIKE', likeQuery )
.orWhere( 'name', 'ILIKE', likeQuery )
} )
return users
if ( cursor )
query.andWhere( 'users.createdAt', '<', cursor )
query.orderBy( 'users.createdAt', 'desc' ).limit( limit )
let rows = await query
return {users: rows, cursor: rows.length > 0 ? rows[rows.length - 1].createdAt.toISOString() : null}
},
async validatePasssword( {email, password} ) {