Merge pull request #6 from AAVikings/master

New client and server version
This commit is contained in:
z@tOr authored and GitHub committed 2019-01-30 11:27:02 -03:00
commit d8c0634016
20 files changed
+132 -127

No files matched your search

+1 -1
View File
@@ -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": {
+1 -1
View File
@@ -24,7 +24,7 @@ export const clone = gql`
combinedProfitsB
assetA
assetB
kind
botType
teamName
botName
botAvatar
+11 -1
View File
@@ -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 })
};
+3 -3
View File
@@ -88,7 +88,7 @@ class ListClones extends Component {
<div className={classes.details}>
<div className={classes.column4}>
<Typography className={classes.cloneInfoBold}>Last Execution:</Typography>
{ (clone.kind === 'TRADER' && isDefined(clone.assetA) ) &&
{ (clone.botType === 'Trading' && isDefined(clone.assetA) ) &&
<React.Fragment>
<Typography className={classes.cloneInfoBold}>Buy Average:</Typography>
<Typography className={classes.cloneInfoBold}>Sell Average:</Typography>
@@ -100,7 +100,7 @@ class ListClones extends Component {
</div>
<div className={classes.column4}>
<Typography className={classes.cloneInfoNormal}>{isDefined(clone.summaryDate) ? toLocalTime(clone.summaryDate): 'waiting update...'}</Typography>
{ ((clone.kind === 'INDICATOR' || clone.kind === 'EXTRACTOR') && isDefined(clone.assetA) ) &&
{ ((clone.botType === 'Indicator' || clone.botType === 'Extraction') && isDefined(clone.assetA) ) &&
<React.Fragment>
<Typography className={classes.cloneInfoNormal}>{clone.buyAverage}</Typography>
<Typography className={classes.cloneInfoNormal}>{clone.sellAverage}</Typography>
@@ -187,7 +187,7 @@ class ListClones extends Component {
<DialogTitle id="logs-dialog-title">
Last clone logs
</DialogTitle>
<DialogContent className={classes.logsDialog}>
<DialogContent>
<Typography className={classes.typography} variant='subtitle1' align='justify'>
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.
+6 -7
View File
@@ -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'
+1 -1
View File
@@ -14,7 +14,7 @@
"containers": [
{
"name": "aadev",
"image": "benme/aadev:v3.3",
"image": "benme/aadev:v3.4",
"command": ["node", "Run.js"],
"env": [
]
-45
View File
@@ -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
+2 -2
View File
@@ -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){
+11
View File
@@ -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
+7 -4
View File
@@ -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
-14
View File
@@ -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 = ''
}
}
+52 -17
View File
@@ -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)
}
+5 -7
View File
@@ -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)
}
@@ -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
};
@@ -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) => {
@@ -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,
+1 -1
View File
@@ -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 = {}
+1 -1
View File
@@ -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 = {}
+1 -1
View File
@@ -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},
+1 -1
View File
@@ -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) }
})
});