diff --git a/.eslintrc.json b/.eslintrc.json index 9b4e6f9a52..afaeee7f71 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,6 +7,7 @@ "parserOptions": { "ecmaVersion": 11 }, + "ignorePatterns": [ "modules/*/tests/*", "node_modules/*", "frontend/*"], "rules": { "arrow-spacing": [ 2, { "before": true, "after": true } ], "array-bracket-spacing": [ 2, "always" ], diff --git a/modules/core/graph/resolvers/streams.js b/modules/core/graph/resolvers/streams.js index 3635fef54b..df53641edb 100644 --- a/modules/core/graph/resolvers/streams.js +++ b/modules/core/graph/resolvers/streams.js @@ -1,92 +1,92 @@ 'use strict' -const { AuthorizationError, ApolloError } = require('apollo-server-express') -const appRoot = require('app-root-path') +const { AuthorizationError, ApolloError } = require( 'apollo-server-express' ) +const appRoot = require( 'app-root-path' ) const { - createStream, - getStream, - updateStream, - deleteStream, - getUserStreams, - getUserStreamsCount, - getStreamUsers, - grantPermissionsStream, - revokePermissionsStream -} = require('../../services/streams') -const { validateServerRole, validateScopes, authorizeResolver } = require(`${appRoot}/modules/shared`) + createStream, + getStream, + updateStream, + deleteStream, + getUserStreams, + getUserStreamsCount, + getStreamUsers, + grantPermissionsStream, + revokePermissionsStream +} = require( '../../services/streams' ) +const { validateServerRole, validateScopes, authorizeResolver } = require( `${appRoot}/modules/shared` ) module.exports = { - Query: { - async stream(parent, args, context, info) { - await validateScopes(context.scopes, 'streams:read') - await authorizeResolver(context.userId, args.id, 'stream:reviewer') - - let stream = await getStream({ streamId: args.id }) - return stream - } + Query: { + async stream( parent, args, context, info ) { + await validateScopes( context.scopes, 'streams:read' ) + await authorizeResolver( context.userId, args.id, 'stream:reviewer' ) + + let stream = await getStream( { streamId: args.id } ) + return stream + } + }, + Stream: { + + async collaborators( parent, args, context, info ) { + let users = await getStreamUsers( { streamId: parent.id } ) + return users + } + + }, + User: { + + async streams( parent, args, context, info ) { + // 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 } ) + + let { cursor, streams } = await getUserStreams( { userId: parent.id, limit: args.limit, cursor: args.cursor, publicOnly: publicOnly } ) + return { totalCount, cursor: cursor, items: streams } + } + + }, + Mutation: { + async streamCreate( parent, args, context, info ) { + await validateServerRole( context, 'server:user' ) + await validateScopes( context.scopes, 'streams:write' ) + + let id = await createStream( { ...args.stream, ownerId: context.userId } ) + return id }, - Stream: { - async collaborators(parent, args, context, info) { - let users = await getStreamUsers({ streamId: parent.id }) - return users - } + async streamUpdate( parent, args, context, info ) { + await validateServerRole( context, 'server:user' ) + await validateScopes( context.scopes, 'streams:write' ) + await authorizeResolver( context.userId, args.stream.id, 'stream:owner' ) + await updateStream( { streamId: args.stream.id, name: args.stream.name, description: args.stream.description } ) + return true }, - User: { - async streams(parent, args, context, info) { - // 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 }) + async streamDelete( parent, args, context, info ) { + await validateServerRole( context, 'server:user' ) + await validateScopes( context.scopes, 'streams:write' ) + await authorizeResolver( context.userId, args.id, 'stream:owner' ) + + await deleteStream( { streamId: args.id } ) + return true + }, - let { cursor, streams } = await getUserStreams({ userId: parent.id, limit: args.limit, cursor: args.cursor, publicOnly: publicOnly }) - return { totalCount, cursor: cursor, items: streams } - } + async streamGrantPermission( parent, args, context, info ) { + await validateServerRole( context, 'server:user' ) + await validateScopes( context.scopes, 'streams:write' ) + await authorizeResolver( context.userId, args.streamId, 'stream:owner' ) + if ( context.userId === args.userId ) throw new Error( 'You cannot set roles for yourself.' ) + + return await grantPermissionsStream( { streamId: args.streamId, userId: args.userId, role: args.role.toLowerCase( ) || 'read' } ) }, - Mutation: { - async streamCreate(parent, args, context, info) { - await validateServerRole(context, 'server:user') - await validateScopes(context.scopes, 'streams:write') - - let id = await createStream({...args.stream, ownerId: context.userId }) - return id - }, - - async streamUpdate(parent, args, context, info) { - await validateServerRole(context, 'server:user') - await validateScopes(context.scopes, 'streams:write') - await authorizeResolver(context.userId, args.stream.id, 'stream:owner') - - await updateStream({ streamId: args.stream.id, name: args.stream.name, description: args.stream.description }) - return true - }, - - async streamDelete(parent, args, context, info) { - await validateServerRole(context, 'server:user') - await validateScopes(context.scopes, 'streams:write') - await authorizeResolver(context.userId, args.id, 'stream:owner') - - await deleteStream({ streamId: args.id }) - return true - }, - - async streamGrantPermission(parent, args, context, info) { - await validateServerRole(context, 'server:user') - await validateScopes(context.scopes, 'streams:write') - await authorizeResolver(context.userId, args.streamId, 'stream:owner') - - if (context.userId === args.userId) throw new Error('You cannot set roles for yourself.') - - return await grantPermissionsStream({ streamId: args.streamId, userId: args.userId, role: args.role.toLowerCase() || 'read' }) - }, - - async streamRevokePermission(parent, args, context, info) { - await validateServerRole(context, 'server:user') - await validateScopes(context.scopes, 'streams:write') - await authorizeResolver(context.userId, args.streamId, 'stream:owner') - - return await revokePermissionsStream({...args }) - } + + async streamRevokePermission( parent, args, context, info ) { + await validateServerRole( context, 'server:user' ) + await validateScopes( context.scopes, 'streams:write' ) + await authorizeResolver( context.userId, args.streamId, 'stream:owner' ) + + return await revokePermissionsStream( { ...args } ) } -} \ No newline at end of file + } +} diff --git a/modules/core/tests/rest.spec.js b/modules/core/tests/rest.spec.js index 017c82630b..c94236c83e 100644 --- a/modules/core/tests/rest.spec.js +++ b/modules/core/tests/rest.spec.js @@ -35,7 +35,7 @@ describe( `Upload/Download Routes`, ( ) => { expressApp = app userA.id = await createUser( userA ) - userA.token = `Bearer ${(await createPersonalAccessToken( userA.id, 'test token user A', [ 'streams:read', 'streams:write', 'users:read', 'users:email', 'tokens:write', 'tokens:read', 'profile:read', 'profile:email' ] ))}` + userA.token = `Bearer ${( await createPersonalAccessToken( userA.id, 'test token user A', [ 'streams:read', 'streams:write', 'users:read', 'users:email', 'tokens:write', 'tokens:read', 'profile:read', 'profile:email' ] ) )}` testStream.id = await createStream( { ...testStream, ownerId: userA.id } ) } ) diff --git a/modules/core/tests/users.spec.js b/modules/core/tests/users.spec.js index 79db677793..d41a73909f 100644 --- a/modules/core/tests/users.spec.js +++ b/modules/core/tests/users.spec.js @@ -92,7 +92,7 @@ describe( 'Actors & Tokens', ( ) => { let match = await validatePasssword( { email: actor.email, password: 'super-test-200' } ) expect( match ).to.equal( true ) - let match_wrong = await validatePasssword( { email: actor.email, password: 'super-test-2000' }) + let match_wrong = await validatePasssword( { email: actor.email, password: 'super-test-2000' } ) expect( match_wrong ).to.equal( false ) } ) diff --git a/modules/index.js b/modules/index.js index 2fe7433738..920a3b7c30 100644 --- a/modules/index.js +++ b/modules/index.js @@ -24,7 +24,7 @@ exports.init = async ( app ) => { } ) // Other modules preflight - moduleDirs.forEach(async dir => { + moduleDirs.forEach( async dir => { await require( dir ).init( app ) } ) diff --git a/readme.md b/readme.md index ea7587e989..89d2341e5f 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # Speckle Server -The next iteration of the Speckle Server. dev:frontend +The next iteration of the Speckle Server. [![Speckle-Next](https://circleci.com/gh/Speckle-Next/SpeckleServer.svg?style=svg&circle-token=76eabd350ea243575cbb258b746ed3f471f7ac29)](https://github.com/Speckle-Next/SpeckleServer/) [![codecov](https://codecov.io/gh/Speckle-Next/SpeckleServer/branch/master/graph/badge.svg?token=PHZWVNUVFE)](https://codecov.io/gh/Speckle-Next/SpeckleServer) @@ -13,7 +13,7 @@ To debug, simply run `npm run dev:server`. To test, run `npm run test:server`. T #### Requirements -1. Dubplicate and rename `.env-example` to `.env`. +1. Duplicate and rename `.env-example` to `.env`. 2. You will need to have a postgres instance running on the default settings, with two databases present, named `speckle2_dev` and `speckle2_test`. @@ -24,26 +24,20 @@ To debug, simply run `npm run dev:server`. To test, run `npm run test:server`. T > For which you can use the [redis.app](https://jpadilla.github.io/redisapp/). ### How to commit to this repo -When pushing commits to this repo, please follow the following guidelines: +When pushing commits to this repo, please follow the following guidelines: -1) Install [commitizen](https://www.npmjs.com/package/commitizen#commitizen-for-contributors) globally -3) When ready to commit, type in the commandline `git cz` & follow the prompts. +- Install [commitizen](https://www.npmjs.com/package/commitizen#commitizen-for-contributors) globally (`npm i -g commitizen`). +- When ready to commit, type in the commandline `git cz` & follow the prompts. ## Modules -The server dynamically loads individual 'modules' from each top level folder in `./modules`. It first loads the core modules, and thereafter others ("third party"). +The server dynamically loads individual 'modules' from each top level folder in `./modules`. It first loads the core modules, and thereafter others. -### Loading - -Loading consists of two stages: -- **Preflight**: stage where a module can configure the behaviour of any shared middleware. -- **Initialisation**: final stage, where modules should hoist their routes on the core express application. - -Modules can create new and alter old database tables, if the knex migration files are present in a `migrations` subfolder (e.g., `./modules/your-module/migrations/my-new-table.js`). +Modules can create new and alter old database tables, if the knex migration files are present in a `migrations` subfolder (e.g., `./modules/your-module/migrations/my-new-table.js`). Make sure to add a date in front of your migration file. ### Structure -A module should contain in its root folder an index.js file that exposes an init function: +A module should contain in its root folder an index.js file that exposes an init function: ```js exports.init = ( app ) => { @@ -51,7 +45,7 @@ exports.init = ( app ) => { } ``` -Any database migration files should be stored and named accordingly in a `migrations` folder. Moreover, modules should include test files. These should be located in `tests`. Here's a sample structure: +Any database migration files should be stored and named accordingly in a `migrations` folder. Moreover, modules should include test files. These should be located in `tests`. Here's a sample structure: ``` @@ -70,7 +64,7 @@ Any database migration files should be stored and named accordingly in a `migrat ### GraphQl // TODO -### REST +### REST Depecrated.