Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matteo/gql #14

Merged
merged 10 commits into from
Jul 29, 2020
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
spaces_around_brackets = both
49 changes: 41 additions & 8 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,46 @@
"parserOptions": {
"ecmaVersion": 11
},
"ignorePatterns": [ "modules/*/tests/*", "node_modules/*", "frontend/*"],
"ignorePatterns": [
"modules/*/tests/*",
"node_modules/*",
"frontend/*"
],
"rules": {
"arrow-spacing": [ 2, { "before": true, "after": true } ],
"array-bracket-spacing": [ 2, "always" ],
"block-spacing": [ 2, "always" ],
"camelcase": [ 1, { "properties": "always" } ],
"space-in-parens": [ 2, "always" ],
"keyword-spacing": 2
"arrow-spacing": [
2,
{
"before": true,
"after": true
}
],
"array-bracket-spacing": [
2,
"always"
],
"block-spacing": [
2,
"always"
],
"camelcase": [
1,
{
"properties": "always"
}
],
"space-in-parens": [
2,
"always"
],
"keyword-spacing": 2,
"semi": "off",
"indent": [
"error",
2
],
"padded-blocks": [
"error",
"never"
]
}
}
}
2 changes: 2 additions & 0 deletions modules/core/graph/resolvers/branches.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ module.exports = {
Stream: {

async branches( parent, args, context, info ) {
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 items, please use pagination.' )
let { items, cursor, totalCount } = await getBranchesByStreamId( { streamId: parent.id, limit: args.limit, cursor: args.cursor } )

return { totalCount, cursor, items }
Expand Down
7 changes: 6 additions & 1 deletion modules/core/graph/resolvers/commits.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ module.exports = {
Stream: {

async commits( parent, args, context, info ) {
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 items, please use pagination.' )
let { commits: items, cursor } = await getCommitsByStreamId( { streamId: parent.id, limit: args.limit, cursor: args.cursor } )
let totalCount = await getCommitsTotalCountByStreamId( { streamId: parent.id } )

Expand All @@ -47,9 +49,10 @@ module.exports = {
User: {

async commits( parent, args, context, info ) {

let publicOnly = context.userId !== parent.id
let totalCount = await getCommitsTotalCountByUserId( { userId: parent.id } )
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 items, please use pagination.' )
let { commits: items, cursor } = await getCommitsByUserId( { userId: parent.id, limit: args.limit, cursor: args.cursor, publicOnly } )

return { items, cursor, totalCount }
Expand All @@ -58,6 +61,8 @@ module.exports = {
},
Branch: {
async commits( parent, args, context, info ) {
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 items, please use pagination.' )
let { commits, cursor } = await getCommitsByBranchId( { branchId: parent.id, limit: args.limit, cursor: args.cursor } )
let totalCount = await getCommitsTotalCountByBranchId( { branchId: parent.id } )

Expand Down
15 changes: 14 additions & 1 deletion modules/core/graph/resolvers/streams.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ 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 )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The discussion around max limits & default limits:

  • max limits should be implemented everywhere that returns a collection (so we don't get stuff like limit: 1000000 to potentially crash UIs
  • default limits: should be a wee bit more conservative, depending on the perceived user-land usage:
    • e.g. commits: we can potentially display a longer list, so it can be higher (50?)
    • e.g. streams: i don't think more than 20-ish? 25? would comfortably fit on a page before you need to switch to either a search or a "next page".

Another note, if we want to be lenient, we could just return the default max limit rather than throw an error, but I guess it's good to inform the dev 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, changed all the defaults to 25. Will keep the error as IMHO it's a better experience than having to open the docs and check why out of my ~123 streams only 100 are showing (happened to me in he past).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deal!

throw new UserInputError( 'Cannot return more than 100 items, please use pagination.' )

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: {
Expand All @@ -35,7 +46,9 @@ module.exports = {
User: {

async streams( parent, args, context, info ) {
// Return only the user's public streams if parent.id !== context.userId
if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 items, please use pagination.' )
// 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 } )

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'
const appRoot = require( 'app-root-path' )
const { ApolloError, AuthenticationError, UserInputError } = require( 'apollo-server-express' )
const { createUser, getUser, getUserByEmail, getUserRole, updateUser, deleteUser, validatePasssword } = require( '../../services/users' )
const { createUser, getUser, getUserByEmail, getUserRole, updateUser, deleteUser, searchUsers, validatePasssword } = require( '../../services/users' )
const { createPersonalAccessToken, createAppToken, revokeToken, revokeTokenById, validateToken, getUserTokens } = require( '../../services/tokens' )
const { validateServerRole, validateScopes, authorizeResolver } = require( `${appRoot}/modules/shared` )
const setupCheck = require( `${appRoot}/setupcheck` )
Expand All @@ -10,12 +10,11 @@ const zxcvbn = require( 'zxcvbn' )
module.exports = {
Query: {

async _( ) {
async _() {
return `Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn.`
},

async user( parent, args, context, info ) {

await validateServerRole( context, 'server:user' )

if ( !args.id )
Expand All @@ -30,6 +29,22 @@ module.exports = {
return await getUser( args.id || context.userId )
},

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.length < 3 )
throw new UserInputError( 'Search query must be at least 3 carachters.' )


if ( args.limit && args.limit > 100 )
throw new UserInputError( 'Cannot return more than 100 items, please use pagination.' )

let {cursor, users} = await searchUsers( args.query, args.limit, args.cursor )
return {cursor: cursor, items: users}
},

async userPwdStrength( parent, args, context, info ) {
let res = zxcvbn( args.pwd )
return { score: res.score, feedback: res.feedback }
Expand Down Expand Up @@ -64,11 +79,13 @@ module.exports = {

},



Mutation: {
async userEdit( parent, args, context, info ) {
await validateServerRole( context, 'server:user' )
await updateUser( context.userId, args.user )
return true
}
}
}
}
8 changes: 4 additions & 4 deletions modules/core/graph/schemas/branchesAndCommits.graphql
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
extend type Stream {
commits( limit: Int! = 20, cursor: String ): CommitCollection
commits( limit: Int! = 25, cursor: String ): CommitCollection
commit( id: String! ): Commit
branches( limit: Int! = 100, cursor: String ): BranchCollection
branches( limit: Int! = 25, cursor: String ): BranchCollection
branch( name: String! ): Branch
}

extend type User {
commits( limit: Int! = 20, cursor: String ): CommitCollectionUser
commits( limit: Int! = 25, cursor: String ): CommitCollectionUser
}

type Branch {
id: String!
name: String!
author: User!
description: String!
commits( limit: Int! = 20, cursor: String ): CommitCollection
commits( limit: Int! = 25, cursor: String ): CommitCollection
}

type Commit {
Expand Down
9 changes: 7 additions & 2 deletions modules/core/graph/schemas/streams.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
extend type Query {
stream( id: String! ): Stream
"""
All the streams of the current user, pass in the `query` parameter to seach by name, description or ID.
"""
streams( query: String!, limit: Int! = 25, cursor: String ): StreamCollection
}

type Stream {
Expand All @@ -16,7 +20,7 @@ extend type User {
"""
All the streams that a user has access to.
"""
streams( limit: Int! = 20, cursor: String ): StreamCollectionUser
streams( limit: Int! = 25, cursor: String ): StreamCollection
}

type StreamCollaborator {
Expand All @@ -25,12 +29,13 @@ type StreamCollaborator {
role: String!
}

type StreamCollectionUser {
type StreamCollection {
totalCount: Int!
cursor: String
items: [ Stream ]
}


extend type Mutation {
"""
Creates a new stream.
Expand Down
16 changes: 16 additions & 0 deletions modules/core/graph/schemas/user.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +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
userSearch( query: String!, limit: Int! = 25, cursor: String ): UserSearchResultCollection
userPwdStrength( pwd: String! ): JSONObject
}

Expand All @@ -22,6 +23,21 @@ type User {
role: String
}

type UserSearchResultCollection {
cursor: String
items: [ UserSearchResult ]
}

type UserSearchResult {
id: String!
username: String
name: String
bio: String
company: String
avatar: String
verified: Boolean
}

extend type Mutation {
"""
Edits a user's profile.
Expand Down
86 changes: 43 additions & 43 deletions modules/core/migrations/002-2020-05-18-scopes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,50 @@ exports.up = async knex => {
debug( 'Setting up core module scopes.' )

let coreModuleScopes = [ {
name: 'server:setup',
description: 'Edit server information.'
},
{
name: 'tokens:read',
description: `Access your api tokens.`
},
{
name: 'tokens:write',
description: `Create and delete api tokens on your behalf.`
},
{
name: 'apps:authorize',
description: 'Grant third party applications access rights on your behalf to the api.'
},
{
name: 'apps:create',
description: 'Register a third party application.'
},
{
name: 'streams:read',
description: 'Read your streams & and any associated information (branches, tags, comments, objects, etc.)'
},
{
name: 'streams:write',
description: 'Create streams on your behalf and read your streams & any associated information (any associated information (branches, tags, comments, objects, etc.)'
},
{
name: 'profile:read',
description: `Read your profile information`
},
{
name: 'profile:email',
description: `Access your email.`
},
name: 'server:setup',
description: 'Edit server information.'
},
{
name: 'tokens:read',
description: `Access your api tokens.`
},
{
name: 'tokens:write',
description: `Create and delete api tokens on your behalf.`
},
{
name: 'apps:authorize',
description: 'Grant third party applications access rights on your behalf to the api.'
},
{
name: 'apps:create',
description: 'Register a third party application.'
},
{
name: 'streams:read',
description: 'Read your streams & and any associated information (branches, tags, comments, objects, etc.)'
},
{
name: 'streams:write',
description: 'Create streams on your behalf and read your streams & any associated information (any associated information (branches, tags, comments, objects, etc.)'
},
{
name: 'profile:read',
description: `Read your profile information`
},
{
name: 'profile:email',
description: `Access your email.`
},

{
name: 'users:read',
description: `Read other users' profile on your behalf.`
},
{
name: 'users:email',
description: 'Access the emails of other users.'
}
{
name: 'users:read',
description: `Read other users' profile on your behalf.`
},
{
name: 'users:email',
description: 'Access the emails of other users.'
}
]

await knex( 'scopes' ).insert( coreModuleScopes )
Expand Down
3 changes: 1 addition & 2 deletions modules/core/services/branches.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,7 +31,7 @@ module.exports = {
},

async getBranchesByStreamId( { streamId, limit, cursor } ) {
limit = limit || 100
limit = limit || 25
let query = Branches( ).select( '*' ).where( { streamId: streamId } )

if ( cursor )
Expand Down
Loading