From b25a66e26edbe3006cf12903d222b1f39ac9d725 Mon Sep 17 00:00:00 2001 From: "mati108@gmail.com" <34651529+matbenitez@users.noreply.github.com> Date: Fri, 1 Feb 2019 17:40:36 -0300 Subject: [PATCH] Adding changes after team review - Add Wait Time explanation on add clone - AACloud logs, execution time - Remove status and logs queries from Kubernates and push them from AACloud - Order list results --- client/package.json | 2 +- client/src/views/Clone/Add/index.js | 11 ++- client/src/views/Clone/List/ListClones.js | 4 +- client/src/views/Home.js | 4 +- server/src/config/clone-deployment.json | 2 +- server/src/config/utils.js | 14 ---- server/src/errors/index.js | 10 ++- server/src/graphQLCalls/teamAvatar.js | 30 -------- .../{teams_FbByTeamMember.js => teamQuery.js} | 17 +++-- server/src/schema/cloneDetails.js | 52 ++++++++++---- .../src/schema/mutations/AddCloneMutation.js | 10 ++- .../schema/mutations/RemoveCloneMutation.js | 8 +-- .../mutations/UpdateExecutionSummary.js | 71 ++++++++++++------- server/src/schema/queries/HistoryClones.js | 22 +++--- server/src/schema/queries/ListClones.js | 10 +-- 15 files changed, 146 insertions(+), 121 deletions(-) delete mode 100644 server/src/graphQLCalls/teamAvatar.js rename server/src/graphQLCalls/{teams_FbByTeamMember.js => teamQuery.js} (59%) diff --git a/client/package.json b/client/package.json index 55e914360..c0a9f35e9 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@superalgos/operations-client", - "version": "0.0.6", + "version": "0.0.7", "description": "Superalgos Operations client-side web app", "main": "src/index.js", "dependencies": { diff --git a/client/src/views/Clone/Add/index.js b/client/src/views/Clone/Add/index.js index b24d35a0c..06781d9ad 100644 --- a/client/src/views/Clone/Add/index.js +++ b/client/src/views/Clone/Add/index.js @@ -157,7 +157,6 @@ class AddClone extends Component { {this.state.selectedBot.kind} - { /* TRADER BOT */ } { this.state.selectedBot.kind === "TRADER" && @@ -213,6 +212,10 @@ class AddClone extends Component { fullWidth /> + + The Wait Time represent the number of miliseconds the bot will wait between executions. + + } - { /* INDICATOR AND EXTRACTOR BOTS */ } { (this.state.selectedBot.kind === "INDICATOR" || this.state.selectedBot.kind === "EXTRACTOR") && @@ -366,6 +368,11 @@ class AddClone extends Component { } + + + You will your clone information under Active Clones until it finishes the execution, then it will moved to the History. + +
diff --git a/client/src/views/Home.js b/client/src/views/Home.js index 2e2de92b5..766c931a2 100644 --- a/client/src/views/Home.js +++ b/client/src/views/Home.js @@ -24,13 +24,13 @@ class Home extends Component {
- The Operations module allows you to create copies of your bot + The Operations module allows you to create instances of your bots that will be executed on a virtual machine.
- +
diff --git a/server/src/config/clone-deployment.json b/server/src/config/clone-deployment.json index 4b202ce6b..bc90901a1 100644 --- a/server/src/config/clone-deployment.json +++ b/server/src/config/clone-deployment.json @@ -14,7 +14,7 @@ "containers": [ { "name": "aadev", - "image": "benme/aadev:v3.5", + "image": "benme/aadev:v3.6", "command": ["node", "Run.js"], "env": [ ] diff --git a/server/src/config/utils.js b/server/src/config/utils.js index 257881ebc..11f9909fa 100644 --- a/server/src/config/utils.js +++ b/server/src/config/utils.js @@ -7,17 +7,3 @@ export function toPlatformDatetime (epoch) { export function isDefined (d) { return d !== null && typeof d !== 'undefined' } - -export function getJobNameFromClone (clone) { - return clone.teamSlug + '-' + clone.botSlug + '-' + clone.mode.toLowerCase() -} - -export function getSelectedBot(botsByUser, selectedBotId){ - if(isDefined(botsByUser)){ - for (var i = 0; i < botsByUser.fb.length; i++) { - if(botsByUser.fb[i].id === selectedBotId){ - return botsByUser.fb[i] - } - } - } -} diff --git a/server/src/errors/index.js b/server/src/errors/index.js index b8fb06cf4..63b27a61a 100644 --- a/server/src/errors/index.js +++ b/server/src/errors/index.js @@ -2,7 +2,15 @@ import logger from '../config/logger' export class AuthentificationError extends Error { code = 401 - message = 'Autentification not found, you have to be authentificated to perform this action' + message = 'Autentification not found, you have to be authentificated to perform this action.' + constructor(){ + super() + } +} + +export class AutorizationError extends Error { + code = 401 + message = 'You are not authorized to perform this action.' constructor(){ super() } diff --git a/server/src/graphQLCalls/teamAvatar.js b/server/src/graphQLCalls/teamAvatar.js deleted file mode 100644 index c2ba09a3f..000000000 --- a/server/src/graphQLCalls/teamAvatar.js +++ /dev/null @@ -1,30 +0,0 @@ -import logger from '../config/logger' -import axios from 'axios' - -const teamAvatar = async(authorization, teamId) => { - logger.debug('GraphQL call teamAvatar.') - - return axios({ - url: process.env.GATEWAY_ENDPOINT, - method: 'post', - data: { - query: ` - query Teams_Team($teamId: String!){ - teams_TeamById(teamId:$teamId) { - profile{ - avatar - } - } - } - `, - variables: { - teamId: teamId - } - }, - headers: { - authorization: authorization - } - }) -}; - -export default teamAvatar diff --git a/server/src/graphQLCalls/teams_FbByTeamMember.js b/server/src/graphQLCalls/teamQuery.js similarity index 59% rename from server/src/graphQLCalls/teams_FbByTeamMember.js rename to server/src/graphQLCalls/teamQuery.js index 112e95790..c42c1722a 100644 --- a/server/src/graphQLCalls/teams_FbByTeamMember.js +++ b/server/src/graphQLCalls/teamQuery.js @@ -1,16 +1,16 @@ import logger from '../config/logger' import axios from 'axios' -const teams_FbByTeamMember = (authorization) => { - logger.debug('GraphQL call teams_FbByTeamMember') +const teamQuery = async(authorization, teamId) => { + logger.debug('GraphQL retrieving team information. %s', teamId) return axios({ url: process.env.GATEWAY_ENDPOINT, method: 'post', data: { query: ` - query Teams_FbByTeamMember { - teams_FbByTeamMember { + query Teams_Team($teamId: String!){ + teams_TeamById(teamId:$teamId) { id name slug @@ -24,11 +24,18 @@ const teams_FbByTeamMember = (authorization) => { members { member { alias + authId } } + profile{ + avatar + } } } `, + variables: { + teamId: teamId + } }, headers: { authorization: authorization @@ -36,4 +43,4 @@ const teams_FbByTeamMember = (authorization) => { }) }; -export default teams_FbByTeamMember +export default teamQuery diff --git a/server/src/schema/cloneDetails.js b/server/src/schema/cloneDetails.js index 040ce1a7d..56d2d5f93 100644 --- a/server/src/schema/cloneDetails.js +++ b/server/src/schema/cloneDetails.js @@ -1,27 +1,55 @@ import logger from '../config/logger' -import { getJobNameFromClone, getSelectedBot } from '../config/utils' -import teamAvatar from '../graphQLCalls/teamAvatar' +import { isDefined } from '../config/utils' +import { AutorizationError } from '../errors' -// TODO Refactor this code once financial beings api is ready -const cloneDetails = async(authorization, botsByUser, clone) => { +const cloneDetails = async(authId, team, clone) => { logger.debug('Retrieving clone details.') - let selectedBot = getSelectedBot(botsByUser.data.data.teams_FbByTeamMember, clone.botId) - clone.teamId = botsByUser.data.data.teams_FbByTeamMember.id + if(!isAuthorized(team, authId)){ + logger.debug('User %s is not authorized to manage team %s.', authId, team.slug ) + throw new AutorizationError() + } + + let selectedBot = getSelectedBot(team, clone.botId) + clone.teamId = team.id clone.botId = selectedBot.id clone.kind = selectedBot.kind - clone.teamSlug = botsByUser.data.data.teams_FbByTeamMember.slug + clone.teamSlug = team.slug clone.botSlug = selectedBot.slug - clone.cloneName = getJobNameFromClone(clone) - clone.teamName = botsByUser.data.data.teams_FbByTeamMember.name + clone.cloneName = clone.teamSlug + '-' + clone.botSlug + '-' + clone.mode.toLowerCase() + clone.teamName = team.name clone.botName = selectedBot.name clone.botAvatar = selectedBot.avatar - let team = await teamAvatar(authorization, botsByUser.data.data.teams_FbByTeamMember.id) - clone.teamAvatar = team.data.data.teams_TeamById.profile.avatar - clone.userLoggedIn = botsByUser.data.data.teams_FbByTeamMember.members[0].member.alias //TODO change by context info if possible + clone.teamAvatar = team.profile.avatar + clone.userLoggedIn = team.members[0].member.alias //TODO change by context info if possible logger.debug('Retrieving clone details complete.') return clone }; +function getSelectedBot(team, selectedBotId){ + logger.debug('Getting selected bot.') + if(isDefined(team)){ + for (var i = 0; i < team.fb.length; i++) { + if(team.fb[i].id === selectedBotId){ + return team.fb[i] + } + } + } +} + +function isAuthorized(team, authId){ + logger.debug('Checking if the user is authorized by the teams module.') + let authorized = false + if(isDefined(team)){ + for (var i = 0; i < team.members.length; i++) { + if(team.members[i].member.authId === authId){ + authorized = true + break + } + } + } + return authorized +} + export default cloneDetails diff --git a/server/src/schema/mutations/AddCloneMutation.js b/server/src/schema/mutations/AddCloneMutation.js index d6f703ae5..dd14e931e 100644 --- a/server/src/schema/mutations/AddCloneMutation.js +++ b/server/src/schema/mutations/AddCloneMutation.js @@ -3,7 +3,7 @@ import { CloneType } from '../types'; import { Clone } from '../../models'; import { CloneModeEnum } from '../../enums/CloneMode'; import { BotTypesEnum } from '../../enums/BotTypes'; -import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember'; +import teamQuery from '../../graphQLCalls/teamQuery' import cloneDetails from '../cloneDetails' import { @@ -14,7 +14,7 @@ import { import logger from '../../config/logger' import createKubernetesClone from '../../kubernetes/createClone' -import { isDefined, getSelectedBot } from '../../config/utils' +import { isDefined } from '../../config/utils' const args = { clone: {type: CloneInputType } @@ -50,10 +50,8 @@ const resolve = async(parent, { clone }, context) => { } try{ - // Authorization is handled by the teams module - let botsByUser = await teams_FbByTeamMember(context.authorization) - clone = await cloneDetails(context.authorization, botsByUser, clone) - let selectedBot = getSelectedBot(botsByUser.data.data.teams_FbByTeamMember, clone.botId) + let team = await teamQuery(context.authorization) + clone = await cloneDetails(context.userId, team.data.data.teams_TeamById, clone) clone.createDatetime = new Date().valueOf() / 1000|0 clone.active = true diff --git a/server/src/schema/mutations/RemoveCloneMutation.js b/server/src/schema/mutations/RemoveCloneMutation.js index 8d1afaf5e..9dc45173f 100644 --- a/server/src/schema/mutations/RemoveCloneMutation.js +++ b/server/src/schema/mutations/RemoveCloneMutation.js @@ -12,8 +12,8 @@ import { import { Clone } from '../../models' import logger from '../../config/logger' import removeKuberneteClone from '../../kubernetes/removeClone' -import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember' -import { isDefined, getSelectedBot } from '../../config/utils' +import teamQuery from '../../graphQLCalls/teamQuery' +import { isDefined } from '../../config/utils' import cloneDetails from '../cloneDetails' const args = { @@ -37,8 +37,8 @@ const resolve = async (parent, { id }, context) => { throw new OperationsError('You are not authorized to remove this clone.') } - let botsByUser = await teams_FbByTeamMember(context.authorization) - clone = await cloneDetails(context.authorization, botsByUser, clone) + let team = await teamQuery(context.authorization) + clone = await cloneDetails(context.userId, team.data.data.teams_TeamById, clone) logger.debug('removeClone -> Removing Clone from Kubernates.') await removeKuberneteClone(clone) diff --git a/server/src/schema/mutations/UpdateExecutionSummary.js b/server/src/schema/mutations/UpdateExecutionSummary.js index b666b455c..235f84c41 100644 --- a/server/src/schema/mutations/UpdateExecutionSummary.js +++ b/server/src/schema/mutations/UpdateExecutionSummary.js @@ -7,12 +7,12 @@ import { } from 'graphql' import { - OperationsError } from '../../errors' import { Clone } from '../../models' import logger from '../../config/logger' +import teamQuery from '../../graphQLCalls/teamQuery' const args = { id: { type: new GraphQLNonNull(GraphQLID) }, @@ -29,34 +29,57 @@ const args = { const resolve = async (parent, { id, summaryDate, buyAverage, sellAverage, marketRate, combinedProfitsA, combinedProfitsB, assetA, assetB - }) => { + }, context) => { + logger.debug('UpdateExecutionSummary -> Entering Function.') - const query = { _id: id } - const options = { new: true } - const update = { - summaryDate: summaryDate, - buyAverage: buyAverage, - sellAverage: sellAverage, - marketRate: marketRate, - combinedProfitsA: combinedProfitsA, - combinedProfitsB: combinedProfitsB, - assetA: assetA, - assetB: assetB + if (!context.userId) { + throw new AuthentificationError() } - let sucessful - await Clone.findOneAndUpdate(query, update, options, (err, doc) => { - if (err){ - throw new OperationsError(err) - } else { - logger.debug('Execution summary updated') - sucessful = true - } - }) + try{ + let sucessful - if(sucessful){ - return 'Execution summary updated.' + let clone = await Clone.findOne( { + _id: id, + active: true + }) + + let team = await teamQuery(context.authorization) + clone = await cloneDetails(context.userId, team.data.data.teams_TeamById, clone) + + logger.debug('removeClone -> Removing Clone from Kubernates.') + await removeKuberneteClone(clone) + + const query = { _id: id } + const options = { new: true } + const update = { + summaryDate: summaryDate, + buyAverage: buyAverage, + sellAverage: sellAverage, + marketRate: marketRate, + combinedProfitsA: combinedProfitsA, + combinedProfitsB: combinedProfitsB, + assetA: assetA, + assetB: assetB + } + + await Clone.update(query, update, (err) => { + if (err){ + logger.error('Error updating execution summary on the database. %s', err.stack) + throw new OperationsError(err.message) + } else { + logger.debug('Execution summary updated.') + sucessful = true + } + }) + + if(sucessful){ + return 'Execution summary updated.' + } + } catch (error) { + logger.error('Error updating the execution summary. %s', error.stack) + throw new OperationsError(error.message) } } diff --git a/server/src/schema/queries/HistoryClones.js b/server/src/schema/queries/HistoryClones.js index a37557ae7..bdf853f3c 100644 --- a/server/src/schema/queries/HistoryClones.js +++ b/server/src/schema/queries/HistoryClones.js @@ -3,7 +3,8 @@ import { } from 'graphql' import { - AuthentificationError + AuthentificationError, + OperationsError } from '../../errors' import { CloneType } from '../types' @@ -11,14 +12,14 @@ import { Clone } from '../../models' import logger from '../../config/logger' import getKuberneteClonePodStatus from '../../kubernetes/getClonePodStatus' import getKuberneteClonePodLogs from '../../kubernetes/getClonePodLogs' -import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember' import cloneDetails from '../cloneDetails' +import teamQuery from '../../graphQLCalls/teamQuery' const args = {} const resolve = async(parent, args, context) => { - logger.debug('History Clones -> Entering Fuction.') - + logger.debug('Retrieving History Clones.') + try{ if (!context.userId) { throw new AuthentificationError() } @@ -26,18 +27,19 @@ const resolve = async(parent, args, context) => { let clones = await Clone.find({ authId: context.userId, active: false - }) - - // TODO Refactor this code once financial beings api is ready - // Authorization is handled by the teams module - let botsByUser = await teams_FbByTeamMember(context.authorization) + }).sort({createDatetime: -1}) for (var i = 0; i < clones.length; i++) { - clones[i] = await cloneDetails(context.authorization, botsByUser, clones[i]) + let team = await teamQuery(context.authorization, clones[i].teamId) + clones[i] = await cloneDetails(context.userId, team.data.data.teams_TeamById, clones[i]) clones[i].botType = clones[i].kind // Only for listing we show the teams value } return clones + } catch (err){ + logger.error('Retrieving History Clones error: %s', err.stack) + throw new OperationsError('There has been an error retrieving history clones.') + } } const query = { diff --git a/server/src/schema/queries/ListClones.js b/server/src/schema/queries/ListClones.js index 74e0868b4..66d283e80 100644 --- a/server/src/schema/queries/ListClones.js +++ b/server/src/schema/queries/ListClones.js @@ -12,7 +12,6 @@ import { Clone } from '../../models' import logger from '../../config/logger' import getCloneStatus from '../../kubernetes/getClonePodStatus' import getCloneLogs from '../../kubernetes/getClonePodLogs' -import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember' import cloneDetails from '../cloneDetails' const args = {} @@ -27,14 +26,11 @@ const resolve = async(parent, args, context) => { let clones = await Clone.find({ authId: context.userId, active: true - }) - - // TODO Refactor this code once financial beings api is ready - // Authorization is handled by the teams module - let botsByUser = await teams_FbByTeamMember(context.authorization) + }).sort({createDatetime: -1}) for (var i = 0; i < clones.length; i++) { - clones[i] = await cloneDetails(context.authorization, botsByUser, clones[i]) + let team = await teamQuery(context.authorization, clones[i].teamId) + clones[i] = await cloneDetails(context.userId, team.data.data.teams_TeamById, clones[i]) let state = await getCloneStatus(clones[i].cloneName) clones[i].state = state.substring(1, state.length-1) let lastLogs = await getCloneLogs(clones[i].cloneName)