Initial release

Change-Id: I04faafd3e3a508d9359138f07e3a10fa2d84689f
This commit is contained in:
Fotis Paraskevopoulos 2024-04-25 13:41:05 +03:00 committed by Radosław Piliszek
parent 29ecb6b8b6
commit efc6c9f6da
9 changed files with 412 additions and 262 deletions

4
.env Normal file
View File

@ -0,0 +1,4 @@
ACTIVEMQ_PORT=
ACTIVEMQ_HOST=
ACTIVEMQ_USERNAME=
ACTIVEMQ_PASSWORD=

View File

@ -1,5 +1,5 @@
require('dotenv').config();
const container= require('rhea');
const connection_options={
'port': process.env.ACTIVEMQ_PORT,
@ -10,14 +10,15 @@ const connection_options={
}
if(!connection_options.port || !connection_options.host) {
console.error("No connection option provided for EXN skipping asynchronous messaging")
return
if (!connection_options.port || !connection_options.host) {
console.error("No connection option provided for EXN skipping asynchronous messaging");
} else {
const connection = container.connect(connection_options);
}
const container= require('rhea');
let connection;
let sender_sal_nodecandidate_get;
let sender_sal_cloud_get;
let sender_sal_cloud_post;
@ -37,8 +38,6 @@ let sender_ui_policies_model_upsert;
const correlations = {}
container.on('message', (context)=>{
// console.log("Received ",context.message)
if(context.message.correlation_id in correlations){
if(context.message.body.metaData['status'] >= 400){
correlations[context.message.correlation_id]['reject'](context.message.body['message'])
@ -53,16 +52,16 @@ container.on('connection_open', function (context) {
console.log("Connected ",context.container.id);
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.get.reply')
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.post.reply')
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.create.reply')
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.delete.reply')
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.nodecandidate.get.reply')
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.node.post.reply')
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.node.create.reply')
sender_sal_nodecandidate_get = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.nodecandidate.get');
sender_sal_cloud_get = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.get');
sender_sal_cloud_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.post');
sender_sal_cloud_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.create');
sender_sal_cloud_delete = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.delete');
sender_sal_node_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.node.post');
sender_sal_node_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.node.create');
sender_ui_application_new = context.connection.open_sender('topic://eu.nebulouscloud.ui.application.new');
sender_ui_application_updated = context.connection.open_sender('topic://eu.nebulouscloud.ui.application.updated');
@ -77,7 +76,6 @@ container.on('connection_open', function (context) {
connection = container.connect();
const {v4: uuidv4} = require("uuid");
@ -91,14 +89,15 @@ module.exports = {
'reject':reject,
};
const message = {
to: sender_ui_deploy_application_new.options.target.address,
to: sender_ui_application_new.options.target.address,
correlation_id: correlation_id,
message_annotations: {application: uuid},
body:{
uuid: uuid
}
}
console.log("Send ", message)
sender_ui_deploy_application_new.send(message)
sender_ui_application_new.send(message)
})
},
@ -113,21 +112,28 @@ module.exports = {
const message = {
to: sender_ui_application_dsl_json.options.target.address,
correlation_id: correlation_id,
message_annotations: {application: uuid},
application_properties: {application: uuid},
body:json
}
sender_ui_application_dsl_json.send(message)
console.log("Sending ", sender_ui_application_dsl_metric.options.target.address, uuid,json)
const metrci_message = {
const metric_model_payload ={
'application': uuid,
'yaml': yaml
}
console.log("Sending ", sender_ui_application_dsl_metric.options.target.address, uuid, metric_model_payload)
const metric_message = {
to: sender_ui_application_dsl_metric.options.target.address,
correlation_id: correlation_id,
body:{
'yaml': yaml
}
message_annotations: {application: uuid},
application_properties: {application: uuid},
body:metric_model_payload
}
sender_ui_application_dsl_metric.send(message)
sender_ui_application_dsl_metric.send(metric_message)
return resolve()
})
},
@ -139,18 +145,20 @@ module.exports = {
'reject':reject,
};
const message = {
to: sender_ui_deploy_application_new.options.target.address,
to: sender_ui_application_updated.options.target.address,
correlation_id: correlation_id,
message_annotations: {application: uuid},
application_properties: {application: uuid},
body:{
uuid: uuid
}
}
console.log("Send ", message)
sender_ui_deploy_application_new.send(message)
sender_ui_application_updated.send(message)
})
},
register_cloud:( uuid, user ,secret ) =>{
register_cloud:(doc) =>{
return new Promise((resolve,reject)=>{
const correlation_id = uuidv4()
@ -159,6 +167,23 @@ module.exports = {
'reject':reject,
};
/**
*
* // Amazon Web Service Elastic Compute Cloud
* AWSEC2("aws-ec2"),
* // Azure VM
* AZUREVM("azure"),
* // Google CLoud Engine
* GCE("gce"),
* // OpenStack NOVA
* OPENSTACKNOVA("openstack"),
* // BYON, to be used for on-premise baremetal
* BYON("byon"),
* // EDGE nodes
* EDGE("edge");
*
*/
const message = {
to: sender_sal_cloud_post.options.target.address,
correlation_id: correlation_id,
@ -167,27 +192,27 @@ module.exports = {
userId: "admin"
},
body: JSON.stringify([{
"cloudId": uuid,
"cloudProviderName": "aws-ec2",
"cloudId": doc.uuid,
"cloudProviderName": doc._platform[0].provider_name,
"cloudType": "PUBLIC",
"securityGroup": null,
"subnet": null,
"securityGroup": doc.securityGroup || '',
"subnet": doc.subnet || null,
"sshCredentials": {
"username": null,
"keyPairName": "mkl",
"privateKey": null
"username": doc.sshCredentials.username,
"keyPairName": doc.sshCredentials.keyPairName,
"privateKey": doc.sshCredentials.privateKey
},
"endpoint": null,
"endpoint": doc.endpoint,
"scope": {
"prefix": null,
"value": null
},
"identityVersion": null,
"defaultNetwork": null,
"identityVersion": doc.identityVersion,
"defaultNetwork": doc.defaultNetwork,
"credentials": {
"user": user,
"secret": secret,
"domain": null
"user": doc.credentials.user,
"secret": doc.credentials.secret,
"domain": doc.credentials.domain || null
},
"blacklist": null
}])
@ -196,10 +221,6 @@ module.exports = {
console.log("Send ", message)
sender_sal_cloud_post.send(message)
})
},
deploy_application: (uuid) => {
},
get_cloud_candidates: () => {
return new Promise((resolve,reject)=> {
@ -213,7 +234,9 @@ module.exports = {
const message = {
to: sender_sal_nodecandidate_get.options.target.address,
correlation_id: correlation_id,
body: {}
body: {
body:[]
}
}
sender_sal_nodecandidate_get.send(message)
})

View File

@ -4,8 +4,7 @@ const math = require('mathjs');
module.exports = {
extractFromEquation: (equation)=>{
equation = equation || '';
const lowerCaseEquation = equation.toLowerCase();
return math.parse(lowerCaseEquation);
return math.parse(equation);
},
extractVariableNames: (mathNode) => {
let variableNames = new Set();

View File

@ -4,92 +4,107 @@ const yaml = require('yaml');
module.exports = {
yaml: (doc) => {
let object = _.clone(doc)
let object = _.cloneDeep(doc);
let componentsForAppSpecComp = [];
let componentsForAppWideScope = [];
const protectedVariables = ["_id", "type", "metaType", "organization", "_edit", "_publish", "variables", "utilityFunctions", "resources", "parameters",];
const protectedVariables = ["_id", "type", "metaType", "organization", "_edit", "_publish", "variables", "utilityFunctions", "resources"];
protectedVariables.forEach(p => {
delete object[p];
});
//Templates
if (object.templates) {
object.templates = object.templates.map(v => {
return {
id: v.id,
type: v.type,
range: [v.minValue, v.maxValue],
unit: v.unit
}
});
object.templates = object.templates.map(v => ({
id: v.id,
type: v.type,
range: [v.minValue, v.maxValue],
unit: v.unit
}));
}
object.metrics_comp = [];
object.metrics_global = [];
//Metrics
if (object.metrics) {
object.metrics.forEach(v => {
let metricsDetail = {};
if (v.type === 'composite') {
const componentNames = v.components.map(component => component.componentName).join(', ');
let windowDetail = {};
if (v.isWindowInput && v.input.type && v.input.interval && v.input.unit) {
windowDetail.type = v.input.type;
windowDetail.size = `${v.input.interval} ${v.input.unit}`;
windowDetail = {
type: v.input.type,
size: `${v.input.interval} ${v.input.unit}`,
};
}
if (v.isWindowOutput && v.output.type && v.output.interval && v.output.unit) {
windowDetail.output = `${v.output.type} ${v.output.interval} ${v.output.unit}`;
// Only add windowDetail to metricsDetail if it's not empty
if (Object.keys(windowDetail).length > 0) {
metricsDetail.window = windowDetail;
}
let componentNames = v.components ? v.components.map(component => component.componentName) : [];
if (componentNames.length === 1) {
componentsForAppSpecComp.push(componentNames[0]);
} else if (componentNames.length > 1) {
componentsForAppWideScope = componentsForAppWideScope.concat(componentNames);
}
metricsDetail = {
name: v.name,
type: v.type,
template: componentNames,
window: windowDetail
template: v.template,
formula: v.formula,
...metricsDetail,
};
if (v.isWindowOutput && v.output.type && v.output.interval && v.output.unit) {
metricsDetail.output = `${v.output.type} ${v.output.interval} ${v.output.unit}`;
}
} else if (v.type === 'raw') {
let configs = v.config.map(configItem => ({
[configItem.name]: configItem.value
}));
let windowDetailRaw = {};
let configObject = configs.reduce((acc, current) => ({ ...acc, ...current }), {});
if (v.isWindowInputRaw && v.inputRaw.type && v.inputRaw.interval && v.inputRaw.unit) {
windowDetailRaw.type = v.inputRaw.type;
windowDetailRaw.size = `${v.inputRaw.interval} ${v.inputRaw.unit}`;
}
if (v.isWindowOutputRaw && v.outputRaw.type && v.outputRaw.interval && v.outputRaw.unit) {
windowDetailRaw.output = `${v.outputRaw.type} ${v.outputRaw.interval} ${v.outputRaw.unit}`;
}
metricsDetail = {
name: v.name,
type: v.type,
sensor: {
type: v.sensor
},
window: windowDetailRaw
type: v.sensor,
config: configObject,
}
};
if (v.outputRaw && v.outputRaw.type && v.outputRaw.interval && v.outputRaw.unit) {
metricsDetail.output = `${v.outputRaw.type} ${v.outputRaw.interval} ${v.outputRaw.unit}`;
}
if (v.template) {
metricsDetail.template = v.template;
}
let componentNames = v.components ? v.components.map(component => component.componentName) : [];
if (componentNames.length === 1) {
componentsForAppSpecComp.push(componentNames[0]);
} else if (componentNames.length > 1) {
componentsForAppWideScope = componentsForAppWideScope.concat(componentNames);
}
}
const metric = {
metrics: metricsDetail
};
if (v.type === 'composite' && v.components.length < 2) {
object.metrics_global.push(metric);
} else if (v.type === 'composite' && v.components.length >= 2) {
object.metrics_comp.push(metric);
} else if (v.type === 'raw') {
object.metrics_global.push(metric);
if (v.components && v.components.length === 1) {
object.metrics_comp.push({...metricsDetail});
} else {
object.metrics_global.push({...metricsDetail});
}
});
}
//SLO Violations
if (object.sloViolations) {
const processSloViolations = (violations) => {
const buildConstraint = (v, parentCondition = '') => {
@ -99,29 +114,58 @@ module.exports = {
} else {
const childConstraints = v.children.map(child => buildConstraint(child, v.condition)).join(` ${v.condition} `);
if (v.not) {
constraint = `NOT (${childConstraints})`;
} else {
constraint = `(${childConstraints})`;
}
constraint = v.not ? `NOT (${childConstraints})` : `(${childConstraints})`;
}
return constraint;
};
const combinedConstraint = buildConstraint(violations);
const combinedConstraint = buildConstraint(JSON.parse(doc['sloViolations']));
const requirement = {
return [{
name: 'Combined SLO',
type: 'slo',
constraint: combinedConstraint
};
return [requirement];
}];
};
object.sloViolations = processSloViolations(JSON.parse(doc['sloViolations']));
object.sloViolations = processSloViolations(object.sloViolations);
}
let specContent = {
components: [
...componentsForAppSpecComp,
{
name: "spec-comp",
metrics: object.metrics_comp
}
], scopes: [{
name: "app-wide-scope",
components: componentsForAppWideScope,
metrics: object.metrics_global,
requirements: object.sloViolations ? object.sloViolations : [],
}]
};
//Parameters + push to app wide scope
if (object.parameters) {
const parametersMetrics = object.parameters.map(v => ({
name: v.name,
type: "constant",
initialValue: v.initialValue,
template: v.template
}));
if (parametersMetrics.length > 0) {
specContent.scopes.push({
name: "parameters-scope",
components: [],
metrics: parametersMetrics,
});
}
}
//Construct and return the final version
const yamlDoc = {
apiVersion: "nebulous/v1",
kind: "MetricModel",
@ -131,19 +175,10 @@ module.exports = {
app: object.title,
}
},
common: object.templates,
spec: {
components: object.metrics_comp
},
scopes: [
{
name: "app-wide-scope",
requirements: object.sloViolations,
components: object.metrics_global,
}
]
templates: object.templates,
spec: specContent
};
return yamlDoc;
}
};

View File

@ -134,7 +134,11 @@ module.exports = {
template: {
type: 'string',
label: 'Template'
}
},
initialValue: {
type: 'float',
label: 'Initial Value'
},
}
}
},
@ -188,8 +192,8 @@ module.exports = {
type: 'select',
label: 'Level',
choices: [
{ label: 'Global', value: 'Global' },
{ label: 'Components', value: 'Components' }
{ label: 'Global', value: 'global' },
{ label: 'Components', value: 'components' }
],
def: 'global'
},
@ -197,7 +201,7 @@ module.exports = {
type: 'array',
label: 'Components',
if: {
level: 'Components'
level: 'components'
},
fields: {
add: {
@ -210,10 +214,12 @@ module.exports = {
},
name: {
type: 'string',
label: 'Name',
if: {
type: ['composite', 'raw']
}
label: 'Name'
},
template: {
type: 'string',
label: 'Template',
textarea: true,
},
formula: {
type: 'string',
@ -239,7 +245,7 @@ module.exports = {
type: 'select',
label: 'Type Input',
choices: [
{label: 'All', value: 'all'},
{label: 'Batch', value: 'batch'},
{label: 'Sliding', value: 'sliding'}
],
},
@ -255,7 +261,8 @@ module.exports = {
{label: 'Sec', value: 'sec'},
{label: 'Min', value: 'min'},
{label: 'Hour', value: 'hour'},
{label: 'Day', value: 'day'}
{label: 'Day', value: 'day'},
{label: 'Events', value: 'events'}
],
},
},
@ -280,7 +287,8 @@ module.exports = {
type: 'select',
choices: [
{label: 'All', value: 'all'},
{label: 'Sliding', value: 'sliding'}
{label: 'First', value: 'first'},
{label: 'Last', value: 'last'}
],
},
interval: {
@ -330,47 +338,6 @@ module.exports = {
}
}
},
isWindowInputRaw: {
type: 'boolean',
label: 'Window Input',
if: {
type: 'raw'
}
},
inputRaw: {
type: 'object',
label: 'Input',
fields: {
add: {
type: {
type: 'select',
label: 'Type Input',
choices: [
{label: 'All', value: 'all'},
{label: 'Sliding', value: 'sliding'}
],
},
interval: {
type: 'integer',
label: 'Interval',
},
unit: {
type: 'select',
label: 'Unit',
choices: [
{label: 'Ms', value: 'ms'},
{label: 'Sec', value: 'sec'},
{label: 'Min', value: 'min'},
{label: 'Hour', value: 'hour'},
{label: 'Day', value: 'day'}
],
},
},
},
if: {
isWindowInputRaw: true
}
},
isWindowOutputRaw: {
type: 'boolean',
label: 'Window Output',
@ -387,7 +354,8 @@ module.exports = {
type: 'select',
choices: [
{label: 'All', value: 'all'},
{label: 'Sliding', value: 'sliding'}
{label: 'First', value: 'first'},
{label: 'Last', value: 'last'}
],
},
interval: {
@ -433,6 +401,7 @@ module.exports = {
label: 'Function Type',
choices: [
{label: 'Maximize', value: 'maximize'},
{label: 'Minimize', value: 'minimize'},
{label: 'Constant', value: 'constant'}
]
},
@ -558,11 +527,11 @@ module.exports = {
application_update_sender.send({
"body":{"uuid":doc.uuid},
"message_annotations":{
"subject":doc.uuid
"application":doc.uuid
}
});
application_dsl_generic.send({body:{}, "message_annotations":{
"subject":doc.uuid
"application":doc.uuid
}});
}
}
@ -585,11 +554,11 @@ module.exports = {
application_update_sender.send({
"body":{"uuid":doc.uuid},
"message_annotations":{
"subject":doc.uuid
"application":doc.uuid
}
});
application_dsl_generic.send({body:{}, "message_annotations":{
"subject":doc.uuid
"application":doc.uuid
}});
}
}
@ -786,10 +755,10 @@ module.exports = {
'string.base': 'Function Name must be a string.',
'any.required': 'Function Name is required.'
}),
functionType: Joi.string().valid('maximize', 'constant').insensitive().required().messages({
functionType: Joi.string().valid('maximize', 'constant','minimize').insensitive().required().messages({
'string.base': 'Function Type must be a string.',
'any.required': 'Function Type is required.',
'any.only': 'Function Type must be either "Maximize" or "Constant".'
'any.only': 'Function Type must be either "Maximize" , "Constant" or "Minimize.'
}),
functionExpression: Joi.string().trim().required().messages({
'string.base': 'Function Expression must be a string.',
@ -982,10 +951,12 @@ module.exports = {
try {
const updatedApp = await self.find(req,{ uuid: uuid , organization:adminOrganization }).project(projection).toArray();
const result = await exn.application_dsl(uuid,
kubevela.json(updatedApp.pop()),
""
)
const updatedAppItem = updatedApp.pop();
await exn.application_dsl(uuid,
kubevela.json(updatedAppItem), metric_model.yaml(updatedAppItem)
);
//TODO refactor to use apostrophe CMS ORM
await self.apos.doc.db.updateOne(
{ uuid: uuid },

View File

@ -8,9 +8,8 @@ module.exports = {
try {
let parsedEquation = mathutils.extractFromEquation(req.body.equation)
const variableNames = mathutils.extractVariableNames(parsedEquation);
const uppercaseVariableNames = variableNames.map(name => name.toUpperCase());
return {
variables: uppercaseVariableNames,
variables: variableNames,
};
} catch (error) {
throw error;

View File

@ -1,8 +1,13 @@
const {v4: uuidv4} = require("uuid");
const _ = require('lodash');
module.exports = {
extend: '@apostrophecms/piece-type',
options: {
label: 'Platform',
sort:{
sortOrder:1,
}
},
fields: {
@ -11,12 +16,29 @@ module.exports = {
type: 'string',
label: 'UUID',
required: false
},
provider_name: {
type: 'string',
label: 'Provider Name',
required: false
},
public: {
type: 'boolean',
label: 'Public',
required: true,
def: true
},
sortOrder: {
type: 'integer',
label: 'Sort Order',
required: true,
def: 0,
}
},
group: {
basics: {
label: 'Basics',
fields: ['uuid']
fields: ['uuid','public','sortOrder']
}
}
},
@ -38,10 +60,12 @@ module.exports = {
const projection = {
title: 1,
uuid: 1,
sortOrder: 1,
};
try {
const platforms = await self.find(req).project(projection).toArray();
return platforms;
return _.map(platforms, (p)=> { return {'title': p.title, 'uuid': p.uuid} })
} catch (error) {
throw self.apos.error('notfound', 'Platforms not found');
}

View File

@ -8,54 +8,130 @@ const projection = {
uuid: 1,
organization: 1,
platform: 1,
appId: 1,
appSecret: 1
subnet: 1,
endpoint: 1,
identityVersion: 1,
credentials: 1,
defaultNetwork: 1,
sshCredentials: 1,
_platform: 1
};
const resourcesSchema = Joi.object({
appId: Joi.string().required().messages({
'string.empty': 'App ID is required.',
'any.required': 'App ID is a required field.'
}),
appSecret: Joi.string().required().messages({
'string.empty': 'App Secret is required.',
'any.required': 'App Secret is a required field.'
}),
platform: Joi.string().required().messages({
'string.empty': 'Platform is required.',
_platform: Joi.required().messages({
'any.required': 'Platform is a required field.'
})
}).unknown().options({ abortEarly: false });
const credentialsSchema = Joi.object({
user: Joi.string().required().messages({
'string.empty': 'Credentials: Users is required.',
'any.required': 'Credentials: Users is a required field.'
}),
secret: Joi.string().required().messages({
'string.empty': 'Credentials: Secret is required.',
'any.required': 'Credentials: Secret is a required field.'
}),
}).unknown().options({ abortEarly: false });
module.exports = {
extend: '@apostrophecms/piece-type',
options: {
label: 'Resource',
label: 'Resource'
},
fields: {
add: {
uuid: {
type: 'string',
label: 'UUID',
readOnly: true
label: 'UUID'
},
platform: {
type: 'string',
label: 'Platform'
_platform: {
label: 'Platform',
type: 'relationship',
withType: "platforms",
max:1
},
appId: {
securityGroup:{
type: 'string',
label: 'App ID',
label: 'Security Group'
},
appSecret: {
type: 'string',
label: 'App Secret',
sshCredentials: {
type: 'object',
label: 'SSH Credentials',
fields: {
add: {
username: {
type: 'string',
label: 'Username',
},
privateKey: {
type: 'string',
label: 'Private Key',
textarea: true,
},
keyPairName: {
type: 'string',
label: 'Key Pair Name',
},
}
}
},
subnet:{
type: 'string',
label: 'Subnet'
},
endpoint:{
type: 'string',
label: 'Endpoint'
},
identityVersion:{
type: 'select',
label: 'Identity Version',
choices: [
{'label':'v3', value:'3'}
],
def: '3'
},
defaultNetwork:{
type: 'string',
label: 'Default Network'
},
credentials:{
type: 'object',
label: 'Credentials',
fields: {
add: {
user: {
type: 'string',
label: 'Username',
},
secret: {
type: 'string',
label: 'Secret',
textarea: true,
},
domain: {
type: 'string',
label: 'Domain',
},
}
}
}
},
group: {
basics: {
label: 'Details',
fields: ['title', 'uuid', 'platform', 'appId', 'appSecret']
fields: ['title', 'uuid', '_platform', 'identityVersion']
},
network: {
label: 'Network',
fields: ['defaultNetwork','endpoint', 'subnet']
},
security: {
label: 'Security',
fields: ['securityGroup','credentials','sshCredentials']
}
}
},
handlers(self) {
@ -79,16 +155,7 @@ module.exports = {
await generateUuid(doc);
try{
if(doc.aposMode === 'published'){
const message = await exn.register_cloud(
doc.uuid,
doc.appId,
doc.appSecret,
)
console.log("Registered ",message);
}
await self.updateWithPlatformInfo(doc);
await self.updateWithPlatformInfo(req,doc);
await assignOrganization(req, doc);
}catch(e){
@ -96,11 +163,10 @@ module.exports = {
}
}
},
beforeSave: {
async handler(req, doc, options) {
try {
await self.updateWithPlatformInfo(req,doc)
self.validateDocument(doc);
} catch (error) {
if (error.name === 'required' && error.error && error.error.length > 0) {
@ -113,28 +179,45 @@ module.exports = {
}
}
}
}
},
}
},
methods(self) {
return {
async updateWithPlatformInfo(doc) {
if (doc.platform && !doc.platformUpdated) {
const platformPiece = await self.apos.doc.db.findOne({
type: 'platforms',
uuid: doc.platform
});
async updateWithPlatformInfo(req, doc) {
if (platformPiece) {
doc.platform = platformPiece.title;
doc.platformUpdated = true;
} else {
throw self.apos.error('notfound', 'Platform not found');
if(req.body.platform && req.body.platform.uuid){
const platform = await self.apos.modules['platforms'].find(req,{'uuid':req.body.platform.uuid}).toObject();
if(!platform){
throw self.apos.error('notfound', 'Platform not found or empty');
}
doc.platformIds = [platform._id]
doc._platform = [platform]
delete req.body.platform
}
},
return doc
},
cleanUp:function(self,req, resources){
_.each(resources,(r)=>{
r= self.removeForbiddenFields(req,r)
r['platform'] = null
if( r._platform.length > 0 ){
r['platform'] = {
'uuid': r._platform[0].uuid,
'title': r._platform[0].title,
}
}
delete r._platform
})
return resources
},
validateDocument(doc) {
const validateField = (data, schema) => {
const {error} = schema.validate(data);
@ -147,6 +230,7 @@ module.exports = {
}
};
validateField(doc, resourcesSchema);
validateField(doc.credentials, credentialsSchema);
}
}
},
@ -159,10 +243,24 @@ module.exports = {
const adminOrganization = currentUser.organization;
try {
const filters = {
organization: adminOrganization
organization: adminOrganization,
};
const page = req.query.page || 1
const pageSize = req.query.pageSize || 10
const count = await self.find(req, filters).toCount()
const resources = await self.find(req, filters).project(projection)
.withPublished(true)
.perPage(pageSize)
.page(page)
.toArray()
return {
"total":count,
"page": page,
"results":self.cleanUp(self,req,resources)
};
const resources = await self.find(req, filters).project(projection).toArray();
return resources;
} catch (error) {
throw self.apos.error('notfound', 'Resource not found');
}
@ -182,19 +280,31 @@ module.exports = {
throw self.apos.error('notfound', 'Resource not found');
}
return doc;
return self.cleanUp(self,req, [doc])[0]
} catch (error) {
throw self.apos.error(error.name, error.message);
}
},
async 'candidates'(req) {
async ':uuid/candidates'(req) {
const uuid = req.params.uuid;
if (!( req.user.organization)) {
throw self.apos.error('forbidden', 'You do not have permission to perform this action');
}
const currentUser = req.user;
const adminOrganization = currentUser.organization;
try {
const doc = await self.find(req, { uuid: uuid , organization:adminOrganization}).project(projection).toObject();
if (!doc) {
throw self.apos.error('notfound', 'Resource not found');
}
await exn.register_cloud(doc)
await new Promise(resolve => setTimeout(resolve, 10000));
const message = await exn.get_cloud_candidates()
return _.map(JSON.parse(message.body), (r)=>{
return {
@ -262,7 +372,6 @@ module.exports = {
if (!(req.user.role === "admin" && req.user.organization)) {
throw self.apos.error('forbidden', 'You do not have permission to perform this action');
}
self.validateDocument(updateData);
const adminOrganization = req.user.organization;
@ -271,33 +380,18 @@ module.exports = {
uuid: uuid,
organization: adminOrganization
};
const resourcesToUpdate = await self.find(req, filters).project(projection).toArray();
if (!resourcesToUpdate || resourcesToUpdate.length === 0) {
const doc = await self.find(req, filters).toObject();
if (!doc) {
throw self.apos.error('notfound', 'Resource not found');
}
await self.updateWithPlatformInfo(req,doc)
self.validateDocument(doc)
const doc = resourcesToUpdate[0];
let update = { ...doc, ...updateData };
await self.update(req,update)
return self.cleanUp(self,req,[update])[0]
if ('platform' in updateData) {
let docToUpdate = { ...doc, ...updateData };
await self.updateWithPlatformInfo(docToUpdate);
await self.apos.doc.db.updateOne(
{ uuid: uuid },
{ $set: docToUpdate }
);
} else {
await self.apos.doc.db.updateOne(
{ uuid: uuid },
{ $set: updateData }
);
}
const resourceUpdated = await self.find(req, filters).project(projection).toArray();
return resourceUpdated;
} catch (error) {
throw self.apos.error(error.name, error.message);
}

View File

@ -42,6 +42,7 @@
"dependencies": {
"@johmun/vue-tags-input": "^2.1.0",
"apostrophe": "^3.61.1",
"dotenv": "^16.4.5",
"flat": "^5.0.2",
"joi": "^17.11.0",
"mathjs": "^12.2.1",