feat(references): added api routes and tests for references

This commit is contained in:
Dimitrie Stefanescu
2020-04-11 17:32:38 +01:00
parent 1527ad2e41
commit a513a7ac8d
9 changed files with 298 additions and 79 deletions
+6 -7
View File
@@ -15,9 +15,14 @@ exports.init = ( ) => {
app.use( bodyParser.json( ) )
app.use( bodyParser.urlencoded( { extended: false } ) )
app.get( '/', ( req, res ) => {
res.send( { fantastic: 'speckle' } )
} )
require( './modules' )( app )
// Error responses
app.use( ( err, req, res, next ) => {
console.log( "ERRRRRR")
if ( process.env.NODE_ENV === 'test' ) {
debug( err )
}
@@ -28,11 +33,5 @@ exports.init = ( ) => {
} )
} )
app.get( '/', ( req, res ) => {
res.send( { fantastic: 'speckle' } )
} )
require( './modules' )( app )
return app
}
+5 -5
View File
@@ -28,8 +28,8 @@ exports.up = async knex => {
` )
await knex.schema.createTable( 'stream_acl', table => {
table.text( 'user_id' ).references( 'id' ).inTable( 'users' ).notNullable( )
table.text( 'resource_id' ).references( 'id' ).inTable( 'streams' ).notNullable( )
table.text( 'user_id' ).references( 'id' ).inTable( 'users' ).notNullable( ).onDelete( 'cascade' )
table.text( 'resource_id' ).references( 'id' ).inTable( 'streams' ).notNullable( ).onDelete( 'cascade' )
table.primary( [ 'user_id', 'resource_id' ] )
table.unique( [ 'user_id', 'resource_id' ] )
table.specificType( 'role', 'speckle_acl_role_type' ).defaultTo( 'write' )
@@ -72,7 +72,7 @@ exports.up = async knex => {
// Reference table. A reference can be a branch or a tag.
await knex.schema.createTable( 'references', table => {
table.uuid( 'id' ).defaultTo( knex.raw( 'gen_random_uuid()' ) ).unique( ).primary( )
table.text( 'stream_id' ).references( 'id' ).inTable( 'streams' ).notNullable( )
table.text( 'stream_id' ).references( 'id' ).inTable( 'streams' ).notNullable( ).onDelete( 'cascade' )
table.text( 'author' ).references( 'id' ).inTable( 'users' )
table.text( 'name' )
table.specificType( 'type', 'speckle_reference_type' ).defaultTo( 'branch' )
@@ -87,7 +87,7 @@ exports.up = async knex => {
// Junction Table Branches >- -< Commits
// Note: Branches >- -< Commits is a many-to-many relationship (one commit can belong to multiple branches, one branch can have multiple commits)
await knex.schema.createTable( 'branch_commits', table => {
table.uuid( 'branch_id' ).references( 'id' ).inTable( 'references' ).notNullable( )
table.uuid( 'branch_id' ).references( 'id' ).inTable( 'references' ).notNullable( ).onDelete('cascade')
table.text( 'commit_id' ).references( 'hash' ).inTable( 'objects' ).notNullable( )
table.primary( [ 'branch_id', 'commit_id' ] )
} )
@@ -95,7 +95,7 @@ exports.up = async knex => {
// Flat table to store all commits to this stream, regardless of branch.
// Optional, might be removed as you can get all the commits from each branch...
await knex.schema.createTable( 'stream_commits', table => {
table.text( 'stream_id' ).references( 'id' ).inTable( 'streams' ).notNullable( )
table.text( 'stream_id' ).references( 'id' ).inTable( 'streams' ).notNullable( ).onDelete('cascade')
table.text( 'commit_id' ).references( 'hash' ).inTable( 'objects' ).notNullable( )
table.primary( [ 'stream_id', 'commit_id' ] )
} )
+2 -2
View File
@@ -16,7 +16,7 @@ module.exports = {
async createCommit( req, res, next ) {
try {
let id = await createCommit( req.params.resourceId, req.user.id, req.body )
res.status( 201 ).send( { success: true, id: id } )
res.status( 201 ).send( id )
next( )
} catch ( err ) {
next( err )
@@ -37,7 +37,7 @@ module.exports = {
async createObjects( req, res, next ) {
try {
let hashes = await createObjects( req.params.resourceId, req.user.id, req.body )
let hashes = await createObjects( req.body )
res.status( 201 ).send( hashes )
next( )
} catch ( err ) {
+5 -5
View File
@@ -22,7 +22,9 @@ module.exports = {
async createCommit( streamId, userId, object ) {
object.speckle_type = 'commit'
let hash = await module.exports.createObject( streamId, userId, object )
object.author = userId
let hash = await module.exports.createObject( object )
let query = StreamCommits( ).insert( { stream_id: streamId, commit_id: hash } ).toString( ) + ' on conflict do nothing'
await knex.raw( query )
@@ -38,14 +40,13 @@ module.exports = {
/*
Objects Proper
*/
async createObject( streamId, userId, object ) {
async createObject( object ) {
// Prep tree refs
let objTreeRefs = object.hasOwnProperty( '__tree' ) && object.__tree ? object.__tree.map( entry => {
return { parent: entry.split( '.' )[ 0 ], path: entry }
} ) : [ ]
let insertionObject = prepInsertionObject( object )
insertionObject.author = userId
let q1 = Objects( ).insert( insertionObject ).toString( ) + ' on conflict do nothing'
await knex.raw( q1 )
@@ -58,7 +59,7 @@ module.exports = {
return insertionObject.hash
},
createObjects: async ( streamId, userId, objects ) => {
createObjects: async ( objects ) => {
let batches = [ ]
let maxBatchSize = process.env.MAX_BATCH_SIZE || 1000
objects = [ ...objects ]
@@ -86,7 +87,6 @@ module.exports = {
}
let insertionObject = prepInsertionObject( obj )
insertionObject.author = userId
objsToInsert.push( insertionObject )
hashes.push( insertionObject.hash )
+94 -12
View File
@@ -1,24 +1,106 @@
'use strict'
const {
createTag,
updateTag,
getTagById,
deleteTagById,
getTagsByStreamId,
createBranch,
updateBranch,
getBranchById,
deleteBranchById,
getBranchesByStreamId,
getStreamReferences
} = require( './services' )
module.exports = {
getReferences: ( req, res, next ) => {
res.send( [ 1, 3, 4 ] )
async getReferences( req, res, next ) {
try {
const references = await getStreamReferences( req.params.resourceId )
res.send( references )
next( )
} catch ( err ) {
next( err )
}
},
getReference: ( req, res, next ) => {
res.send( { todo: true } )
async getTag( req, res, next ) {
try {
const reference = await getTagById( req.params.referenceId )
res.send( reference )
next( )
} catch ( err ) {
next( err )
}
},
createReference: ( req, res, next ) => {
res.send( { todo: true } )
async createTag( req, res, next ) {
try {
let id = await createTag( req.body, req.params.resourceId, req.user.id )
res.status( 201 ).send( { id: id } )
next( )
} catch ( err ) {
next( err )
}
},
updateReference: ( req, res, next ) => {
res.send( { todo: true } )
async updateTag( req, res, next ) {
try {
req.body.id = req.params.referenceId
await updateTag( req.body )
res.status( 200 ).send( { success: true } )
next( )
} catch ( err ) {
next( err )
}
},
deleteReference: ( req, res, next ) => {
res.send( { todo: true } )
async deleteTag( req, res, next ) {
try {
await deleteTagById( req.params.referenceId )
res.status( 200 ).send( { success: true } )
next( )
} catch ( err ) {
next( err )
}
},
async getBranch( req, res, next ) {
try {
const reference = await getBranchById( req.params.referenceId )
res.send( reference )
next( )
} catch ( err ) {
next( err )
}
},
async createBranch( req, res, next ) {
try {
const id = await createBranch( req.body, req.params.resourceId, req.user.id )
res.send( { id: id } )
next( )
} catch ( err ) {
next( err )
}
},
async updateBranch( req, res, next ) {
try {
req.body.id = req.params.referenceId
await updateBranch( req.body )
res.status( 200 ).send( { success: true } )
} catch ( err ) {
next( err )
}
},
async deleteBranch( req, res, next ) {
try {
await deleteBranchById( req.params.referenceId )
res.send( { success: true } )
} catch ( err ) {
next( err )
}
}
}
}
+68 -12
View File
@@ -1,14 +1,25 @@
'use strict'
const root = require( 'app-root-path' )
const { getReferences, getReference, createReference, updateReference, deleteReference } = require( './controllers' )
const { authenticate, authorize, announce } = require( `${root}/modules/shared` )
const {
getReferences,
getTag,
createTag,
updateTag,
deleteTag,
getBranch,
createBranch,
updateBranch,
deleteBranch
} = require( './controllers' )
// References (branches & tags)
const references = require( 'express' ).Router( { mergeParams: true } )
module.exports = references
// Get all branches and tags
// Get all branches and tags for a stream
references.get(
'/streams/:resourceId/references',
authenticate( 'streams:read', false ),
@@ -16,35 +27,80 @@ references.get(
getReferences
)
// Get specific tag or branch
/*
Tags
*/
// Get specific tag
references.get(
'/streams/:streamId/references/:referenceId',
'/streams/:resourceId/tags/:referenceId',
authenticate( 'streams:read', false ),
authorize( 'streams_acl', 'streams', 'read' ),
getReference
getTag
)
// Create a branch or a tag
// Create a tag
references.post(
'/streams/:streamId/references',
'/streams/:resourceId/tags',
authenticate( 'streams:write' ),
authorize( 'stream_acl', 'streams', 'write' ),
createReference,
createTag,
announce( 'reference-created', 'stream' )
)
// Edit a branch or a tag
// Edit a tag
references.put(
'/streams/:streamId/references/:referenceId',
'/streams/:resourceId/tags/:referenceId',
authenticate( 'streams:write' ),
authorize( 'stream_acl', 'streams', 'write' ),
updateTag,
announce( 'reference-updated', 'stream' )
)
// Delete a tag
references.delete(
'/streams/:streamId/references/:referenceId',
'/streams/:resourceId/tags/:referenceId',
authenticate( 'streams:write' ),
authorize( 'stream_acl', 'streams', 'write' ),
deleteReference,
deleteTag,
announce( 'reference-deleted', 'stream' )
)
/*
Branches
*/
// Get specific branch
references.get(
'/streams/:resourceId/branches/:referenceId',
authenticate( 'streams:read', false ),
authorize( 'streams_acl', 'streams', 'read' ),
getBranch
)
// Create a branch
references.post(
'/streams/:resourceId/branches',
authenticate( 'streams:write' ),
authorize( 'stream_acl', 'streams', 'write' ),
createBranch,
announce( 'reference-created', 'stream' )
)
// Edit a branch
references.put(
'/streams/:resourceId/branches/:referenceId',
authenticate( 'streams:write' ),
authorize( 'stream_acl', 'streams', 'write' ),
updateBranch,
announce( 'reference-updated', 'stream' )
)
// Delete a branch
references.delete(
'/streams/:resourceId/branches/:referenceId',
authenticate( 'streams:write' ),
authorize( 'stream_acl', 'streams', 'write' ),
deleteBranch,
announce( 'reference-deleted', 'stream' )
)
+5 -2
View File
@@ -34,7 +34,7 @@ module.exports = {
},
deleteTagById: async ( tagId ) => {
return Refs( ).where( { id: tag.id, type: 'tag' } ).del( )
return Refs( ).where( { id: tagId, type: 'tag' } ).del( )
},
getTagsByStreamId: async ( streamId ) => {
@@ -69,7 +69,7 @@ module.exports = {
let branchCommits = commits.map( commitId => { return { branch_id: branch.id, commit_id: commitId } } )
await knex.raw( BranchCommits( ).insert( branchCommits ) + ' on conflict do nothing' )
}
await Refs( ).where( { id: branch.id } ).update( branch )
},
@@ -89,6 +89,9 @@ module.exports = {
return Refs( ).where( { stream_id: streamId, type: 'branch' } ).select( '*' )
},
async deleteBranchById( branchId ) {
await Refs( ).where( { id: branchId, type: 'branch' } ).del( )
},
/*
Generic
*/
+4 -4
View File
@@ -78,8 +78,8 @@ describe( 'Objects', ( ) => {
} )
it( 'Should create objects', async ( ) => {
sampleObject.hash = await createObject( stream.id, userOne.id, sampleObject )
sampleCommit.hash = await createObject( stream.id, userOne.id, sampleCommit )
sampleObject.hash = await createObject( sampleObject )
sampleCommit.hash = await createObject( sampleCommit )
} )
let objCount_1 = 10
@@ -100,7 +100,7 @@ describe( 'Objects', ( ) => {
} )
}
let hashes = await createObjects( stream.id, userOne.id, objs )
let hashes = await createObjects( objs )
expect( hashes ).to.have.lengthOf( objCount_1 )
@@ -124,7 +124,7 @@ describe( 'Objects', ( ) => {
} )
}
let hashes = await createObjects( stream.id, userOne.id, objs2 )
let hashes = await createObjects( objs2 )
hashes.forEach( ( h, i ) => objs2[ i ].hash = h )
+109 -30
View File
@@ -12,7 +12,7 @@ chai.use( chaiHttp )
const { createUser, createToken, revokeToken, revokeTokenById, validateToken, getUserTokens } = require( '../users/services' )
const { createStream, getStream, updateStream, deleteStream, getStreamsUser, grantPermissionsStream, revokePermissionsStream } = require( '../streams/services' )
const { createObject, createObjects, getObject, getObjects } = require( '../objects/services' )
const { createObject, createCommit, createObjects, getObject, getObjects } = require( '../objects/services' )
const {
createTag,
updateTag,
@@ -27,43 +27,42 @@ const {
} = require( '../references/services' )
describe( 'Tags & Branches', ( ) => {
let user = {
username: 'dim4242',
name: 'Dimitrie Stefanescu',
email: 'didimitrie4342@gmail.com',
password: 'sn3aky-1337-b1m'
}
let stream = {
name: 'Test Stream References',
description: 'Whatever goes in here usually...'
}
let commit1 = { description: 'First Commit' }
let commit2 = { description: 'Second Commit' }
let branch = {
name: '🧨 super branch 🧨',
description: 'a test branch'
}
let tag = {
name: 'v.1.20.3',
description: 'release version shite'
}
describe( 'Services/Queries', ( ) => {
let user = {
username: 'dim4242',
name: 'Dimitrie Stefanescu',
email: 'didimitrie4342@gmail.com',
password: 'sn3aky-1337-b1m'
}
let stream = {
name: 'Test Stream References',
description: 'Whatever goes in here usually...'
}
let commit1 = { description: 'First Commit' }
let commit2 = { description: 'Second Commit' }
let branch = {
name: '🧨 super branch 🧨',
description: 'a test branch'
}
let tag = {
name: 'v.1.20.3',
description: 'release version shite'
}
before( async ( ) => {
await knex.migrate.latest( )
user.id = await createUser( user )
stream.id = await createStream( stream, user.id )
commit1.hash = await createObject( stream.id, user.id, commit1 )
commit1.hash = await createCommit( stream.id, user.id, commit1 )
commit2.parents = [ commit1.hash ]
commit2.hash = await createObject( stream.id, user.id, commit2 )
commit2.hash = await createCommit( stream.id, user.id, commit2 )
tag.commit_id = commit2.hash
} )
@@ -106,7 +105,7 @@ describe( 'Tags & Branches', ( ) => {
expect( myBranchAfterFirstUpdate.commits ).to.have.lengthOf( 1 )
let newCommit = { test: 'test', best: true }
newCommit.hash = await createObject( stream.id, user.id, newCommit )
newCommit.hash = await createCommit( stream.id, user.id, newCommit )
branch.commits = [ commit2.hash, newCommit.hash, commit1.hash ]
branch.name = 'A Different Name'
@@ -170,4 +169,84 @@ describe( 'Tags & Branches', ( ) => {
expect( branches ).to.have.lengthOf( 3 )
} )
} )
describe( 'Integration (API)', ( ) => {
let token
before( async ( ) => {
await knex.migrate.latest( )
user.id = await createUser( user )
token = await createToken( user.id, 'Generic Token', [ 'streams:read', 'streams:write' ] )
stream.id = await createStream( stream, user.id )
commit1.hash = await createCommit( stream.id, user.id, commit1 )
commit2.parents = [ commit1.hash ]
commit2.hash = await createCommit( stream.id, user.id, commit2 )
tag.commit_id = commit2.hash
} )
after( async ( ) => {
await knex.migrate.rollback( )
} )
it( 'Should create a tag', async ( ) => {
const response = await chai.request( app ).post( `/streams/${stream.id}/tags` ).send( tag ).set( 'Authorization', `Bearer ${token}` )
expect( response ).to.have.status( 201 )
expect( response.body ).to.have.property( 'id' )
tag.id = response.body.id
} )
it( 'Should update a tag', async ( ) => {
const response = await chai.request( app ).put( `/streams/${stream.id}/tags/${tag.id}` ).send( { name: 'semver love' } ).set( 'Authorization', `Bearer ${token}` )
expect( response ).to.have.status( 200 )
} )
it( 'Should get a tag', async ( ) => {
const tagResponse = await chai.request( app ).get( `/streams/${stream.id}/tags/${tag.id}` ).set( 'Authorization', `Bearer ${token}` )
expect( tagResponse ).to.have.status( 200 )
expect( tagResponse.body.name ).to.equal( 'semver love' )
} )
it( 'Should delete a tag', async ( ) => {
const deleteResponse = await chai.request( app ).delete( `/streams/${stream.id}/tags/${tag.id}` ).set( 'Authorization', `Bearer ${token}` )
expect( deleteResponse ).to.have.status( 200 )
} )
it( 'Should create a branch', async ( ) => {
branch.commits = [ commit1.hash, commit1.hash ]
const response = await chai.request( app ).post( `/streams/${stream.id}/branches` ).send( branch ).set( 'Authorization', `Bearer ${token}` )
expect( response ).to.have.status( 200 )
expect( response.body ).to.have.property( 'id' )
branch.id = response.body.id
} )
it( 'Should update a branch', async ( ) => {
const response = await chai.request( app ).put( `/streams/${stream.id}/branches/${branch.id}` ).send( { name: 'semver love' } ).set( 'Authorization', `Bearer ${token}` )
expect( response ).to.have.status( 200 )
} )
it( 'Should get a branch', async ( ) => {
const branchResponse = await chai.request( app ).get( `/streams/${stream.id}/branches/${branch.id}` ).set( 'Authorization', `Bearer ${token}` )
expect( branchResponse ).to.have.status( 200 )
expect( branchResponse.body ).to.have.property( 'name' )
expect( branchResponse.body.name ).to.equal( 'semver love' )
} )
it( 'Should delete create a branch', async ( ) => {
const deleteResponse = await chai.request( app ).delete( `/streams/${stream.id}/branches/${branch.id}` ).set( 'Authorization', `Bearer ${token}` )
expect( deleteResponse ).to.have.status( 200 )
} )
} )
} )