From 59c7600ae29560c416265158baef094aa773442d Mon Sep 17 00:00:00 2001 From: "mati108@gmail.com" <34651529+matbenitez@users.noreply.github.com> Date: Wed, 30 Jan 2019 11:24:36 -0300 Subject: [PATCH] New client and server version --- client/package.json | 2 +- client/src/GraphQL/Fragments/clone.js | 2 +- client/src/views/Clone/Add/index.js | 12 +++- client/src/views/Clone/List/ListClones.js | 6 +- client/src/views/Clone/List/styles.js | 13 ++-- server/src/config/clone-deployment.json | 2 +- server/src/config/envConfig.js | 45 ------------ server/src/config/utils.js | 4 +- server/src/enums/BotTypes.js | 11 +++ server/src/enums/CloneMode.js | 11 +-- server/src/errors/index.js | 14 ---- server/src/kubernetes/createClone.js | 69 ++++++++++++++----- server/src/kubernetes/removeClone.js | 12 ++-- .../src/schema/{queries => }/cloneDetails.js | 19 +++-- .../src/schema/mutations/AddCloneMutation.js | 21 +++--- .../schema/mutations/RemoveCloneMutation.js | 8 +-- server/src/schema/queries/HistoryClones.js | 2 +- server/src/schema/queries/ListClones.js | 2 +- server/src/schema/types/CloneType.js | 2 +- server/src/schema/types/input/Clone.js | 2 +- 20 files changed, 132 insertions(+), 127 deletions(-) delete mode 100644 server/src/config/envConfig.js create mode 100644 server/src/enums/BotTypes.js rename server/src/schema/{queries => }/cloneDetails.js (50%) diff --git a/client/package.json b/client/package.json index 934363ed9..f010087e1 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@superalgos/operations-client", - "version": "0.0.3", + "version": "0.0.4", "description": "Superalgos Operations client-side web app", "main": "src/index.js", "dependencies": { diff --git a/client/src/GraphQL/Fragments/clone.js b/client/src/GraphQL/Fragments/clone.js index 3cecd787d..26a5aab59 100644 --- a/client/src/GraphQL/Fragments/clone.js +++ b/client/src/GraphQL/Fragments/clone.js @@ -24,7 +24,7 @@ export const clone = gql` combinedProfitsB assetA assetB - kind + botType teamName botName botAvatar diff --git a/client/src/views/Clone/Add/index.js b/client/src/views/Clone/Add/index.js index b58257b37..b24d35a0c 100644 --- a/client/src/views/Clone/Add/index.js +++ b/client/src/views/Clone/Add/index.js @@ -435,7 +435,8 @@ class AddClone extends Component { botId: this.state.selectedBot.id, mode: this.state.mode, resumeExecution: this.state.resumeExecution, - runAsTeam: this.state.runAsTeam + runAsTeam: this.state.runAsTeam, + botType: this.getBotTypeFromKind(this.state.selectedBot.kind) } } @@ -457,6 +458,15 @@ class AddClone extends Component { }) } + getBotTypeFromKind(kind){ + if(kind === 'TRADER') + return 'Trading' + else if(kind === 'INDICATOR') + return 'Indicator' + else if(kind === 'EXTRACTOR') + return 'Extraction' + } + handleNewCloneConfirmationOpen = () => { this.setState({ isNewCloneConfirmationOpen: true }) }; diff --git a/client/src/views/Clone/List/ListClones.js b/client/src/views/Clone/List/ListClones.js index 433890125..67535515a 100644 --- a/client/src/views/Clone/List/ListClones.js +++ b/client/src/views/Clone/List/ListClones.js @@ -88,7 +88,7 @@ class ListClones extends Component {
Last Execution: - { (clone.kind === 'TRADER' && isDefined(clone.assetA) ) && + { (clone.botType === 'Trading' && isDefined(clone.assetA) ) && Buy Average: Sell Average: @@ -100,7 +100,7 @@ class ListClones extends Component {
{isDefined(clone.summaryDate) ? toLocalTime(clone.summaryDate): 'waiting update...'} - { ((clone.kind === 'INDICATOR' || clone.kind === 'EXTRACTOR') && isDefined(clone.assetA) ) && + { ((clone.botType === 'Indicator' || clone.botType === 'Extraction') && isDefined(clone.assetA) ) && {clone.buyAverage} {clone.sellAverage} @@ -187,7 +187,7 @@ class ListClones extends Component { Last clone logs - + Here you can check the clone output directly from the virtual machine. The last 20 lines are displayed, but you can check the Logs Module for more details. diff --git a/client/src/views/Clone/List/styles.js b/client/src/views/Clone/List/styles.js index 418db1c3e..f2c33d236 100644 --- a/client/src/views/Clone/List/styles.js +++ b/client/src/views/Clone/List/styles.js @@ -33,9 +33,11 @@ const styles = theme => ({ }, textArea: { width:'1750px', - marginLeft: theme.spacing.unit, - marginRight: theme.spacing.unit, + margin: theme.spacing.unit, whiteSpace: 'pre', + width:'98%', + overflowY:'hidden', + overflowX:'hidden' }, heading: { fontSize: theme.typography.pxToRem(18), @@ -50,13 +52,10 @@ const styles = theme => ({ borderLeft: `2px solid ${theme.palette.divider}`, padding: `0px ${theme.spacing.unit * 2}px`, }, - logsDialog:{ - overflowY:'hidden', - overflowX:'hidden' - }, logsWrapper:{ + width:'1750px', overflowY:'auto', - overflowX:'auto' + overflowX:'hidden' }, details:{ display:'flex' diff --git a/server/src/config/clone-deployment.json b/server/src/config/clone-deployment.json index 770e7bce1..db84d8c18 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.3", + "image": "benme/aadev:v3.4", "command": ["node", "Run.js"], "env": [ ] diff --git a/server/src/config/envConfig.js b/server/src/config/envConfig.js deleted file mode 100644 index a831a6bad..000000000 --- a/server/src/config/envConfig.js +++ /dev/null @@ -1,45 +0,0 @@ -import 'dotenv/config' - -const env = [] - -env.push({ - "name": "PLATFORM_ENVIRONMENT", - "value": process.env.PLATFORM_ENVIRONMENT -}) - -env.push({ - "name": "GATEWAY_ENDPOINT", - "value": process.env.GATEWAY_ENDPOINT -}) - -env.push({ - "name": "STORAGE_BASE_URL", - "value": process.env.STORAGE_BASE_URL -}) - -env.push({ - "name": "STORAGE_CONNECTION_STRING", - "value": process.env.STORAGE_CONNECTION_STRING -}) - -env.push({ - "name": "AUTH_URL", - "value": process.env.AUTH_URL -}) - -env.push({ - "name": "AUTH_CLIENT_ID", - "value": process.env.AUTH_CLIENT_ID -}) - -env.push({ - "name": "AUTH_CLIENT_SECRET", - "value": process.env.AUTH_CLIENT_SECRET -}) - -env.push({ - "name": "AUTH_AUDIENCE", - "value": process.env.AUTH_AUDIENCE -}) - -export default env diff --git a/server/src/config/utils.js b/server/src/config/utils.js index e9f3b1c12..257881ebc 100644 --- a/server/src/config/utils.js +++ b/server/src/config/utils.js @@ -8,8 +8,8 @@ export function isDefined (d) { return d !== null && typeof d !== 'undefined' } -export function getJobNameFromClone (teamSlug, botSlug, mode) { - return teamSlug + '-' + botSlug + '-' + mode.toLowerCase() +export function getJobNameFromClone (clone) { + return clone.teamSlug + '-' + clone.botSlug + '-' + clone.mode.toLowerCase() } export function getSelectedBot(botsByUser, selectedBotId){ diff --git a/server/src/enums/BotTypes.js b/server/src/enums/BotTypes.js new file mode 100644 index 000000000..62cf49339 --- /dev/null +++ b/server/src/enums/BotTypes.js @@ -0,0 +1,11 @@ +export const TRADER = 'Trading' +export const INDICATOR = 'Indicator' +export const EXTRACTION = 'Extraction' + +export const BotTypesEnum = [ + TRADER, + INDICATOR, + EXTRACTION +] + +export default BotTypesEnum diff --git a/server/src/enums/CloneMode.js b/server/src/enums/CloneMode.js index 4205db6be..2a3109f26 100644 --- a/server/src/enums/CloneMode.js +++ b/server/src/enums/CloneMode.js @@ -1,9 +1,11 @@ export const LIVE = 'Live' export const BACKTEST = 'Backtest' export const COMPETITION = 'Competition' -export const ALL_MONTHS = 'All Months' -export const ONE_MONTH = 'One Month' -export const NO_TIME = 'No Time' +export const ALL_MONTHS = 'allMonths' +export const ONE_MONTH = 'oneMonth' +export const NO_TIME = 'noTime' +export const FIXED_INTERVAL = 'fixedInterval' + export const CloneModeEnum = [ LIVE, @@ -11,7 +13,8 @@ export const CloneModeEnum = [ COMPETITION, ALL_MONTHS, ONE_MONTH, - NO_TIME + NO_TIME, + FIXED_INTERVAL ] export default CloneModeEnum diff --git a/server/src/errors/index.js b/server/src/errors/index.js index 88ee37eae..b8fb06cf4 100644 --- a/server/src/errors/index.js +++ b/server/src/errors/index.js @@ -5,7 +5,6 @@ export class AuthentificationError extends Error { message = 'Autentification not found, you have to be authentificated to perform this action' constructor(){ super() - logger.error(this.stack) } } @@ -14,7 +13,6 @@ export class DatabaseError extends Error { message = 'Ressource not found : ' + this.message constructor(message){ super(message) - logger.error(this.stack) } } @@ -23,7 +21,6 @@ export class WrongArgumentsError extends Error { message = 'Wrong arguments : ' + this.message constructor(message){ super(message) - logger.error(this.stack) } } @@ -32,7 +29,6 @@ export class ServiceUnavailableError extends Error { message = 'At least one service is unresponding ' + this.message constructor(message){ super(message) - logger.error(this.stack) } } @@ -52,13 +48,3 @@ export class KubernateError extends Error { logger.error(this.stack) } } - -export class CustomError extends Error { - code = 500 - message = this.message - constructor(message){ - super(message) - logger.error(this.stack) - this.stack = '' - } -} diff --git a/server/src/kubernetes/createClone.js b/server/src/kubernetes/createClone.js index e68654ec3..165b96db4 100644 --- a/server/src/kubernetes/createClone.js +++ b/server/src/kubernetes/createClone.js @@ -1,40 +1,66 @@ import logger from '../config/logger' -import { toPlatformDatetime, getJobNameFromClone, isDefined } from '../config/utils' -import envConfig from '../config/envConfig' +import { toPlatformDatetime, isDefined } from '../config/utils' import { KubernateError } from '../errors' import { Client, config } from 'kubernetes-client' // Get base Deployment config import deploymentManifest from '../config/clone-deployment.json' import { BACKTEST} from '../enums/CloneMode'; -const createClone = async (clone, teamSlug, botSlug) => { +const createClone = async (clone) => { try { - let cloneName = getJobNameFromClone(teamSlug, botSlug, clone.mode) - logger.info("createClone %s", cloneName) + logger.info("createClone %j", clone) const client = new Client({config: config.fromKubeconfig(), version: '1.9'}) // Make changes to base deployment config - deploymentManifest.metadata.name = cloneName + deploymentManifest.metadata.name = clone.cloneName logger.debug("createClone Environment and Auth Configuration.") - let env = envConfig + let env = [] + env.push({ + "name": "PLATFORM_ENVIRONMENT", + "value": process.env.PLATFORM_ENVIRONMENT + }) + env.push({ + "name": "GATEWAY_ENDPOINT", + "value": process.env.GATEWAY_ENDPOINT + }) + env.push({ + "name": "STORAGE_BASE_URL", + "value": process.env.STORAGE_BASE_URL + }) + env.push({ + "name": "STORAGE_CONNECTION_STRING", + "value": process.env.STORAGE_CONNECTION_STRING + }) + env.push({ + "name": "AUTH_URL", + "value": process.env.AUTH_URL + }) + env.push({ + "name": "AUTH_CLIENT_ID", + "value": process.env.AUTH_CLIENT_ID + }) + env.push({ + "name": "AUTH_CLIENT_SECRET", + "value": process.env.AUTH_CLIENT_SECRET + }) + env.push({ + "name": "AUTH_AUDIENCE", + "value": process.env.AUTH_AUDIENCE + }) logger.debug("createClone General Financial Being Configuration.") env.push({ "name": "DEV_TEAM", - "value": clone.teamId + "value": clone.teamSlug }) env.push({ "name": "BOT", - "value": clone.botId + "value": clone.botSlug }) env.push({ "name": "TYPE", - "value": clone.kind - }) - env.push({ - "name": "PROCESS", - "value": clone.processName + "value": clone.botType }) env.push({ "name": "CLONE_ID", @@ -54,7 +80,11 @@ const createClone = async (clone, teamSlug, botSlug) => { }) logger.debug("createClone Trading Configuration.") - if(clone.kind === 'Trading'){ + if(clone.botType === 'Trading'){ + env.push({ + "name": "PROCESS", + "value": 'Trading-Process' + }) env.push({ "name": "RESUME_EXECUTION", "value": clone.resumeExecution.toString() @@ -74,7 +104,11 @@ const createClone = async (clone, teamSlug, botSlug) => { "value": clone.waitTime.toString() }) } - } else if(clone.kind === 'Indicator' || clone.kind === 'Extractor'){ + } else if(clone.botType === 'Indicator' || clone.botType === 'Extraction'){ + env.push({ + "name": "PROCESS", + "value": clone.processName + }) env.push({ "name": "MIN_YEAR", "value": isDefined(clone.startYear) ? clone.startYear.toString() : '' @@ -93,12 +127,13 @@ const createClone = async (clone, teamSlug, botSlug) => { }) } + logger.debug("Configuration: %j.", env) deploymentManifest.spec.template.spec.containers[0].env = env const create = await client.apis.batch.v1.namespaces('default').jobs.post( { body: deploymentManifest }) - logger.info("createClone %s on Kubernates success.", cloneName) + logger.info("createClone %s on Kubernates success.", clone.cloneName) } catch (err) { throw new KubernateError(err) } diff --git a/server/src/kubernetes/removeClone.js b/server/src/kubernetes/removeClone.js index 2ec404572..d6440626d 100644 --- a/server/src/kubernetes/removeClone.js +++ b/server/src/kubernetes/removeClone.js @@ -1,28 +1,26 @@ import logger from '../config/logger' import { KubernateError } from '../errors' import { Client, config } from 'kubernetes-client' -import { getJobNameFromClone } from '../config/utils' -const removeClone = async (clone, teamSlug, botSlug) => { +const removeClone = async (clone) => { try { - let cloneName = getJobNameFromClone(teamSlug, botSlug, clone.mode) - logger.info("removeClone %s", cloneName) + logger.info("removeClone %s", clone.cloneName) const client = new Client({config: config.fromKubeconfig(), version: '1.9'}) let query = { "qs":{ - "labelSelector": "job-name="+cloneName + "labelSelector": "job-name=" + clone.cloneName } } const removed = await client.apis.batch.v1.namespaces('default').jobs( - cloneName).delete() + clone.cloneName).delete() const pod = await client.api.v1.namespaces('default').pods.get(query) const podRemoved = await client.api.v1.namespaces('default') .pods(pod.body.items[0].metadata.name).delete() - logger.info("removeClone %s on kubernates succesful", cloneName) + logger.info("removeClone %s on kubernates succesful", clone.cloneName) } catch (err) { throw new KubernateError(err) } diff --git a/server/src/schema/queries/cloneDetails.js b/server/src/schema/cloneDetails.js similarity index 50% rename from server/src/schema/queries/cloneDetails.js rename to server/src/schema/cloneDetails.js index 06c3ed99c..447c34ecc 100644 --- a/server/src/schema/queries/cloneDetails.js +++ b/server/src/schema/cloneDetails.js @@ -1,19 +1,24 @@ -import logger from '../../config/logger' -import { getJobNameFromClone, getSelectedBot } from '../../config/utils' -import teamAvatar from '../../graphQLCalls/teamAvatar' +import logger from '../config/logger' +import { getJobNameFromClone, getSelectedBot } from '../config/utils' +import teamAvatar from '../graphQLCalls/teamAvatar' +// TODO Refactor this code once financial beings api is ready const cloneDetails = async(authorization, botsByUser, clone) => { logger.debug('Retrieving clone details.') - // TODO Refactor this code once financial beings api is ready + let selectedBot = getSelectedBot(botsByUser.data.data.teams_FbByTeamMember, clone.botId) - clone.cloneName = getJobNameFromClone(botsByUser.data.data.teams_FbByTeamMember.slug, selectedBot.slug, clone.mode) - clone.kind = selectedBot.kind + clone.teamId = botsByUser.data.data.teams_FbByTeamMember.id + clone.botId = selectedBot.id + clone.teamSlug = botsByUser.data.data.teams_FbByTeamMember.slug + clone.botSlug = selectedBot.slug + clone.cloneName = getJobNameFromClone(clone) clone.teamName = botsByUser.data.data.teams_FbByTeamMember.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 + logger.debug('Retrieving clone details complete.') return clone }; diff --git a/server/src/schema/mutations/AddCloneMutation.js b/server/src/schema/mutations/AddCloneMutation.js index 5aceb940b..92511bf3d 100644 --- a/server/src/schema/mutations/AddCloneMutation.js +++ b/server/src/schema/mutations/AddCloneMutation.js @@ -2,13 +2,14 @@ import { CloneInputType } from '../types/input'; 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 cloneDetails from '../cloneDetails' import { AuthentificationError, WrongArgumentsError, - OperationsError, - CustomError + OperationsError } from '../../errors' import logger from '../../config/logger' @@ -30,6 +31,10 @@ const resolve = async(parent, { clone }, context) => { throw new WrongArgumentsError('The mode selected is not valid.') } + if(!Object.values(BotTypesEnum).includes(clone.botType)){ + throw new WrongArgumentsError('The bot type selected is not valid.') + } + let existingClone = await Clone.find( { authId: context.userId, @@ -40,25 +45,23 @@ const resolve = async(parent, { clone }, context) => { logger.debug('addClone -> Checking existing clone %j', existingClone) if(existingClone.length > 0){ - throw new CustomError('You can only have one active clone by mode. Remove the' + throw new OperationsError('You can only have one active clone by mode. Remove the' + ' existing clone of type: ' + clone.mode + ' and try again.') } 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) - clone.teamId = botsByUser.data.data.teams_FbByTeamMember.id - clone.botId = selectedBot.id - clone.userLoggedIn = botsByUser.data.data.teams_FbByTeamMember.members[0].member.alias //TODO change by context info if possible clone.createDatetime = new Date().valueOf() / 1000|0 clone.active = true - logger.debug('addClone -> Creating a new clone. %j', clone) + logger.debug('addClone -> Creating a new clone.') let newClone = new Clone(clone) - newClone.id = newClone._id newClone.authId = context.userId - await createKubernetesClone(newClone, botsByUser.data.data.teams_FbByTeamMember.slug, selectedBot.slug ) + clone.id = newClone._id + await createKubernetesClone(clone) return new Promise((resolve, reject) => { newClone.save((err) => { diff --git a/server/src/schema/mutations/RemoveCloneMutation.js b/server/src/schema/mutations/RemoveCloneMutation.js index dcd51faae..8d1afaf5e 100644 --- a/server/src/schema/mutations/RemoveCloneMutation.js +++ b/server/src/schema/mutations/RemoveCloneMutation.js @@ -14,6 +14,7 @@ import logger from '../../config/logger' import removeKuberneteClone from '../../kubernetes/removeClone' import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember' import { isDefined, getSelectedBot } from '../../config/utils' +import cloneDetails from '../cloneDetails' const args = { id: { type: new GraphQLNonNull(GraphQLID) } @@ -33,15 +34,14 @@ const resolve = async (parent, { id }, context) => { }) if(!isDefined(clone)){ - throw new CustomError('You are not authorized to remove this clone.') + throw new OperationsError('You are not authorized to remove this clone.') } - // Authorization is also handled by the teams module let botsByUser = await teams_FbByTeamMember(context.authorization) - let selectedBot = getSelectedBot(botsByUser.data.data.teams_FbByTeamMember, clone.botId) + clone = await cloneDetails(context.authorization, botsByUser, clone) logger.debug('removeClone -> Removing Clone from Kubernates.') - await removeKuberneteClone(clone, botsByUser.data.data.teams_FbByTeamMember.slug, selectedBot.slug) + await removeKuberneteClone(clone) const query = { _id: id, diff --git a/server/src/schema/queries/HistoryClones.js b/server/src/schema/queries/HistoryClones.js index 5c20df2bb..467548e27 100644 --- a/server/src/schema/queries/HistoryClones.js +++ b/server/src/schema/queries/HistoryClones.js @@ -12,7 +12,7 @@ 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 cloneDetails from '../cloneDetails' const args = {} diff --git a/server/src/schema/queries/ListClones.js b/server/src/schema/queries/ListClones.js index c6cbe9500..c3cc91a67 100644 --- a/server/src/schema/queries/ListClones.js +++ b/server/src/schema/queries/ListClones.js @@ -13,7 +13,7 @@ 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' +import cloneDetails from '../cloneDetails' const args = {} diff --git a/server/src/schema/types/CloneType.js b/server/src/schema/types/CloneType.js index 6c8db2b18..5a653c35e 100644 --- a/server/src/schema/types/CloneType.js +++ b/server/src/schema/types/CloneType.js @@ -32,7 +32,7 @@ const CloneType = new GraphQLObjectType({ combinedProfitsB: {type: GraphQLFloat}, assetA: {type: GraphQLString}, assetB: {type: GraphQLString}, - kind: {type: GraphQLString}, + botType: {type: GraphQLString}, teamName: {type: GraphQLString}, botName: {type: GraphQLString}, botAvatar: {type: GraphQLString}, diff --git a/server/src/schema/types/input/Clone.js b/server/src/schema/types/input/Clone.js index ef59f0a87..9d20ec6ac 100644 --- a/server/src/schema/types/input/Clone.js +++ b/server/src/schema/types/input/Clone.js @@ -22,7 +22,7 @@ const Type = new GraphQLInputObjectType({ month: { type: GraphQLInt }, interval: { type: GraphQLInt }, processName: { type: GraphQLString }, - kind: { type: GraphQLString } + botType: { type: new GraphQLNonNull(GraphQLString) } }) });