Commit e38d248f authored by priit's avatar priit
Browse files

Töövoogude haldus

parent 5be2ae2c
......@@ -5,6 +5,7 @@ var express = require('express');
var router = express.Router();
var logger = require('log4js').getLogger('project_controller');
var workflowService = require(__base + 'src/service/workflowService');
var workflowDefinitionService = require(__base + 'src/service/workflowDefinitionService');
var projectService = require('../../../src/service/projectService');
var authMiddleware = require(__base + 'middlewares/auth');
......@@ -75,4 +76,14 @@ router.get('/:projectId/workflows', authMiddleware('regular'), function(req, res
});
});
/**
* Projekti vaate töövoo kirjelduste nimekiri
*/
router.get('/:projectId/definitions', authMiddleware('regular'), function(req, res) {
workflowDefinitionService.getProjectWorkflowDefinitionsList(req, req.params.projectId, function(error, definitions) {
return res.sendApiResponse(error, definitions);
});
});
module.exports = router;
......@@ -12,6 +12,15 @@ router.get('/', authMiddleware('regular'), function(req, res) {
});
});
/**
* Töövoogude haldus
*/
router.get('/management-list', authMiddleware('admin'), function(req, res) {
workflowDefinitionService.getWorkflowDefinitionsManagementList(req, req.query, function(err, data) {
return res.sendApiResponse( err, data);
});
});
/**
* Avaliku urli vaade
*/
......@@ -21,4 +30,13 @@ router.get('/:definitionId', authMiddleware('regular'), function(req, res) {
});
});
/**
* Definitsiooni sätted
*/
router.put('/:definitionId', authMiddleware('regular'), function(req, res) {
workflowDefinitionService.updateWorkflowDefinitionSettings(req, req.body, function(err, overview) {
return res.sendApiResponse( err, overview);
});
});
module.exports = router;
\ No newline at end of file
No preview for this file type
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
queryInterface.addColumn(
'workflow_definition',
'published_at',
{
type: Sequelize.DATE,
allowNull: true
}
)
},
down: function (queryInterface, Sequelize) {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.dropTable('users');
*/
}
};
'use strict';
var Promise = require('bluebird');
var fs = require('fs');
module.exports = {
up: function (queryInterface, Sequelize) {
return Promise
.resolve()
.then(function () {
return queryInterface.sequelize.query("" +
" INSERT INTO notification_type ( application_context, code, url_template, message_template, is_send_email, mail_subject_template, notify_period_days ) " +
" SELECT " +
" 'workflow-definition', " +
" 'workflow-definition-unpublished', " +
" '{appUrl}/#/project/{projectId}/definition/{workflowDefinitionId}/view', " +
" 'Töövoo kirjeldus \"{workflowDefinitionName}\" muudeti mitteavalikuks', " +
" TRUE, " +
" 'Töövoo kirjeldus \"{workflowDefinitionName}\" muudeti mitteavalikuks', " +
" 0 " +
" WHERE NOT EXISTS (SELECT 1 FROM notification_type WHERE notification_type.code = 'workflow-definition-unpublished'); ");
});
},
down: function (queryInterface, Sequelize) {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.dropTable('users');
*/
}
};
......@@ -5,14 +5,16 @@ module.exports = function(sequelize, DataTypes) {
var applicationContexts = {
PROJECT: 'project',
USER: 'user',
WORKFLOW: 'workflow'
WORKFLOW: 'workflow',
WORKFLOW_DEFINITION: 'workflow-definition'
};
var codes = {
WORKFLOW_FINISHED : 'workflow-finished',
WORKFLOW_STILL_RUNNING : 'workflow-still-running',
WORKFLOW_ERROR : 'workflow-error',
PROJECT_USER_ADDED : 'project-user-added'
PROJECT_USER_ADDED : 'project-user-added',
WORKFLOW_DEFINITION_UNPUBLISHED : 'workflow-definition-unpublished'
};
var NotificationType = sequelize.define("NotificationType", {
......
......@@ -84,6 +84,10 @@ module.exports = function(sequelize, DataTypes) {
updatedAt: {
type: DataTypes.DATE,
field: 'updated_at'
},
publishedAt: {
type: DataTypes.DATE,
field: 'published_at'
}
}, {
tableName: 'workflow_definition',
......@@ -130,7 +134,7 @@ module.exports = function(sequelize, DataTypes) {
}
);
WorkflowDefinition.hasMany(models.WorkflowDefinitionUser, {
as: 'workflowDefinitionUserRelations',
as: 'sharedUsers',
foreignKey: 'workflowDefinitionId'
}
);
......@@ -150,7 +154,24 @@ module.exports = function(sequelize, DataTypes) {
return cb(err);
});
}
},
hooks: {
beforeValidate: function(item, options, cb) {
if(item.changed('accessStatus') && item.accessStatus == accessStatuses.PUBLIC){
if(options.fields.indexOf('publishedAt') === -1){
options.fields.push('publishedAt');
}
if(options.skip.indexOf('publishedAt') !== -1){
var index = options.skip.indexOf('publishedAt');
options.skip = options.skip.splice(index, 1);
}
item.publishedAt = new Date();
}
cb();
}
}
});
WorkflowDefinition.editStatuses = editStatuses;
......
......@@ -19,7 +19,8 @@ function WorkflowDefinitionDaoService() {
" wfd.description as description," +
" wfd.purpose as purpose," +
" wfd.edit_status as edit_status," +
" wfd.access_status as access_status " +
" wfd.access_status as access_status, " +
" wfd.published_at as published_at " +
" FROM workflow_definition as wfd " +
" WHERE " +
" wfd.access_status = '" + WorkflowDefinition.accessStatuses.PUBLIC + "'";
......@@ -31,7 +32,8 @@ function WorkflowDefinitionDaoService() {
" wfd.description as description," +
" wfd.purpose as purpose," +
" wfd.edit_status as edit_status," +
" '" + WorkflowDefinition.accessStatuses.PRIVATE + "' as access_status " +
" '" + WorkflowDefinition.accessStatuses.PRIVATE + "' as access_status, " +
" null as published_at " +
" FROM workflow_definition as wfd " +
" WHERE " +
" wfd.user_id = :userId ";
......@@ -43,7 +45,8 @@ function WorkflowDefinitionDaoService() {
" wfd.description as description," +
" wfd.purpose as purpose," +
" wfd.edit_status as edit_status," +
" wfd.access_status as access_status " +
" wfd.access_status as access_status, " +
" null as published_at " +
" FROM workflow_definition as wfd " +
" JOIN workflow_definition_user AS wfdu ON ( wfdu.user_id = :userId AND wfdu.workflow_definition_id = wfd.id )" +
" WHERE " +
......@@ -57,6 +60,7 @@ function WorkflowDefinitionDaoService() {
" definition.purpose, " +
" definition.edit_status, " +
" definition.access_status, " +
" definition.published_at as published_at, " +
" u.name AS owner " +
" FROM ((" + publicQuery + ") UNION ALL ("+ personalQuery + ") UNION ALL ("+ sharedQuery + ") ) as definition " +
" JOIN workflow_definition_user AS wdu ON (wdu.workflow_definition_id = definition.id AND wdu.role = '"+ WorkflowDefinitionUser.roles.OWNER +"')" +
......@@ -77,6 +81,101 @@ function WorkflowDefinitionDaoService() {
return callback(err.message);
});
};
this.getProjectWorkflowDefinitionsList = function (projectId, cb) {
var query = " SELECT " +
" wfd.id as id," +
" wfd.name as name," +
" wfd.description as description," +
" wfd.purpose as purpose," +
" wfd.edit_status as edit_status," +
" wfd.access_status as access_status, " +
" wfd.published_at as published_at, " +
" wfd.created_at as created_at, " +
" wfd.updated_at as updated_at, " +
" COUNT( wf.id ) as usage_count" +
" FROM workflow_definition as wfd " +
" LEFT JOIN workflow AS wf ON ( wf.workflow_definition_id = wfd.id ) " +
" WHERE " +
" wfd.project_id = :projectId " +
" GROUP BY wfd.id " +
" ORDER BY wfd.name;";
sequelize.query( query, {
replacements: { projectId: projectId },
type: sequelize.QueryTypes.SELECT
}).then(function (workflowDefinitions) {
return cb(null, workflowDefinitions);
}).catch(function (err) {
logger.error(err);
return cb(err.message);
});
};
this.getWorkflowDefinitionsManagementList = function ( params, cb) {
var where = '';
if(params.name && params.accessStatus){
where = " WHERE wfd.name ILIKE '%" + params.name + "%' AND wfd.access_status = '" + params.accessStatus + "' ";
} else if( params.name ){
where = " WHERE wfd.name ILIKE '%" + params.name + "%' ";
} else if( params.accessStatus ){
where = " WHERE wfd.access_status = '" + params.accessStatus + "' ";
}
var limits = '';
if( params.page && params.perPage ){
limits = " LIMIT " + params.perPage + " OFFSET " + ((params.page - 1) * params.perPage);
}
var countQuery = " SELECT " +
" COUNT(wfd.id) as total_count" +
" FROM workflow_definition as wfd " + where + " ;";
var query = " SELECT " +
" wfd.id as id," +
" wfd.name as name," +
" wfd.description as description," +
" wfd.purpose as purpose," +
" wfd.edit_status as edit_status," +
" wfd.access_status as access_status, " +
" wfd.published_at as published_at, " +
" wfd.created_at as created_at, " +
" wfd.updated_at as updated_at, " +
" COUNT( wf.id ) as usage_count" +
" FROM workflow_definition as wfd " +
" LEFT JOIN workflow AS wf ON ( wf.workflow_definition_id = wfd.id ) " +
" " + where + " " +
" GROUP BY wfd.id " +
" ORDER BY wfd.name " + limits + ";";
var result = {
count: 0,
rows: []
};
sequelize.query(countQuery, {
replacements: {/*projectId: projectId*/},
type: sequelize.QueryTypes.SELECT
}).then(function (countResult) {
result.count = countResult.pop().total_count;
sequelize.query(query, {
replacements: {/*projectId: projectId*/},
type: sequelize.QueryTypes.SELECT
}).then(function (workflowDefinitions) {
result.rows = workflowDefinitions;
return cb(null, result);
}).catch(function (err) {
logger.error(err);
return cb(err.message);
});
}).catch(function (err) {
logger.error(err);
return cb(err.message);
});
}
}
module.exports = new WorkflowDefinitionDaoService();
\ No newline at end of file
......@@ -3,6 +3,7 @@ var logger = require('log4js').getLogger('meta_service');
var Project = require(__base + 'src/service/dao/sql').Project;
var Workflow = require(__base + 'src/service/dao/sql').Workflow;
var WorkflowDefinition = require(__base + 'src/service/dao/sql').WorkflowDefinition;
var User = require(__base + 'src/service/dao/sql').User;
var ResourceType = require(__base + 'src/service/dao/sql').ResourceType;
var Service = require(__base + 'src/service/dao/sql').Service;
......@@ -34,6 +35,15 @@ function MetaService() {
callback();
});
},
function (callback) {
if(!query.definitionId){
return callback();
}
WorkflowDefinition.find({where:{id: query.definitionId},attributes:['id','name']}).then(function (item) {
response.definitionId = item ? item.name : '';
callback();
});
},
function (callback) {
if(!query.userId){
return callback();
......
var notificationDaoService = require('./dao/notificationDaoService');
var Notification = require(__base + 'src/service/dao/sql').Notification;
var NotificationType = require(__base + 'src/service/dao/sql').NotificationType;
var WorkflowDefinition = require(__base + 'src/service/dao/sql').WorkflowDefinition;
var User = require(__base + 'src/service/dao/sql').User;
var Project = require(__base + 'src/service/dao/sql').Project;
var Workflow = require(__base + 'src/service/dao/sql').Workflow;
......@@ -278,7 +278,35 @@ function NotificationService() {
}).catch(function(err) {
callback(err.message);
});
},
function isWorkflowDefinitionNotification( callback ) {
if(notificationType.applicationContext != NotificationType.applicationContexts.WORKFLOW_DEFINITION){
return callback();
}
self._replaceInObjectValues(templates, '{workflowDefinitionId}', notification.modelId);
WorkflowDefinition.findById( notification.modelId).then(function (workflowDefinition) {
if(!workflowDefinition){
return callback('Workflow definition not found');
}
self._replaceInObjectValues(templates, '{workflowDefinitionName}', workflowDefinition.name );
self._replaceInObjectValues(templates, '{projectId}', workflowDefinition.projectId );
workflowDefinition.getProject().then(function (project) {
if(!project){
return callback('Project not found');
}
self._replaceInObjectValues(templates, '{projectName}', project.name );
callback();
}).catch(function(err) {
callback(err.message);
});
}).catch(function(err) {
callback(err.message);
});
}
],
function (err) {
if(err){
......
......@@ -8,17 +8,14 @@ var Project = require(__base + 'src/service/dao/sql').Project;
var User = require(__base + 'src/service/dao/sql').User;
var ProjectUser = require(__base + 'src/service/dao/sql').ProjectUser;
var userService = require('./userService');
var userDaoService = require('./dao/userDaoService');
var async = require('async');
var notificationService = require(__base + 'src/service/notificationService');
var resourceService = require(__base + 'src/service/resourceService');
var NotificationType = require(__base + 'src/service/dao/sql').NotificationType;
var ArrayUtil = require('../util/arrayUtils');
var ObjectUtil = require('../util/objectUtils');
var ResourceAssociation = require(__base + 'src/service/dao/sql').ResourceAssociation;
var resourceDaoService = require(__base + 'src/service/dao/resourceDaoService');
function ProjectService(){
......
......@@ -11,10 +11,14 @@ var User = require(__base + 'src/service/dao/sql').User;
var ServiceModel = require(__base + 'src/service/dao/sql').Service;
var WorkflowDefinitionServiceModel = require(__base + 'src/service/dao/sql').WorkflowDefinitionService;
var WorkflowDefinitionUser = require(__base + 'src/service/dao/sql').WorkflowDefinitionUser;
var NotificationType = require(__base + 'src/service/dao/sql').NotificationType;
var async = require('async');
var ArrayUtil = require(__base + 'src/util/arrayUtils');
var ObjectUtil = require(__base + 'src/util/objectUtils');
var userService = require('./userService');
var notificationService = require('./notificationService');
var projectService = require('./projectService');
var config = require(__base + 'config');
......@@ -46,13 +50,13 @@ function WorkflowDefinitionService() {
return cb(null, true);
}
item.getWorkflowDefinitionUserRelations({where:{userId: req.redisSession.data.userId}}).then(function (relations) {
item.getSharedUsers({where:{userId: req.redisSession.data.userId}}).then(function (relations) {
if(relations.length == 0){
return cb(null, false);
}
if(item.accessStatus == WorkflowDefinition.accessStatuses.SHARED){
return callback(null, true);
return cb(null, true);
}
return cb(null, false);
}).catch(function (err) {
......@@ -111,7 +115,7 @@ function WorkflowDefinitionService() {
function createWorkflow(user, callback) {
workflowDefinitionData.userId = user.id;
var workflow = Workflow.build(workflowDefinitionData, {fields: ['name', 'description', 'purpose', 'projectId', 'userId', 'accessStatus']});
var workflow = Workflow.build(workflowDefinitionData, {fields: ['name', 'description', 'purpose', 'projectId', 'userId']});
workflow.validate().then(function (err) {
if (err) {
return callback(err.message);
......@@ -127,6 +131,9 @@ function WorkflowDefinitionService() {
function createDefinition(workflow, callback) {
workflowDefinitionData.workflowId = workflow.id;
var definition = WorkflowDefinition.build(workflowDefinitionData, {fields: ['name', 'description', 'purpose', 'projectId', 'userId', 'workflowId', 'accessStatus']});
/*if(definition.accessStatus == WorkflowDefinition.accessStatuses.PUBLIC){
definition.publishedAt = new Date();
}*/
definition.validate().then(function (err) {
if (err) {
......@@ -232,7 +239,6 @@ function WorkflowDefinitionService() {
var userId = req.redisSession.data.userId;
async.waterfall(
[
function (callback) {
......@@ -256,23 +262,33 @@ function WorkflowDefinitionService() {
},
attributes:[
'id',
'projectId',
'name',
'description',
'purpose',
'accessStatus',
'createdAt'
'editStatus',
'createdAt',
'updatedAt',
'publishedAt'
],
include: [
{
model: User,
as: 'user',
attributes:['id','name', 'displaypicture'],
required: false
model: WorkflowDefinitionUser,
as: 'sharedUsers',
attributes:[ 'role' ],
required: false,
include: [{
model: User,
as: 'user',
attributes:['id','name', 'displaypicture'],
required: false
}]
},
{
model: WorkflowDefinitionServiceModel,
as: 'definitionServices',
attributes:['id','orderNum'],
attributes:['id','orderNum','serviceParamsValues'],
required: false,
include: [
{
......@@ -299,13 +315,19 @@ function WorkflowDefinitionService() {
});
}
callback(null, item);
},
function (item, callback) {
item = item.toJSON();
if(item.accessStatus == WorkflowDefinition.accessStatuses.PUBLIC){
item.publicUrl = self.getDefinitionPublicUrl(item);
}
callback(null, item);
}
],
function (err, data) {
if(err){
logger.error(err);
}
cb(err, data);
}
);
......@@ -366,6 +388,76 @@ function WorkflowDefinitionService() {
});
};
this.updateWorkflowDefinitionSettings = function (req, data, cb) {
logger.debug('Update settings');
async.waterfall(
[
function (callback) {
self.canViewDefinitionById(req, data, function (err, canView) {
if(err || !canView){
return callback('Kasutajal puudub töövoo muutmiseks ligipääs');
}
callback()
});
},
function (callback) {
WorkflowDefinition.findById( data.id).then(function (definition) {
logger.debug('Definition found');
callback(null, definition);
}).catch(function (err) {
callback(err.message);
});
},
function (definition, callback) {
if( definition.accessStatus == WorkflowDefinition.accessStatuses.PUBLIC &&
data.accessStatus != WorkflowDefinition.accessStatuses.PUBLIC &&
req.redisSession.data.role == User.roles.ROLE_ADMIN &&
req.redisSession.data.userId != definition.userId ){
notificationService.addNotification(definition.userId, NotificationType.codes.WORKFLOW_DEFINITION_UNPUBLISHED, definition.id, function (err, notification) {
if(err){
logger.error(err);
}
return callback(null, definition);
});
} else {
return callback(null, definition);
}
},
function (definition, callback) {
logger.debug('Update attributes');
definition.updateAttributes(data, {fields:['name', 'description', 'purpose', 'accessStatus']}).then(function () {
logger.debug('Attributes Updated ');
callback(null, definition);
})
},
function (definition, callback) {
if( definition.accessStatus == WorkflowDefinition.accessStatuses.SHARED ){
return self._updateDefinitionUserRelations( req, definition, data.users, function (err, definition) {
return callback(null, definition);
});
}
return callback(null, definition);
},
function (definition, callback) {
self.getWorkflowDefinitionOverview(req, definition.id, callback);
}
],
function (err, overview) {