2018-04-15 12:18:10 +02:00
|
|
|
/**
|
|
|
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
**/
|
|
|
|
|
|
|
|
/**
|
2018-12-01 00:01:09 +01:00
|
|
|
* @mixin @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef Flows
|
|
|
|
* @type {object}
|
|
|
|
* @property {string} rev - the flow revision identifier
|
|
|
|
* @property {Array} flows - the flow configuration, an array of node configuration objects
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef Flow
|
|
|
|
* @type {object}
|
|
|
|
* @property {string} id - the flow identifier
|
|
|
|
* @property {string} label - a label for the flow
|
|
|
|
* @property {Array} nodes - an array of node configuration objects
|
|
|
|
*/
|
|
|
|
|
|
|
|
var runtime;
|
|
|
|
|
|
|
|
var api = module.exports = {
|
|
|
|
init: function(_runtime) {
|
|
|
|
runtime = _runtime;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Gets the current flow configuration
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {User} opts.user - the user calling the api
|
2019-08-09 17:56:11 +02:00
|
|
|
* @param {Object} opts.req - the request to log (optional)
|
2018-04-15 12:18:10 +02:00
|
|
|
* @return {Promise<Flows>} - the active flow configuration
|
2018-12-01 00:01:09 +01:00
|
|
|
* @memberof @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
getFlows: function(opts) {
|
|
|
|
return new Promise(function(resolve,reject) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flows.get"}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
return resolve(runtime.nodes.getFlows());
|
|
|
|
});
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Sets the current flow configuration
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {User} opts.user - the user calling the api
|
2019-08-09 17:56:11 +02:00
|
|
|
* @param {Object} opts.req - the request to log (optional)
|
2018-04-15 12:18:10 +02:00
|
|
|
* @return {Promise<Flows>} - the active flow configuration
|
2018-12-01 00:01:09 +01:00
|
|
|
* @memberof @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
setFlows: function(opts) {
|
|
|
|
return new Promise(function(resolve,reject) {
|
|
|
|
|
|
|
|
var flows = opts.flows;
|
|
|
|
var deploymentType = opts.deploymentType||"full";
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flows.set",type:deploymentType}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
|
|
|
|
var apiPromise;
|
|
|
|
if (deploymentType === 'reload') {
|
2018-09-25 12:20:50 +02:00
|
|
|
apiPromise = runtime.nodes.loadFlows(true);
|
2018-04-15 12:18:10 +02:00
|
|
|
} else {
|
|
|
|
if (flows.hasOwnProperty('rev')) {
|
|
|
|
var currentVersion = runtime.nodes.getFlows().rev;
|
|
|
|
if (currentVersion !== flows.rev) {
|
2018-04-28 16:57:32 +02:00
|
|
|
var err;
|
2018-04-15 12:18:10 +02:00
|
|
|
err = new Error();
|
|
|
|
err.code = "version_mismatch";
|
|
|
|
err.status = 409;
|
|
|
|
//TODO: log warning
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
apiPromise = runtime.nodes.setFlows(flows.flows,deploymentType);
|
|
|
|
}
|
|
|
|
apiPromise.then(function(flowId) {
|
|
|
|
return resolve({rev:flowId});
|
|
|
|
}).catch(function(err) {
|
2018-04-19 22:39:44 +02:00
|
|
|
runtime.log.warn(runtime.log._("api.flows.error-"+(deploymentType === 'reload'?'reload':'save'),{message:err.message}));
|
|
|
|
runtime.log.warn(err.stack);
|
2018-04-15 12:18:10 +02:00
|
|
|
return reject(err);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a flow configuration
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {User} opts.user - the user calling the api
|
|
|
|
* @param {Object} opts.flow - the flow to add
|
2019-08-09 17:56:11 +02:00
|
|
|
* @param {Object} opts.req - the request to log (optional)
|
2018-04-15 12:18:10 +02:00
|
|
|
* @return {Promise<String>} - the id of the added flow
|
2018-12-01 00:01:09 +01:00
|
|
|
* @memberof @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
addFlow: function(opts) {
|
|
|
|
return new Promise(function(resolve,reject) {
|
|
|
|
var flow = opts.flow;
|
|
|
|
runtime.nodes.addFlow(flow).then(function(id) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.add",id:id}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
return resolve(id);
|
|
|
|
}).catch(function(err) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.add",error:err.code||"unexpected_error",message:err.toString()}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
err.status = 400;
|
|
|
|
return reject(err);
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets an individual flow configuration
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {User} opts.user - the user calling the api
|
|
|
|
* @param {Object} opts.id - the id of the flow to retrieve
|
2019-08-09 17:56:11 +02:00
|
|
|
* @param {Object} opts.req - the request to log (optional)
|
2018-04-15 12:18:10 +02:00
|
|
|
* @return {Promise<Flow>} - the active flow configuration
|
2018-12-01 00:01:09 +01:00
|
|
|
* @memberof @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
getFlow: function(opts) {
|
|
|
|
return new Promise(function (resolve,reject) {
|
|
|
|
var flow = runtime.nodes.getFlow(opts.id);
|
|
|
|
if (flow) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.get",id:opts.id}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
return resolve(flow);
|
|
|
|
} else {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.get",id:opts.id,error:"not_found"}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
var err = new Error();
|
2018-04-24 16:01:49 +02:00
|
|
|
err.code = "not_found";
|
2018-04-15 12:18:10 +02:00
|
|
|
err.status = 404;
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Updates an existing flow configuration
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {User} opts.user - the user calling the api
|
|
|
|
* @param {Object} opts.id - the id of the flow to update
|
|
|
|
* @param {Object} opts.flow - the flow configuration
|
2019-08-09 17:56:11 +02:00
|
|
|
* @param {Object} opts.req - the request to log (optional)
|
2018-04-15 12:18:10 +02:00
|
|
|
* @return {Promise<String>} - the id of the updated flow
|
2018-12-01 00:01:09 +01:00
|
|
|
* @memberof @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
updateFlow: function(opts) {
|
|
|
|
return new Promise(function (resolve,reject) {
|
|
|
|
var flow = opts.flow;
|
|
|
|
var id = opts.id;
|
|
|
|
try {
|
|
|
|
runtime.nodes.updateFlow(id,flow).then(function() {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.update",id:id}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
return resolve(id);
|
|
|
|
}).catch(function(err) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
err.status = 400;
|
|
|
|
return reject(err);
|
|
|
|
})
|
|
|
|
} catch(err) {
|
|
|
|
if (err.code === 404) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.update",id:id,error:"not_found"}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
// TODO: this swap around of .code and .status isn't ideal
|
|
|
|
err.status = 404;
|
|
|
|
err.code = "not_found";
|
|
|
|
return reject(err);
|
|
|
|
} else {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
err.status = 400;
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Deletes a flow
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {User} opts.user - the user calling the api
|
|
|
|
* @param {Object} opts.id - the id of the flow to delete
|
2019-08-09 17:56:11 +02:00
|
|
|
* @param {Object} opts.req - the request to log (optional)
|
2018-04-15 12:18:10 +02:00
|
|
|
* @return {Promise} - resolves if successful
|
2018-12-01 00:01:09 +01:00
|
|
|
* @memberof @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
deleteFlow: function(opts) {
|
|
|
|
return new Promise(function (resolve,reject) {
|
|
|
|
var id = opts.id;
|
|
|
|
try {
|
|
|
|
runtime.nodes.removeFlow(id).then(function() {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.remove",id:id}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
return resolve();
|
2018-04-28 16:57:32 +02:00
|
|
|
}).catch(function(err) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.remove",id:id,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
|
2018-04-28 16:57:32 +02:00
|
|
|
err.status = 400;
|
|
|
|
return reject(err);
|
|
|
|
});
|
2018-04-15 12:18:10 +02:00
|
|
|
} catch(err) {
|
|
|
|
if (err.code === 404) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.remove",id:id,error:"not_found"}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
// TODO: this swap around of .code and .status isn't ideal
|
|
|
|
err.status = 404;
|
|
|
|
err.code = "not_found";
|
|
|
|
return reject(err);
|
|
|
|
} else {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "flow.remove",id:id,error:err.code||"unexpected_error",message:err.toString()}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
err.status = 400;
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the safe credentials for a node
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {User} opts.user - the user calling the api
|
|
|
|
* @param {String} opts.type - the node type to return the credential information for
|
|
|
|
* @param {String} opts.id - the node id
|
2019-08-09 17:56:11 +02:00
|
|
|
* @param {Object} opts.req - the request to log (optional)
|
2018-04-15 12:18:10 +02:00
|
|
|
* @return {Promise<Object>} - the safe credentials
|
2018-12-01 00:01:09 +01:00
|
|
|
* @memberof @node-red/runtime_flows
|
2018-04-15 12:18:10 +02:00
|
|
|
*/
|
|
|
|
getNodeCredentials: function(opts) {
|
|
|
|
return new Promise(function(resolve,reject) {
|
2019-08-09 17:56:11 +02:00
|
|
|
runtime.log.audit({event: "credentials.get",type:opts.type,id:opts.id}, opts.req);
|
2018-04-15 12:18:10 +02:00
|
|
|
var credentials = runtime.nodes.getCredentials(opts.id);
|
|
|
|
if (!credentials) {
|
|
|
|
return resolve({});
|
|
|
|
}
|
2018-04-28 16:57:32 +02:00
|
|
|
var definition = runtime.nodes.getCredentialDefinition(opts.type) || {};
|
2018-04-15 12:18:10 +02:00
|
|
|
|
|
|
|
var sendCredentials = {};
|
|
|
|
for (var cred in definition) {
|
|
|
|
if (definition.hasOwnProperty(cred)) {
|
|
|
|
if (definition[cred].type == "password") {
|
|
|
|
var key = 'has_' + cred;
|
|
|
|
sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sendCredentials[cred] = credentials[cred] || '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resolve(sendCredentials);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|