Merge commit '49f05eac5f6a8af2ae71d9ef650bab2e5c995b9d' into matteo/gql
# Conflicts: # readme.md
This commit is contained in:
@@ -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" ],
|
||||
|
||||
@@ -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')
|
||||
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
|
||||
},
|
||||
|
||||
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 })
|
||||
}
|
||||
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
|
||||
},
|
||||
|
||||
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 } )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ module.exports = ( app ) => {
|
||||
|
||||
busboy.on( 'finish', ( ) => {
|
||||
debug( 'speckle:upload-endpoint' )( 'Done parsing ' + totalProcessed + ' objs ' + process.memoryUsage( ).heapUsed / 1024 / 1024 + ' mb mem' )
|
||||
res.writeHead( 303, { Connection: 'close', Location: '/' } )
|
||||
res.end( )
|
||||
|
||||
res.status( 201 ).end( )
|
||||
} )
|
||||
|
||||
req.pipe( busboy )
|
||||
} )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 } )
|
||||
} )
|
||||
@@ -89,11 +89,11 @@ describe( `Upload/Download Routes`, ( ) => {
|
||||
.attach( 'batch2', Buffer.from( JSON.stringify( objBatches[ 1 ] ), 'utf8' ) )
|
||||
.attach( 'batch3', Buffer.from( JSON.stringify( objBatches[ 2 ] ), 'utf8' ) )
|
||||
|
||||
expect( res ).to.have.status( 303 )
|
||||
expect( res ).to.have.status( 201 )
|
||||
} )
|
||||
|
||||
it( 'Should properly download an object, with all its children, into a application/json response', ( done ) => {
|
||||
new Promise( resolve => setTimeout( resolve, 1000 ) ) // avoids race condition
|
||||
new Promise( resolve => setTimeout( resolve, 1500 ) ) // avoids race condition
|
||||
.then( ( ) => {
|
||||
let res = request( expressApp )
|
||||
.get( `/objects/${testStream.id}/${parentId}` )
|
||||
@@ -177,4 +177,4 @@ function createManyObjects( amount, noise ) {
|
||||
|
||||
function getId( obj ) {
|
||||
obj.id = obj.id || crypto.createHash( 'md5' ).update( JSON.stringify( obj ) ).digest( 'hex' )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 )
|
||||
|
||||
} )
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ exports.init = async ( app ) => {
|
||||
} )
|
||||
|
||||
// Other modules preflight
|
||||
moduleDirs.forEach(async dir => {
|
||||
moduleDirs.forEach( async dir => {
|
||||
await require( dir ).init( app )
|
||||
} )
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Speckle Server
|
||||
|
||||
The next iteration of the Speckle Server. dev:frontend
|
||||
The next iteration of the Speckle Server.
|
||||
|
||||
[](https://github.com/Speckle-Next/SpeckleServer/) [](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,34 +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
|
||||
2. When ready to commit, type in the commandline `git cz` & follow the prompts.
|
||||
3. Install eslint globally `npm i -g eslint`
|
||||
1. if using VS code install the `eslint` extension
|
||||
2. we also recommend setting it to run on save by adding the following VS Code setting
|
||||
```
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
```
|
||||
- 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 ) => {
|
||||
@@ -59,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:
|
||||
|
||||
```
|
||||
|
||||
@@ -78,7 +64,7 @@ Any database migration files should be stored and named accordingly in a `migrat
|
||||
### GraphQl
|
||||
|
||||
// TODO
|
||||
### REST
|
||||
### REST
|
||||
|
||||
Depecrated.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user