@@ -109,7 +109,7 @@ class BrowseClones extends Component {
size='big'
title='No Bot Clones to display'
text='Please Login to access your Bot Clones.'
- backgroundUrl='https://advancedalgos.net/img/photos/ecosystem.jpg'
+ backgroundUrl='https://superalgos.org/img/photos/ecosystem.jpg'
/>
)
} else {
@@ -119,7 +119,7 @@ class BrowseClones extends Component {
size='big'
title='No Bot Clones to display'
text="You don't have any Bot Clone yet. After you create a new Clone, it will be listed here."
- backgroundUrl='https://advancedalgos.net/img/photos/ecosystem.jpg'
+ backgroundUrl='https://superalgos.org/img/photos/ecosystem.jpg'
/>
)
}
diff --git a/client/src/views/Clone/List/styles.js b/client/src/views/Clone/List/styles.js
index 652be82cd..bd3f9b06e 100644
--- a/client/src/views/Clone/List/styles.js
+++ b/client/src/views/Clone/List/styles.js
@@ -46,7 +46,7 @@ const styles = theme => ({
},
helper: {
borderLeft: `2px solid ${theme.palette.divider}`,
- padding: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px`,
+ padding: `0px ${theme.spacing.unit * 2}px`,
},
logsDialog:{
width:'1750px',
@@ -56,26 +56,27 @@ const styles = theme => ({
details:{
display:'flex'
},
- column: {
- flexBasis: '15%',
- maxWidth: '15%'
- },
column2: {
- flexBasis: '42.5%',
- maxWidth: '42.5%'
+ flexBasis: '50%',
+ maxWidth: '50%'
},
column3: {
- flexBasis: '42.5%',
- maxWidth: '42.5%'
+ flexBasis: '33.33%',
+ maxWidth: '33.33%'
},
- cloneInfo1:{
+ cloneInfoTitle:{
fontWeight: '600',
- paddingLeft: '16px',
- paddingTop: '16px'
+ flexBasis: '100%',
+ maxWidth: '100%'
},
- cloneInfo2:{
- paddingLeft: '16px',
- paddingTop: '16px'
+ cloneInfoBold:{
+ fontWeight: '600',
+ paddingLeft: '10px',
+ paddingTop: '10px'
+ },
+ cloneInfoNormal:{
+ paddingLeft: '10px',
+ paddingTop: '10px'
},
})
diff --git a/client/src/views/History/AuditLogList.js b/client/src/views/History/AuditLogList.js
deleted file mode 100644
index e9d18f38c..000000000
--- a/client/src/views/History/AuditLogList.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import React, { Component } from 'react'
-import {graphql, compose} from 'react-apollo'
-import { getAuditLog } from '../../../queries'
-
-import { Table, TableBody, TableCell, TableHead, TableRow, Typography } from '@material-ui/core';
-
-import { withStyles } from '@material-ui/core/styles'
-import { toLocalTime } from '../../../utils'
-
-const styles = theme => ({
- root: {
- flexGrow: 1,
- padding: 20,
- margin: 10
- }
-});
-
-
-class AuditLogList extends Component {
-
- constructor(props){
- super(props)
- const key = this.props.currentKey
- this.state = {
- id: key.id,
- key: key.key,
- date:'',
- action: ''
- }
- }
-
- render() {
- const { classes } = this.props
- var data = this.props.data
- if(data.loading){
- return
- )
- }
- }
-
-}
-
-export default compose(
- graphql(getAuditLog, { // What follows is the way to pass a parameter to a query.
- options: (props) => {
- return {
- variables: {
- key: props.currentKey.id
- }
- }
- }
- }),
- withStyles(styles)
-)(AuditLogList)
diff --git a/client/src/views/Home.js b/client/src/views/Home.js
index a91fd49a4..2e2de92b5 100644
--- a/client/src/views/Home.js
+++ b/client/src/views/Home.js
@@ -12,7 +12,7 @@ class Home extends Component {
size='big'
title='Operations Module'
text='Responsible for manage bot clones.'
- backgroundUrl='https://advancedalgos.net/img/photos/ecosystem.jpg'
+ backgroundUrl='https://superalgos.org/img/photos/ecosystem.jpg'
/>
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 48e341a09..000000000
--- a/package-lock.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "lockfileVersion": 1
-}
diff --git a/server/CODE_OF_CONDUCT.md b/server/CODE_OF_CONDUCT.md
index c71b685b4..05b430885 100644
--- a/server/CODE_OF_CONDUCT.md
+++ b/server/CODE_OF_CONDUCT.md
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at feedback at advancedalgos dot org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at feedback at superalgos dot org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
diff --git a/server/CONTRIBUTING.md b/server/CONTRIBUTING.md
index 311653120..c67e8d115 100644
--- a/server/CONTRIBUTING.md
+++ b/server/CONTRIBUTING.md
@@ -2,44 +2,44 @@
We are thrilled you are interested in contributing to this repository. Your help may be essential to the project's success and we want you in!
-Advanced Algos LTD is a startup, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
+Superalgos is a collective project, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
-Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regards.
+Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regard.
In any case, contributors agree to provide an express grant of patent rights for their contributions. Submitting Pull Requests to this repository represents an express agreement with the terms of our Balanced Contributor Intellectual Property Agreement. This agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md).
# Balanced Contributor Intellectual Property Agreement 1.0.1
-This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Advanced Algos LTD ("Company").
+This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Superalgos.org ("Project").
-**What is this?** This is the Company's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Company.
+**What is this?** This is the Project's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Project.
-**Why is this?** The Company needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Company having the legal rights to the products and services it is providing so that the Company can continue operating and doing business.
+**Why is this?** The Project needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Project having the legal rights to the products and services it is providing so that the Project can continue operating and doing business.
-The Company also believes that it's important to be clear on what it doesn't own. The Company doesn't want you looking over your shoulder every time you work on something personal or worrying that the Company will someday seize your open source non-lethal mousetrap simulation software. In other words, the Company isn't interested in appropriating your personal projects.
+The Project also believes that it's important to be clear on what it doesn't own. The Project doesn't want you looking over your shoulder every time you work on something personal or worrying that the Project will someday seize your open source non-lethal mousetrap simulation software. In other words, the Project isn't interested in appropriating your personal projects.
-**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Company cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Company's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
+**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Project cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Project's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
Cool? Then, by submitting your first Pull Request, and as a condition of your contribution, you agree to the following:
-1. **What the Company owns.** The Company owns any IP ("Company IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Company product or service at the time you developed, invented, or created it, (ii) developed for use by the Company, or (iii) developed or promoted with existing Company IP or with the Company's endorsement. You hereby grant and assign, and will grant and assign, to the Company all rights and interests in all Company IP.
+1. **What the Project owns.** The Project owns any IP ("Project IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Project product or service at the time you developed, invented, or created it, (ii) developed for use by the Project, or (iii) developed or promoted with existing Project IP or with the Project's endorsement. You hereby grant and assign, and will grant and assign, to the Project all rights and interests in all Project IP.
- "Company IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
+ "Project IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
-2. **What the Company doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Company doesn't own it. In other words, the Company doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
+2. **What the Project doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Project doesn't own it. In other words, the Project doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
-3. **Contribution of your IP to Company projects.** If you include your own IP – such as IP you created prior to working for the Company – into a Company product or service, it's still yours, of course, but you grant the Company a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Company would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
+3. **Contribution of your IP to Project projects.** If you include your own IP – such as IP you created prior to working for the Project – into a Project product or service, it's still yours, of course, but you grant the Project a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Project would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
-4. **Your contributions to non-Company projects.** The Company recognizes that you may be engaged in work that requires you to submit IP to entities other than the Company, such as open source projects used by the Company. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Company and so should be run through Legal first.
+4. **Your contributions to non-Project projects.** The Project recognizes that you may be engaged in work that requires you to submit IP to entities other than the Project, such as open source projects used by the Project. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Project and so should be run through Legal first.
-5. **Voluntary Works** All Company IP that you are involved with or create as part of your work for the Company is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Company to learn more.
+5. **Voluntary Works** All Project IP that you are involved with or create as part of your work for the Project is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Project to learn more.
6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future.
-7. **IP protection.** The Company might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Company IP or IP under License (together, "All IP"), and, if the Company asks, to provide those records to the Company. You agree to help the Company secure and defend its rights in All IP. For your help the Company will compensate you at a reasonable rate. If the Company is unable to secure your help, you authorize the Company to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
+7. **IP protection.** The Project might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Project IP or IP under License (together, "All IP"), and, if the Project asks, to provide those records to the Project. You agree to help the Project secure and defend its rights in All IP. For your help the Project will compensate you at a reasonable rate. If the Project is unable to secure your help, you authorize the Project to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
-8. **Confidentiality.** As an contributor or contractor of the Company, you will have access to sensitive confidential information that is important to the Company's business. You are responsible for keeping this information confidential, including after you end your work for the Company.
+8. **Confidentiality.** As an contributor or contractor of the Project, you will have access to sensitive confidential information that is important to the Project's business. You are responsible for keeping this information confidential, including after you end your work for the Project.
- "Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Company because it is not publicly known.
+ "Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Project because it is not publicly known.
- The Company's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Company's Confidential Information for the purpose of performing your contribution and for the benefit of the Company. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
\ No newline at end of file
+ The Project's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Project's Confidential Information for the purpose of performing your contribution and for the benefit of the Project. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
\ No newline at end of file
diff --git a/server/LICENSE b/server/LICENSE
index c049df9cb..2fe09f449 100644
--- a/server/LICENSE
+++ b/server/LICENSE
@@ -1,23 +1,22 @@
COPYRIGHT NOTICE
-© Copyright 2018 Advanced Algos LTD.
+© Copyright 2018 Superalgos.org
LICENSE NOTICE
-Advanced Algos LTD. is a startup, currently at a very early stage. Because certain aspects of
-the business are still not clearly defined, we have chosen to --temporarily-- grant a
-restrictive license for the use of this repository, until those business aspects are clarified
-and a proper open source license can be granted.
+Superalgos is a collaborative project currently at a very early stage. Because certain aspects of
+the project are still not clearly defined, we have chosen to --temporarily-- grant a restrictive
+license for the use of this repository, until those aspects are clarified and a proper open source
+license can be granted.
-Advanced Algos LTD. is publishing the code of this software for several reasons. However,
-it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company
-in the crypto space and this module is still proprietary software.
+Superalgos.org is publishing the code of this software for several reasons. However, it shall not be considered free software yet. Superalgos is a collaborative project in the crypto space and this
+software is still proprietary software.
-The reasons for opening up the code of this module at this stage are the following:
+The reasons for opening up the code of this software at this stage are the following:
- * We believe that transparency is paramount to achieving our goals, so we want everyone
- to be able to understand what this module does and how. Opening up the code of this module
+ * We believe that transparency is paramount to achieving our goals, so we want everyone to
+ be able to understand what this software does and how. Opening up the code of this software
allows anyone to take a close look.
* We welcome contributions from the community. Contributions are regarded as voluntary work.
@@ -27,16 +26,14 @@ The reasons for opening up the code of this module at this stage are the followi
LICENSE WORDING
-Advanced Algos LTD. grants a limited license for the following uses:
+Superalgos.org grants a limited license for the following uses:
1. CODE REVIEW AND AUDITS. You are granted permission to clone this repository to analyze,
review or audit the code.
-2. COLLABORATING WITH ADVANCED ALGOS LTD. You are granted permission to fork this repository
+2. COLLABORATING WITH Superalgos.org. You are granted permission to fork this repository
with the sole purpose of correcting bugs and developing functionality in agreement with
- Advanced Algos LTD. By contributing to this repository you explicitely agree to the terms
+ Superalgos.org. By contributing to this repository you explicitely agree to the terms
detailed in CONTRIBUTING.MD.
-PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
-
-
+PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
\ No newline at end of file
diff --git a/server/package.json b/server/package.json
index cd56dd112..d4b618543 100644
--- a/server/package.json
+++ b/server/package.json
@@ -1,7 +1,7 @@
{
"name": "operations-server",
"version": "1.0.0",
- "description": "Advanced Algos Operations backend API",
+ "description": "Superalgos Operations backend API",
"main": "src/index.js",
"scripts": {
"start": "nodemon --exec babel-node --presets env,stage-2 src/index.js",
diff --git a/server/src/config/envConfig.js b/server/src/config/envConfig.js
index 62d7d6073..7bd3cf254 100644
--- a/server/src/config/envConfig.js
+++ b/server/src/config/envConfig.js
@@ -10,7 +10,7 @@ env.push({
// FIXME
env.push({
"name": "GATEWAY_ENDPOINT",
- "value": "https://platform-api-dev.advancedalgos.net/graphql"
+ "value": "https://platform-api-dev.superalgos.org/graphql"
//"value": process.env.GATEWAY_ENDPOINT
})
diff --git a/server/src/config/utils.js b/server/src/config/utils.js
index 11f9909fa..f27cd7b72 100644
--- a/server/src/config/utils.js
+++ b/server/src/config/utils.js
@@ -7,3 +7,7 @@ export function toPlatformDatetime (epoch) {
export function isDefined (d) {
return d !== null && typeof d !== 'undefined'
}
+
+export function getJobNameFromClone (clone) {
+ return clone.teamId + '-' + clone.botId + '-' + clone.mode.toLowerCase()
+}
diff --git a/server/src/kubernetes/createClone.js b/server/src/kubernetes/createClone.js
index ff89142a6..9632ac6b8 100644
--- a/server/src/kubernetes/createClone.js
+++ b/server/src/kubernetes/createClone.js
@@ -1,5 +1,5 @@
import logger from '../config/logger'
-import { toPlatformDatetime } from '../config/utils'
+import { toPlatformDatetime, getJobNameFromClone } from '../config/utils'
import envConfig from '../config/envConfig'
import { KubernateError } from '../errors'
import { Client, config } from 'kubernetes-client'
@@ -7,8 +7,9 @@ import { Client, config } from 'kubernetes-client'
import deploymentManifest from '../config/clone-deployment.json'
import { BACKTEST} from '../enums/CloneMode';
-const createClone = async (clone, cloneName) => {
+const createClone = async (clone) => {
try {
+ let cloneName = getJobNameFromClone(clone)
logger.info("createClone %j", cloneName)
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
@@ -52,6 +53,11 @@ const createClone = async (clone, cloneName) => {
})
logger.debug("createClone Configuration for the execution.")
+ env.push({
+ "name": "CLONE_ID",
+ "value": clone.id.toString()
+ })
+
env.push({
"name": "START_MODE",
"value": clone.mode.toLowerCase()
diff --git a/server/src/models/clone.js b/server/src/models/clone.js
index f1b0bc3be..45532d5b9 100644
--- a/server/src/models/clone.js
+++ b/server/src/models/clone.js
@@ -39,7 +39,16 @@ const cloneSchema = new Schema({
},
stateDatetime: Number,
createDatetime: Number,
- runAsTeam: Boolean
+ runAsTeam: Boolean,
+ active: Boolean,
+ summaryDate: Number,
+ buyAverage: Number,
+ sellAverage: Number,
+ marketRate: Number,
+ combinedProfitsA: Number,
+ combinedProfitsB: Number,
+ assetA: String,
+ assetB: String
})
const Clone = mongoose.model('Clone', cloneSchema)
diff --git a/server/src/platform/downloadBlob.js b/server/src/platform/downloadBlob.js
new file mode 100644
index 000000000..a7044399b
--- /dev/null
+++ b/server/src/platform/downloadBlob.js
@@ -0,0 +1,17 @@
+import logger from '../config/logger'
+import { toPlatformDatetime } from '../config/utils'
+
+const downloadBlob = async (containerName, blobName) => {
+ logger.debug('downloadBlob -> About to get a file: %s%s', containerName, blobName)
+ return new Promise((resolve, reject) => {
+ blobService.getBlobToText(containerName, blobName, (err, data) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(data);
+ }
+ });
+ });
+};
+
+export default downloadBlob
diff --git a/server/src/platform/fileStorageReader.js b/server/src/platform/fileStorageReader.js
new file mode 100644
index 000000000..c5e95c3c8
--- /dev/null
+++ b/server/src/platform/fileStorageReader.js
@@ -0,0 +1,98 @@
+import logger from '../config/logger'
+import { toPlatformDatetime } 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 platformExecutionContext = async (clone, cloneName) => {
+ try {
+ logger.info("createClone %j", cloneName)
+ const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
+
+ // Make changes to base deployment config
+ deploymentManifest.metadata.name = cloneName
+
+ logger.debug("createClone Set basic environment configuration.")
+ let env = envConfig
+
+ logger.debug("createClone Configuration for team, bot and process.")
+ env.push({
+ "name": "DEV_TEAM",
+ "value": clone.teamId
+ })
+
+ env.push({
+ "name": "BOT",
+ "value": clone.botId
+ })
+
+ // TODO allow other types
+ env.push({
+ "name": "PROCESS",
+ "value": "Trading-Process"
+ })
+
+ // TODO allow other types
+ env.push({
+ "name": "TYPE",
+ "value": "Trading"
+ })
+
+ env.push({
+ "name": "RUN_AS_TEAM",
+ "value": clone.runAsTeam.toString()
+ })
+
+ env.push({
+ "name": "USER_LOGGED_IN",
+ "value": clone.userLoggedIn
+ })
+
+ logger.debug("createClone Configuration for the execution.")
+ env.push({
+ "name": "START_MODE",
+ "value": clone.mode.toLowerCase()
+ })
+
+ env.push({
+ "name": "RESUME_EXECUTION",
+ "value": clone.resumeExecution.toString()
+ })
+
+ if(clone.mode === BACKTEST){
+ env.push({
+ "name": "BEGIN_DATE_TIME",
+ "value": toPlatformDatetime(clone.beginDatetime)
+ })
+
+ env.push({
+ "name": "END_DATE_TIME",
+ "value": toPlatformDatetime(clone.endDatetime)
+ })
+
+ env.push({
+ "name": "WAIT_TIME",
+ "value": clone.waitTime.toString()
+ })
+ }
+
+ deploymentManifest.spec.template.spec.containers[0].env = env
+
+ // Create a new Deployment
+ const create = await client.apis.batch.v1.namespaces('default').jobs.post(
+ { body: deploymentManifest })
+ logger.debug("Create: %j ", create)
+
+ // Fetch the Deployment we just created
+ const deployment = await client.apis.batch.v1.namespaces('default').jobs(
+ deploymentManifest.metadata.name).get()
+ logger.debug("Job: %j ", deployment)
+
+ logger.info("createClone %j succesfull", cloneName)
+ } catch (err) {
+ throw new KubernateError(err)
+ }
+}
+export default platformExecutionContext
diff --git a/server/src/platform/platformExecutionContext.js b/server/src/platform/platformExecutionContext.js
new file mode 100644
index 000000000..c5e95c3c8
--- /dev/null
+++ b/server/src/platform/platformExecutionContext.js
@@ -0,0 +1,98 @@
+import logger from '../config/logger'
+import { toPlatformDatetime } 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 platformExecutionContext = async (clone, cloneName) => {
+ try {
+ logger.info("createClone %j", cloneName)
+ const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
+
+ // Make changes to base deployment config
+ deploymentManifest.metadata.name = cloneName
+
+ logger.debug("createClone Set basic environment configuration.")
+ let env = envConfig
+
+ logger.debug("createClone Configuration for team, bot and process.")
+ env.push({
+ "name": "DEV_TEAM",
+ "value": clone.teamId
+ })
+
+ env.push({
+ "name": "BOT",
+ "value": clone.botId
+ })
+
+ // TODO allow other types
+ env.push({
+ "name": "PROCESS",
+ "value": "Trading-Process"
+ })
+
+ // TODO allow other types
+ env.push({
+ "name": "TYPE",
+ "value": "Trading"
+ })
+
+ env.push({
+ "name": "RUN_AS_TEAM",
+ "value": clone.runAsTeam.toString()
+ })
+
+ env.push({
+ "name": "USER_LOGGED_IN",
+ "value": clone.userLoggedIn
+ })
+
+ logger.debug("createClone Configuration for the execution.")
+ env.push({
+ "name": "START_MODE",
+ "value": clone.mode.toLowerCase()
+ })
+
+ env.push({
+ "name": "RESUME_EXECUTION",
+ "value": clone.resumeExecution.toString()
+ })
+
+ if(clone.mode === BACKTEST){
+ env.push({
+ "name": "BEGIN_DATE_TIME",
+ "value": toPlatformDatetime(clone.beginDatetime)
+ })
+
+ env.push({
+ "name": "END_DATE_TIME",
+ "value": toPlatformDatetime(clone.endDatetime)
+ })
+
+ env.push({
+ "name": "WAIT_TIME",
+ "value": clone.waitTime.toString()
+ })
+ }
+
+ deploymentManifest.spec.template.spec.containers[0].env = env
+
+ // Create a new Deployment
+ const create = await client.apis.batch.v1.namespaces('default').jobs.post(
+ { body: deploymentManifest })
+ logger.debug("Create: %j ", create)
+
+ // Fetch the Deployment we just created
+ const deployment = await client.apis.batch.v1.namespaces('default').jobs(
+ deploymentManifest.metadata.name).get()
+ logger.debug("Job: %j ", deployment)
+
+ logger.info("createClone %j succesfull", cloneName)
+ } catch (err) {
+ throw new KubernateError(err)
+ }
+}
+export default platformExecutionContext
diff --git a/server/src/schema/mutations/AddCloneMutation.js b/server/src/schema/mutations/AddCloneMutation.js
index 9195fb0d0..a0de06946 100644
--- a/server/src/schema/mutations/AddCloneMutation.js
+++ b/server/src/schema/mutations/AddCloneMutation.js
@@ -40,7 +40,8 @@ const resolve = async(parent, { clone }, context) => {
let existingClone = await Clone.find(
{
authId: context.userId,
- mode: clone.mode
+ mode: clone.mode,
+ active: true
})
logger.debug('addClone -> Checking existing clone %j', existingClone)
@@ -49,8 +50,6 @@ const resolve = async(parent, { clone }, context) => {
+ ' existing clone of type: ' + clone.mode + ' and try again.')
}
- logger.debug('addClone -> Creating a new clone. %j', clone)
-
try{
const userTeam = await axios({
url: process.env.GATEWAY_ENDPOINT,
@@ -86,13 +85,13 @@ const resolve = async(parent, { clone }, context) => {
clone.botId = userTeam.data.data.teams_FbByTeamMember.fb[0].slug
clone.userLoggedIn = userTeam.data.data.teams_FbByTeamMember.members[0].member.alias
clone.createDatetime = new Date().valueOf() / 1000|0
+ clone.active = true
+ logger.debug('addClone -> Creating a new clone. %j', clone)
let newClone = new Clone(clone)
-
- await createKubernetesClone(clone, newClone._id)
-
newClone.id = newClone._id
newClone.authId = context.userId
+ await createKubernetesClone(newClone)
return new Promise((resolve, reject) => {
newClone.save((err) => {
@@ -102,7 +101,6 @@ const resolve = async(parent, { clone }, context) => {
}
else {
//TODO transaction on database
- // saveAuditLog(newClone.id, 'addKey', context)
logger.debug('addClone -> Save clone sucessful.')
resolve(newClone)
}
diff --git a/server/src/schema/mutations/RemoveCloneMutation.js b/server/src/schema/mutations/RemoveCloneMutation.js
index a9e741f63..88152cf54 100644
--- a/server/src/schema/mutations/RemoveCloneMutation.js
+++ b/server/src/schema/mutations/RemoveCloneMutation.js
@@ -12,10 +12,9 @@ import {
import { Clone } from '../../models'
import logger from '../../config/logger'
import removeKuberneteClone from '../../kubernetes/removeClone'
-//import saveAuditLog from './AddAuditLog'
const args = {
- id: {type: new GraphQLNonNull(GraphQLID)}
+ id: { type: new GraphQLNonNull(GraphQLID) }
}
const resolve = async (parent, { id }, context) => {
@@ -27,18 +26,19 @@ const resolve = async (parent, { id }, context) => {
await removeKuberneteClone(id)
- var query = {
+ const query = {
_id: id,
authId: context.userId
}
+ const options = { new: true }
+ const update = { active: false }
- //saveAuditLog(id, 'removeClone', context)
logger.debug('removeClone -> Removing Clone from DB.')
- Clone.deleteOne(query, function (err) {
+ Clone.findOneAndUpdate(query, update, options, (err, doc) => {
if (err){
throw new OperationsError(err)
}else{
- return 'Clone Removed'
+ resolve('Clone Removed.')
}
})
}
diff --git a/server/src/schema/mutations/UpdateExecutionSummary.js b/server/src/schema/mutations/UpdateExecutionSummary.js
new file mode 100644
index 000000000..b666b455c
--- /dev/null
+++ b/server/src/schema/mutations/UpdateExecutionSummary.js
@@ -0,0 +1,71 @@
+import {
+ GraphQLNonNull,
+ GraphQLID,
+ GraphQLString,
+ GraphQLInt,
+ GraphQLFloat
+} from 'graphql'
+
+import {
+
+ OperationsError
+} from '../../errors'
+
+import { Clone } from '../../models'
+import logger from '../../config/logger'
+
+const args = {
+ id: { type: new GraphQLNonNull(GraphQLID) },
+ summaryDate: { type: GraphQLInt },
+ buyAverage: { type: GraphQLFloat },
+ sellAverage: { type: GraphQLFloat },
+ marketRate: { type: GraphQLFloat },
+ combinedProfitsA: { type: GraphQLFloat },
+ combinedProfitsB: { type: GraphQLFloat },
+ assetA: { type: GraphQLString },
+ assetB: { type: GraphQLString }
+}
+
+const resolve = async (parent,
+ { id, summaryDate, buyAverage, sellAverage, marketRate, combinedProfitsA,
+ combinedProfitsB, assetA, assetB
+ }) => {
+ 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
+ }
+
+ let sucessful
+ await Clone.findOneAndUpdate(query, update, options, (err, doc) => {
+ if (err){
+ throw new OperationsError(err)
+ } else {
+ logger.debug('Execution summary updated')
+ sucessful = true
+ }
+ })
+
+ if(sucessful){
+ return 'Execution summary updated.'
+ }
+}
+
+const mutation = {
+ updateExecutionSummary: {
+ type: GraphQLString,
+ args,
+ resolve
+ }
+}
+
+export default mutation
diff --git a/server/src/schema/mutations/index.js b/server/src/schema/mutations/index.js
index 44bae56f0..b5aa00205 100644
--- a/server/src/schema/mutations/index.js
+++ b/server/src/schema/mutations/index.js
@@ -4,12 +4,14 @@ import {
import AddCloneMutation from './AddCloneMutation'
import RemoveCloneMutation from './RemoveCloneMutation'
+import UpdateExecutionSummary from './UpdateExecutionSummary'
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: Object.assign(
AddCloneMutation,
- RemoveCloneMutation
+ RemoveCloneMutation,
+ UpdateExecutionSummary
)
})
diff --git a/server/src/schema/queries/AuditLogsQuery.js b/server/src/schema/queries/AuditLogsQuery.js
deleted file mode 100644
index 2ff8cfd85..000000000
--- a/server/src/schema/queries/AuditLogsQuery.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import {
- GraphQLList,
- GraphQLString,
- GraphQLNonNull
-} from 'graphql'
-
-import {
- AuthentificationError
-} from '../../errors'
-
-import { AuditLogType } from '../types'
-import { AuditLog } from '../../models'
-import logger from '../../config/logger'
-
-const args = {
- key: { type: new GraphQLNonNull(GraphQLString) }
-}
-
-const resolve = (parent, { key }, context) => {
- logger.debug('auditLogs -> Entering Fuction.')
-
- if (!context.userId) {
- throw new AuthentificationError()
- }
-
- return AuditLog.find({
- authId: context.userId,
- keyId: key
- }).sort({date: -1}).limit(20)
-}
-
-const query = {
- auditLogs: {
- type: new GraphQLList(AuditLogType),
- args,
- resolve
- }
-}
-
-export default query
diff --git a/server/src/schema/queries/ExchangesQuery.js b/server/src/schema/queries/ExchangesQuery.js
deleted file mode 100644
index dc7e37355..000000000
--- a/server/src/schema/queries/ExchangesQuery.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import {
- GraphQLList
-} from 'graphql'
-import { ExchangeType } from '../types'
-import { Exchange } from '../../models'
-import logger from '../../config/logger'
-
-const args = {}
-
-const resolve = (parent, args, context) => {
- logger.debug('exchanges -> Entering Fuction.')
- return Exchange
-}
-
-const query = {
- exchanges: {
- type: new GraphQLList(ExchangeType),
- args,
- resolve
- }
-}
-
-export default query
diff --git a/server/src/schema/queries/HistoryClones.js b/server/src/schema/queries/HistoryClones.js
new file mode 100644
index 000000000..e413997ec
--- /dev/null
+++ b/server/src/schema/queries/HistoryClones.js
@@ -0,0 +1,40 @@
+import {
+ GraphQLList
+} from 'graphql'
+
+import {
+ AuthentificationError
+} from '../../errors'
+
+import { CloneType } from '../types'
+import { Clone } from '../../models'
+import logger from '../../config/logger'
+import getKuberneteClonePodStatus from '../../kubernetes/getClonePodStatus'
+import getKuberneteClonePodLogs from '../../kubernetes/getClonePodLogs'
+
+const args = {}
+
+const resolve = async(parent, args, context) => {
+ logger.debug('History Clones -> Entering Fuction.')
+
+ if (!context.userId) {
+ throw new AuthentificationError()
+ }
+
+ let clones = await Clone.find({
+ authId: context.userId,
+ active: false
+ })
+
+ return clones
+}
+
+const query = {
+ historyClones: {
+ type: new GraphQLList(CloneType),
+ args,
+ resolve
+ }
+}
+
+export default query
diff --git a/server/src/schema/queries/ListClones.js b/server/src/schema/queries/ListClones.js
index 398b4f85d..f1fb34e25 100644
--- a/server/src/schema/queries/ListClones.js
+++ b/server/src/schema/queries/ListClones.js
@@ -9,8 +9,9 @@ import {
import { CloneType } from '../types'
import { Clone } from '../../models'
import logger from '../../config/logger'
-import getKuberneteClonePodStatus from '../../kubernetes/getClonePodStatus'
-import getKuberneteClonePodLogs from '../../kubernetes/getClonePodLogs'
+import getCloneStatus from '../../kubernetes/getClonePodStatus'
+import getCloneLogs from '../../kubernetes/getClonePodLogs'
+import { getJobNameFromClone } from '../../config/utils'
const args = {}
@@ -21,11 +22,16 @@ const resolve = async(parent, args, context) => {
throw new AuthentificationError()
}
- let clones = await Clone.find({authId: context.userId})
+ let clones = await Clone.find({
+ authId: context.userId,
+ active: true
+ })
for (var i = 0; i < clones.length; i++) {
- clones[i].state = await getKuberneteClonePodStatus(clones[i]._id)
- clones[i].lastLogs = await getKuberneteClonePodLogs(clones[i]._id)
+ logger.debug('List Clones -> clone: %j', clones[i])
+ let cloneName = getJobNameFromClone(clones[i])
+ clones[i].state = await getCloneStatus(cloneName)
+ clones[i].lastLogs = await getCloneLogs(cloneName)
}
return clones
}
diff --git a/server/src/schema/queries/index.js b/server/src/schema/queries/index.js
index dac2c4145..11d48edc5 100644
--- a/server/src/schema/queries/index.js
+++ b/server/src/schema/queries/index.js
@@ -3,11 +3,13 @@ import {
} from 'graphql'
import ListClones from './ListClones'
+import HistoryClones from './HistoryClones'
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: Object.assign(
- ListClones
+ ListClones,
+ HistoryClones
)
})
diff --git a/server/src/schema/types/CloneType.js b/server/src/schema/types/CloneType.js
index 44082e9be..d6e679c66 100644
--- a/server/src/schema/types/CloneType.js
+++ b/server/src/schema/types/CloneType.js
@@ -3,7 +3,8 @@ import {
GraphQLString,
GraphQLInt,
GraphQLBoolean,
- GraphQLID
+ GraphQLID,
+ GraphQLFloat
} from 'graphql'
const CloneType = new GraphQLObjectType({
@@ -22,7 +23,15 @@ const CloneType = new GraphQLObjectType({
stateDatetime: {type: GraphQLInt},
createDatetime: {type: GraphQLInt},
lastLogs: {type: GraphQLString},
- runAsTeam: {type: GraphQLBoolean}
+ runAsTeam: {type: GraphQLBoolean},
+ summaryDate: {type: GraphQLInt},
+ buyAverage: {type: GraphQLFloat},
+ sellAverage: {type: GraphQLFloat},
+ marketRate: {type: GraphQLFloat},
+ combinedProfitsA: {type: GraphQLFloat},
+ combinedProfitsB: {type: GraphQLFloat},
+ assetA: {type: GraphQLString},
+ assetB: {type: GraphQLString}
})
})
export default CloneType
diff --git a/server/src/schema/types/input/index.js b/server/src/schema/types/input/index.js
index 13c71b313..e51b0ca74 100644
--- a/server/src/schema/types/input/index.js
+++ b/server/src/schema/types/input/index.js
@@ -1,4 +1,4 @@
-import CloneInputType from './Clone';
+import CloneInputType from './Clone'
export {
CloneInputType
From a8ac7385d6e198cab21f404aaefc1b45fbbb1e0f Mon Sep 17 00:00:00 2001
From: "mati108@gmail.com" <34651529+matbenitez@users.noreply.github.com>
Date: Mon, 28 Jan 2019 10:56:00 -0300
Subject: [PATCH 2/4] Adding support for Indicators and Extractors
---
client/package-lock.json | 12 +-
client/package.json | 5 +-
client/src/GraphQL/Calls/teams.js | 21 +-
client/src/GraphQL/models.js | 7 +-
client/src/utils/index.js | 9 +
client/src/views/Clone/Add/index.js | 406 ++++++++++++------
client/src/views/Clone/History/index.js | 2 +-
server/src/enums/CloneMode.js | 18 +-
.../teams_FbByTeamMember.js} | 0
server/src/platform/fileStorageReader.js | 98 -----
.../src/platform/platformExecutionContext.js | 98 -----
.../src/schema/mutations/AddCloneMutation.js | 5 +-
12 files changed, 325 insertions(+), 356 deletions(-)
rename server/src/{platform/downloadBlob.js => graphQLCalls/teams_FbByTeamMember.js} (100%)
delete mode 100644 server/src/platform/fileStorageReader.js
delete mode 100644 server/src/platform/platformExecutionContext.js
diff --git a/client/package-lock.json b/client/package-lock.json
index 9a3c14063..2f4b4a7ba 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1,6 +1,6 @@
{
- "name": "@advancedalgos/operations-client",
- "version": "0.0.1",
+ "name": "@superalgos/operations-client",
+ "version": "0.0.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -689,6 +689,14 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
+ "react-number-format": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-4.0.6.tgz",
+ "integrity": "sha512-WfBlqYRD0fGZpQxRy1apxsPeaqtxhzo7CnUVFKBoGDsQbc89+DQNhjG/D3IxgGYM47oKB+W/Su9ol/vVS6Wc2Q==",
+ "requires": {
+ "prop-types": "15.6.2"
+ }
+ },
"react-router": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
diff --git a/client/package.json b/client/package.json
index 4fcd2d33b..81d6b3829 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
- "name": "@advancedalgos/operations-client",
- "version": "0.0.1",
+ "name": "@superalgos/operations-client",
+ "version": "0.0.2",
"description": "Superalgos Operations client-side web app",
"main": "src/index.js",
"dependencies": {
@@ -15,6 +15,7 @@
"react": "^16.6.1",
"react-apollo": "^2.2.4",
"react-dom": "^16.6.1",
+ "react-number-format": "^4.0.6",
"react-router-dom": "^4.3.1"
},
"devDependencies": {
diff --git a/client/src/GraphQL/Calls/teams.js b/client/src/GraphQL/Calls/teams.js
index 1a5f02f5b..7329d3e07 100644
--- a/client/src/GraphQL/Calls/teams.js
+++ b/client/src/GraphQL/Calls/teams.js
@@ -10,29 +10,12 @@ export const GET_ALL_TEAMS_QUERY = gql`
id
name
slug
- }
- }
- }
-`
-
-export const GET_ALL_TEAM_BOTS_QUERY = gql`
- query teamBotsQuery($teamId: String!) {
- teams_TeamById(teamId: $teamId) {
- fb {
- id
- name
- slug
- team{
- id
- name
- slug
- }
+ kind
}
}
}
`
export default {
- GET_ALL_TEAMS_QUERY,
- GET_ALL_TEAM_BOTS_QUERY
+ GET_ALL_TEAMS_QUERY
};
diff --git a/client/src/GraphQL/models.js b/client/src/GraphQL/models.js
index 252f1ee61..474df1fcf 100644
--- a/client/src/GraphQL/models.js
+++ b/client/src/GraphQL/models.js
@@ -1,4 +1,7 @@
const types = [ 'Live', 'Competition', 'Backtest' ]
+const startModes = [ 'All Months', 'One Month', 'No Time' ]
+const availableMonths = ["January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"]
const exchanges = [
{
@@ -7,4 +10,6 @@ const exchanges = [
}
]
-export { types, exchanges }
+
+
+export { types, exchanges, startModes, availableMonths }
diff --git a/client/src/utils/index.js b/client/src/utils/index.js
index d81838754..e5e223f3c 100644
--- a/client/src/utils/index.js
+++ b/client/src/utils/index.js
@@ -72,3 +72,12 @@ export function slugify (string) {
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, '') // Trim - from end of text
}
+
+export function getIndicatorYears () {
+ const localDate = new Date()
+ let availableYears = []
+ for (var i = 2015; i <= localDate.getUTCFullYear(); i++) {
+ availableYears[i-2015] = i
+ }
+ return availableYears
+}
diff --git a/client/src/views/Clone/Add/index.js b/client/src/views/Clone/Add/index.js
index c86adc833..6b3c76462 100644
--- a/client/src/views/Clone/Add/index.js
+++ b/client/src/views/Clone/Add/index.js
@@ -9,20 +9,22 @@ import { withStyles } from '@material-ui/core/styles'
import classNames from 'classnames'
import styles from './styles'
-import { types} from '../../../GraphQL/models'
-import { isDefined } from '../../../utils'
+import { types, startModes, availableMonths, processNames } from '../../../GraphQL/models'
+import { isDefined, getIndicatorYears } from '../../../utils'
import {
MenuItem, Button, TextField, FormControl, InputLabel, Input, Typography,
Paper, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions,
- Select, FormControlLabel, Checkbox
+ Select, FormControlLabel, Checkbox, FormHelperText
} from '@material-ui/core'
-import { DateTimePicker } from 'material-ui-pickers';
+import NumberFormat from 'react-number-format'
+
+import { DateTimePicker } from 'material-ui-pickers'
import {
ChevronLeft,
ChevronRight,
-} from '@material-ui/icons';
+} from '@material-ui/icons'
class AddClone extends Component {
@@ -31,17 +33,23 @@ class AddClone extends Component {
let user = localStorage.getItem('user')
this.state = {
user: JSON.parse(user),
- teamId:'',
- botId:'',
- mode:'',
+ selectedBot: {'id':''},
+ mode: '',
resumeExecution: false,
beginDatetime: DateTime.local().minus({ days: 8 }).startOf('day'),
endDatetime: DateTime.local(),
waitTime: 1,
- state : '',
+ state: '',
stateDatetime: 0,
createDatetime: 0,
runAsTeam: false,
+ financialBeings:[],
+
+ // Indicator Bot
+ startYear: 2019,
+ endYear: 2019,
+ month: 1,
+ botProcessName: '',
//Error handlers
nameError: false,
@@ -49,8 +57,9 @@ class AddClone extends Component {
botError: false,
modeError: false,
isNewCloneConfirmationOpen: false,
- serverResponse:'',
- serverError: false
+ serverResponse: '',
+ serverError: false,
+ botProcessNameError: false
}
}
@@ -84,8 +93,7 @@ class AddClone extends Component {
A bot clone it's a copy of your bot running on a virtual machine.
- At the moment Live clones trade by default 0.001 BTC on Poloniex.
- Please select the team and bot you want to clone!
+ Please select from your teams and bots the one you want to clone!
@@ -94,6 +102,8 @@ class AddClone extends Component {
{({ loading, error, data }) => {
+ if (error) return `Error! There has been an error loading teams.`
+
if (loading) {
return (
Loading...
)
- } else if(!isDefined(data.teams_FbByTeamMember)){
+ }
+
+ if(!isDefined(data.teams_FbByTeamMember)){
return (
You don't have any teams yet.
)
- } else if (error) return `Error! There has been an error loading teams.`
+ } else{
+ this.financialBeings = data.teams_FbByTeamMember.fb
+ }
return (
- }
- className={classes.selectEmpty}
- onBlur={(e)=>this.setState({teamError:false})}
- error={this.state.teamError}
- onChange={e => this.setState({teamId:e.target.value})}
- >
-
- {data.teams_FbByTeamMember.name + '/' + data.teams_FbByTeamMember.fb[0].name}
-
-
+ this.setState({botError:false})}
+ error={this.state.botError}
+ onChange={e => this.setSelectedBot(e.target.value)}
+ >
+ {
+ data.teams_FbByTeamMember.fb.map(financialBeing => (
+
+ {data.teams_FbByTeamMember.name + '/' + financialBeing.name}
+
+ ))
+ }
+
)
}}
+ {this.state.selectedBot.kind}
-
this.setState({mode:e.target.value})}
- onBlur={(e)=>this.setState({modeError:false})}
- error={this.state.modeError}
- fullWidth
- >
- {types.map(option => (
-
- {option}
-
- ))}
-
+ { /* TRADER BOT */ }
+ { this.state.selectedBot.kind === "TRADER1" &&
+
+
+ At the moment Live Trading Clones trade by default 0.001 BTC on Poloniex.
+
- { this.state.mode == "Backtest" &&
-
- }
- rightArrowIcon={ }
- value={this.state.beginDatetime}
- label='Backtest Start Date'
- onChange={newVal => this.setState({ beginDatetime: newVal })}
- className={classNames(classes.form, classes.textField)}
- fullWidth
- />
+ this.setState({mode:e.target.value})}
+ onBlur={(e)=>this.setState({modeError:false})}
+ error={this.state.modeError}
+ fullWidth
+ >
+ {types.map(option => (
+
+ {option}
+
+ ))}
+
- }
- rightArrowIcon={ }
- value={this.state.endDatetime}
- label='Backtest End Date'
- onChange={newVal => this.setState({ endDatetime: newVal })}
- className={classNames(classes.form, classes.textField)}
- fullWidth
- />
+ { this.state.mode == "Backtest" &&
+
+ }
+ rightArrowIcon={ }
+ value={this.state.beginDatetime}
+ label='Backtest Start Date'
+ onChange={newVal => this.setState({ beginDatetime: newVal })}
+ className={classNames(classes.form, classes.textField)}
+ fullWidth
+ />
- this.setState({waitTime:e.target.value})}
- fullWidth
- />
-
- }
+ }
+ rightArrowIcon={ }
+ value={this.state.endDatetime}
+ label='Backtest End Date'
+ onChange={newVal => this.setState({ endDatetime: newVal })}
+ className={classNames(classes.form, classes.textField)}
+ fullWidth
+ />
-
- If Run as Team is selected, the clone will be taken from the
- team respository instead of your own team member folder.
- Competitions runs from the team respository.
-
- this.setState({runAsTeam:e.target.checked })}
- value="runAsTeam"
- color="primary"
- />
- }
- label="Run as Team"
- className={classNames(classes.form, classes.textField)}
- />
+ this.setState({waitTime:e.target.value})}
+ fullWidth
+ />
+
+ }
-
- The Resume Execution option let's you pick up the context of
- the last execution and continue from there.
-
- this.setState({resumeExecution:e.target.checked })}
- value="resumeExecution"
- color="primary"
- />
- }
- label="Resume Execution"
- className={classNames(classes.form, classes.textField)}
- />
+
+ If Run as Team is selected, the clone will be taken from the
+ team respository instead of your own team member folder.
+ Competitions runs from the team respository.
+
+ this.setState({runAsTeam:e.target.checked })}
+ value="runAsTeam"
+ color="primary"
+ />
+ }
+ label="Run as Team"
+ className={classNames(classes.form, classes.textField)}
+ />
+
+
+ The Resume Execution option let's you pick up the context of
+ the last execution and continue from there.
+
+ this.setState({resumeExecution:e.target.checked })}
+ value="resumeExecution"
+ color="primary"
+ />
+ }
+ label="Resume Execution"
+ className={classNames(classes.form, classes.textField)}
+ />
+
+ }
+
+ { /* INDICATOR AND EXTRACTOR BOTS */ }
+ { (this.state.selectedBot.kind === "TRADER"
+ || this.state.selectedBot.kind === "EXTRACTOR") &&
+
+ this.setState({botProcessName:e.target.value})}
+ onBlur={(e)=>this.setState({botProcessNameError:false})}
+ error={this.state.botProcessNameError}
+ fullWidth
+ />
+
+ this.setState({mode:e.target.value})}
+ onBlur={(e)=>this.setState({modeError:false})}
+ error={this.state.modeError}
+ fullWidth
+ >
+ {startModes.map(option => (
+
+ {option}
+
+ ))}
+
+
+ { this.state.mode === "All Months" &&
+
+ this.setState({startYear:e.target.value})}
+ fullWidth
+ >
+ {getIndicatorYears().map((option, index) => (
+
+ {option}
+
+ ))}
+
+
+ this.setState({endYear:e.target.value})}
+ fullWidth
+ >
+ {getIndicatorYears().map((option, index) => (
+
+ {option}
+
+ ))}
+
+
+ }
+
+ { this.state.mode === "One Month" &&
+
+ this.setState({startYear:e.target.value})}
+ fullWidth
+ >
+ {getIndicatorYears().map((option, index) => (
+
+ {option}
+
+ ))}
+
+
+ this.setState({month:e.target.value})}
+ fullWidth
+ >
+ {availableMonths.map(option => (
+
+ {option}
+
+ ))}
+
+
+ }
+
+ }
({ teamError: true }));
}
@@ -356,6 +505,11 @@ class AddClone extends Component {
this.setState(state => ({ modeError: true }));
}
+ if(this.state.botProcessName.length < 1) {
+ isError = true
+ this.setState(state => ({ botProcessNameError: true }));
+ }
+
return isError;
}
diff --git a/client/src/views/Clone/History/index.js b/client/src/views/Clone/History/index.js
index 961ce3189..7a1c103e3 100644
--- a/client/src/views/Clone/History/index.js
+++ b/client/src/views/Clone/History/index.js
@@ -65,7 +65,7 @@ class HistoryClones extends Component {
)
diff --git a/server/src/enums/CloneMode.js b/server/src/enums/CloneMode.js
index 12b3eb37c..4205db6be 100644
--- a/server/src/enums/CloneMode.js
+++ b/server/src/enums/CloneMode.js
@@ -1,11 +1,17 @@
-export const LIVE = 'Live';
-export const BACKTEST = 'Backtest';
-export const COMPETITION = 'Competition';
+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 CloneModeEnum = [
LIVE,
BACKTEST,
- COMPETITION
-];
+ COMPETITION,
+ ALL_MONTHS,
+ ONE_MONTH,
+ NO_TIME
+]
-export default CloneModeEnum;
+export default CloneModeEnum
diff --git a/server/src/platform/downloadBlob.js b/server/src/graphQLCalls/teams_FbByTeamMember.js
similarity index 100%
rename from server/src/platform/downloadBlob.js
rename to server/src/graphQLCalls/teams_FbByTeamMember.js
diff --git a/server/src/platform/fileStorageReader.js b/server/src/platform/fileStorageReader.js
deleted file mode 100644
index c5e95c3c8..000000000
--- a/server/src/platform/fileStorageReader.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import logger from '../config/logger'
-import { toPlatformDatetime } 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 platformExecutionContext = async (clone, cloneName) => {
- try {
- logger.info("createClone %j", cloneName)
- const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
-
- // Make changes to base deployment config
- deploymentManifest.metadata.name = cloneName
-
- logger.debug("createClone Set basic environment configuration.")
- let env = envConfig
-
- logger.debug("createClone Configuration for team, bot and process.")
- env.push({
- "name": "DEV_TEAM",
- "value": clone.teamId
- })
-
- env.push({
- "name": "BOT",
- "value": clone.botId
- })
-
- // TODO allow other types
- env.push({
- "name": "PROCESS",
- "value": "Trading-Process"
- })
-
- // TODO allow other types
- env.push({
- "name": "TYPE",
- "value": "Trading"
- })
-
- env.push({
- "name": "RUN_AS_TEAM",
- "value": clone.runAsTeam.toString()
- })
-
- env.push({
- "name": "USER_LOGGED_IN",
- "value": clone.userLoggedIn
- })
-
- logger.debug("createClone Configuration for the execution.")
- env.push({
- "name": "START_MODE",
- "value": clone.mode.toLowerCase()
- })
-
- env.push({
- "name": "RESUME_EXECUTION",
- "value": clone.resumeExecution.toString()
- })
-
- if(clone.mode === BACKTEST){
- env.push({
- "name": "BEGIN_DATE_TIME",
- "value": toPlatformDatetime(clone.beginDatetime)
- })
-
- env.push({
- "name": "END_DATE_TIME",
- "value": toPlatformDatetime(clone.endDatetime)
- })
-
- env.push({
- "name": "WAIT_TIME",
- "value": clone.waitTime.toString()
- })
- }
-
- deploymentManifest.spec.template.spec.containers[0].env = env
-
- // Create a new Deployment
- const create = await client.apis.batch.v1.namespaces('default').jobs.post(
- { body: deploymentManifest })
- logger.debug("Create: %j ", create)
-
- // Fetch the Deployment we just created
- const deployment = await client.apis.batch.v1.namespaces('default').jobs(
- deploymentManifest.metadata.name).get()
- logger.debug("Job: %j ", deployment)
-
- logger.info("createClone %j succesfull", cloneName)
- } catch (err) {
- throw new KubernateError(err)
- }
-}
-export default platformExecutionContext
diff --git a/server/src/platform/platformExecutionContext.js b/server/src/platform/platformExecutionContext.js
deleted file mode 100644
index c5e95c3c8..000000000
--- a/server/src/platform/platformExecutionContext.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import logger from '../config/logger'
-import { toPlatformDatetime } 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 platformExecutionContext = async (clone, cloneName) => {
- try {
- logger.info("createClone %j", cloneName)
- const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
-
- // Make changes to base deployment config
- deploymentManifest.metadata.name = cloneName
-
- logger.debug("createClone Set basic environment configuration.")
- let env = envConfig
-
- logger.debug("createClone Configuration for team, bot and process.")
- env.push({
- "name": "DEV_TEAM",
- "value": clone.teamId
- })
-
- env.push({
- "name": "BOT",
- "value": clone.botId
- })
-
- // TODO allow other types
- env.push({
- "name": "PROCESS",
- "value": "Trading-Process"
- })
-
- // TODO allow other types
- env.push({
- "name": "TYPE",
- "value": "Trading"
- })
-
- env.push({
- "name": "RUN_AS_TEAM",
- "value": clone.runAsTeam.toString()
- })
-
- env.push({
- "name": "USER_LOGGED_IN",
- "value": clone.userLoggedIn
- })
-
- logger.debug("createClone Configuration for the execution.")
- env.push({
- "name": "START_MODE",
- "value": clone.mode.toLowerCase()
- })
-
- env.push({
- "name": "RESUME_EXECUTION",
- "value": clone.resumeExecution.toString()
- })
-
- if(clone.mode === BACKTEST){
- env.push({
- "name": "BEGIN_DATE_TIME",
- "value": toPlatformDatetime(clone.beginDatetime)
- })
-
- env.push({
- "name": "END_DATE_TIME",
- "value": toPlatformDatetime(clone.endDatetime)
- })
-
- env.push({
- "name": "WAIT_TIME",
- "value": clone.waitTime.toString()
- })
- }
-
- deploymentManifest.spec.template.spec.containers[0].env = env
-
- // Create a new Deployment
- const create = await client.apis.batch.v1.namespaces('default').jobs.post(
- { body: deploymentManifest })
- logger.debug("Create: %j ", create)
-
- // Fetch the Deployment we just created
- const deployment = await client.apis.batch.v1.namespaces('default').jobs(
- deploymentManifest.metadata.name).get()
- logger.debug("Job: %j ", deployment)
-
- logger.info("createClone %j succesfull", cloneName)
- } catch (err) {
- throw new KubernateError(err)
- }
-}
-export default platformExecutionContext
diff --git a/server/src/schema/mutations/AddCloneMutation.js b/server/src/schema/mutations/AddCloneMutation.js
index a0de06946..e85c5f6f5 100644
--- a/server/src/schema/mutations/AddCloneMutation.js
+++ b/server/src/schema/mutations/AddCloneMutation.js
@@ -46,7 +46,7 @@ const resolve = async(parent, { clone }, context) => {
logger.debug('addClone -> Checking existing clone %j', existingClone)
if(isDefined(existingClone) && existingClone.length > 0){
- throw new CustomError('You can only create one clone per mode. Remove the'
+ throw new CustomError('You can only have one active clone by mode. Remove the'
+ ' existing clone of type: ' + clone.mode + ' and try again.')
}
@@ -76,8 +76,7 @@ const resolve = async(parent, { clone }, context) => {
`,
},
headers: {
- authorization: context.authorization,
- preshared: process.env.OPERATIONS_API_PRESHARED
+ authorization: context.authorization
}
})
From 6785a45415303ca1ec0d12d0cc2a813a17d91235 Mon Sep 17 00:00:00 2001
From: "mati108@gmail.com" <34651529+matbenitez@users.noreply.github.com>
Date: Tue, 29 Jan 2019 16:31:01 -0300
Subject: [PATCH 3/4] Adding images to the list view
---
client/src/GraphQL/Fragments/clone.js | 5 +
client/src/GraphQL/Fragments/index.js | 3 +-
client/src/GraphQL/index.js | 55 ---------
client/src/views/Clone/Add/index.js | 29 ++---
client/src/views/Clone/List/ListClones.js | 42 ++++---
client/src/views/Clone/List/styles.js | 10 +-
server/src/config/envConfig.js | 4 +-
server/src/config/utils.js | 14 ++-
server/src/errors/index.js | 2 -
server/src/graphQLCalls/teamAvatar.js | 30 +++++
.../src/graphQLCalls/teams_FbByTeamMember.js | 46 ++++++--
server/src/kubernetes/createClone.js | 105 +++++++++---------
server/src/kubernetes/getClonePodLogs.js | 8 +-
server/src/kubernetes/getClonePodStatus.js | 8 +-
server/src/kubernetes/removeClone.js | 10 +-
.../src/schema/mutations/AddCloneMutation.js | 60 +++-------
server/src/schema/queries/ListClones.js | 28 ++++-
server/src/schema/types/CloneType.js | 7 +-
server/src/schema/types/input/Clone.js | 9 +-
19 files changed, 247 insertions(+), 228 deletions(-)
create mode 100644 server/src/graphQLCalls/teamAvatar.js
diff --git a/client/src/GraphQL/Fragments/clone.js b/client/src/GraphQL/Fragments/clone.js
index fa94628c4..3cecd787d 100644
--- a/client/src/GraphQL/Fragments/clone.js
+++ b/client/src/GraphQL/Fragments/clone.js
@@ -24,5 +24,10 @@ export const clone = gql`
combinedProfitsB
assetA
assetB
+ kind
+ teamName
+ botName
+ botAvatar
+ teamAvatar
}
`;
diff --git a/client/src/GraphQL/Fragments/index.js b/client/src/GraphQL/Fragments/index.js
index 38211844b..2ee040281 100644
--- a/client/src/GraphQL/Fragments/index.js
+++ b/client/src/GraphQL/Fragments/index.js
@@ -1,4 +1,5 @@
-import { clone } from './clone';
+import { clone } from './clone'
+
export {
clone
diff --git a/client/src/GraphQL/index.js b/client/src/GraphQL/index.js
index 51f292fb3..8b1378917 100644
--- a/client/src/GraphQL/index.js
+++ b/client/src/GraphQL/index.js
@@ -1,56 +1 @@
-import gql from 'graphql-tag'
-const getSecret = gql`
- query($id: ID!){
- keyVault_Secret(id: $id)
- }
-`
-
-const editCloneMutation = gql`
- mutation($id: ID!, $type: String!, $description: String!, $validFrom: Int!,
- $validTo: Int!, $active: Boolean!, $botId: ID!){
- operations_EditClone(
- id: $id,
- type: $type,
- description: $description,
- validFrom: $validFrom,
- validTo: $validTo,
- active: $active,
- botId: $botId
- ){
- id
- key
- }
- }
-`
-
-const getCloneQuery = gql`
- query($id: ID!){
- opeations_Clone(id: $id) {
- id
- authId
- key
- type
- description
- exchange
- validFrom
- validTo
- active
- botId
- }
- }
-`
-
-const getAuditLog = gql`
- query($key: String!){
- keyVault_AuditLogs(key: $key){
- id,
- date,
- action,
- details
- }
- }
-`
-
-export { getSecret, editCloneMutation,
- getCloneQuery, getAuditLog }
diff --git a/client/src/views/Clone/Add/index.js b/client/src/views/Clone/Add/index.js
index 6b3c76462..b58257b37 100644
--- a/client/src/views/Clone/Add/index.js
+++ b/client/src/views/Clone/Add/index.js
@@ -49,7 +49,7 @@ class AddClone extends Component {
startYear: 2019,
endYear: 2019,
month: 1,
- botProcessName: '',
+ processName: '',
//Error handlers
nameError: false,
@@ -59,7 +59,7 @@ class AddClone extends Component {
isNewCloneConfirmationOpen: false,
serverResponse: '',
serverError: false,
- botProcessNameError: false
+ processNameError: false
}
}
@@ -158,7 +158,7 @@ class AddClone extends Component {
{ /* TRADER BOT */ }
- { this.state.selectedBot.kind === "TRADER1" &&
+ { this.state.selectedBot.kind === "TRADER" &&
At the moment Live Trading Clones trade by default 0.001 BTC on Poloniex.
@@ -262,16 +262,16 @@ class AddClone extends Component {
}
{ /* INDICATOR AND EXTRACTOR BOTS */ }
- { (this.state.selectedBot.kind === "TRADER"
+ { (this.state.selectedBot.kind === "INDICATOR"
|| this.state.selectedBot.kind === "EXTRACTOR") &&
this.setState({botProcessName:e.target.value})}
- onBlur={(e)=>this.setState({botProcessNameError:false})}
- error={this.state.botProcessNameError}
+ value={this.state.processName}
+ onChange={(e)=> this.setState({processName:e.target.value})}
+ onBlur={(e)=>this.setState({processNameError:false})}
+ error={this.state.processNameError}
fullWidth
/>
@@ -446,7 +446,7 @@ class AddClone extends Component {
}
if(this.state.selectedBot.kind !== "TRADER"){
- variables.clone.botProcessName = this.state.botProcessName
+ variables.clone.processName = this.state.processName
variables.clone.startYear = this.state.startYear
variables.clone.endYear = this.state.endYear
variables.clone.month = this.state.month
@@ -478,7 +478,7 @@ class AddClone extends Component {
stateDatetime: 0,
createDatetime: 0,
runAsTeam: false,
- botProcessName: '',
+ processName: '',
//Error handlers
nameError: false,
@@ -488,7 +488,7 @@ class AddClone extends Component {
isNewCloneConfirmationOpen: false,
serverResponse: '',
serverError: false,
- botProcessNameError: false
+ processNameError: false
})
};
@@ -497,7 +497,7 @@ class AddClone extends Component {
if(this.state.selectedBot.length < 1) {
isError = true
- this.setState(state => ({ teamError: true }));
+ this.setState(state => ({ selectedBotError: true }));
}
if(this.state.mode.length < 1) {
@@ -505,9 +505,10 @@ class AddClone extends Component {
this.setState(state => ({ modeError: true }));
}
- if(this.state.botProcessName.length < 1) {
+ if(this.state.selectedBot.kind !== "TRADER"
+ && this.state.processName.length < 1 ) {
isError = true
- this.setState(state => ({ botProcessNameError: true }));
+ this.setState(state => ({ processNameError: true }));
}
return isError;
diff --git a/client/src/views/Clone/List/ListClones.js b/client/src/views/Clone/List/ListClones.js
index 15bcfa5e3..3dcfeda24 100644
--- a/client/src/views/Clone/List/ListClones.js
+++ b/client/src/views/Clone/List/ListClones.js
@@ -11,7 +11,7 @@ import {
ExpansionPanelActions, Chip, Divider
} from '@material-ui/core'
-
+import Avatar from '@material-ui/core/Avatar';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { isDefined, toLocalTime } from '../../../utils'
@@ -37,10 +37,12 @@ class ListClones extends Component {
}>
-
{clone.teamId}
+
+
{clone.teamName}
-
{clone.botId}
+
+
{clone.botName}
{clone.mode}
@@ -82,23 +84,31 @@ class ListClones extends Component {
-
Trading Information
+
Execution Information
- Date:
- Buy Average:
- Sell Average:
- Market Rate:
- ROI {clone.assetA}:
- ROI {clone.assetB}:
+ Last Execution:
+ { (clone.kind === 'TRADER') &&
+
+ Buy Average:
+ Sell Average:
+ Market Rate:
+ ROI {clone.assetA}:
+ ROI {clone.assetB}:
+
+ }
- {toLocalTime(clone.summaryDate)}
- {clone.buyAverage}
- {clone.sellAverage}
- {clone.marketRate}
- {clone.combinedProfitsA}
- {clone.combinedProfitsB}
+ {isDefined(clone.summaryDate) ? toLocalTime(clone.summaryDate): 'waiting update...'}
+ { (clone.kind === 'INDICATOR' || clone.kind === 'EXTRACTOR') &&
+
+ {clone.buyAverage}
+ {clone.sellAverage}
+ {clone.marketRate}
+ {clone.combinedProfitsA}
+ {clone.combinedProfitsB}
+
+ }
diff --git a/client/src/views/Clone/List/styles.js b/client/src/views/Clone/List/styles.js
index bd3f9b06e..6b91e1f58 100644
--- a/client/src/views/Clone/List/styles.js
+++ b/client/src/views/Clone/List/styles.js
@@ -1,7 +1,6 @@
const styles = theme => ({
root: {
flexGrow: 1,
- padding: 10,
margin: 10
},
image: {
@@ -42,7 +41,11 @@ const styles = theme => ({
heading: {
fontSize: theme.typography.pxToRem(18),
flexBasis: '33.33%',
- fontWeight: '600'
+ fontWeight: '600',
+ whiteSpace: 'nowrap',
+ display: 'inline-flex',
+ alignItems: 'center',
+ paddingRight: theme.typography.pxToRem(20)
},
helper: {
borderLeft: `2px solid ${theme.palette.divider}`,
@@ -78,6 +81,9 @@ const styles = theme => ({
paddingLeft: '10px',
paddingTop: '10px'
},
+ avatar: {
+ margin: 10,
+ },
})
export default styles
diff --git a/server/src/config/envConfig.js b/server/src/config/envConfig.js
index 7bd3cf254..a831a6bad 100644
--- a/server/src/config/envConfig.js
+++ b/server/src/config/envConfig.js
@@ -7,11 +7,9 @@ env.push({
"value": process.env.PLATFORM_ENVIRONMENT
})
-// FIXME
env.push({
"name": "GATEWAY_ENDPOINT",
- "value": "https://platform-api-dev.superalgos.org/graphql"
- //"value": process.env.GATEWAY_ENDPOINT
+ "value": process.env.GATEWAY_ENDPOINT
})
env.push({
diff --git a/server/src/config/utils.js b/server/src/config/utils.js
index f27cd7b72..e9f3b1c12 100644
--- a/server/src/config/utils.js
+++ b/server/src/config/utils.js
@@ -8,6 +8,16 @@ export function isDefined (d) {
return d !== null && typeof d !== 'undefined'
}
-export function getJobNameFromClone (clone) {
- return clone.teamId + '-' + clone.botId + '-' + clone.mode.toLowerCase()
+export function getJobNameFromClone (teamSlug, botSlug, mode) {
+ return teamSlug + '-' + botSlug + '-' + 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 dea8803fb..88ee37eae 100644
--- a/server/src/errors/index.js
+++ b/server/src/errors/index.js
@@ -41,8 +41,6 @@ export class OperationsError extends Error {
message = 'OperationsModule Internal Error. ' + this.message
constructor(message){
super(message)
- logger.error(this.stack)
- this.stack = ''
}
}
diff --git a/server/src/graphQLCalls/teamAvatar.js b/server/src/graphQLCalls/teamAvatar.js
new file mode 100644
index 000000000..c2ba09a3f
--- /dev/null
+++ b/server/src/graphQLCalls/teamAvatar.js
@@ -0,0 +1,30 @@
+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/teams_FbByTeamMember.js
index a7044399b..112e95790 100644
--- a/server/src/graphQLCalls/teams_FbByTeamMember.js
+++ b/server/src/graphQLCalls/teams_FbByTeamMember.js
@@ -1,17 +1,39 @@
import logger from '../config/logger'
-import { toPlatformDatetime } from '../config/utils'
+import axios from 'axios'
-const downloadBlob = async (containerName, blobName) => {
- logger.debug('downloadBlob -> About to get a file: %s%s', containerName, blobName)
- return new Promise((resolve, reject) => {
- blobService.getBlobToText(containerName, blobName, (err, data) => {
- if (err) {
- reject(err);
- } else {
- resolve(data);
+const teams_FbByTeamMember = (authorization) => {
+ logger.debug('GraphQL call teams_FbByTeamMember')
+
+ return axios({
+ url: process.env.GATEWAY_ENDPOINT,
+ method: 'post',
+ data: {
+ query: `
+ query Teams_FbByTeamMember {
+ teams_FbByTeamMember {
+ id
+ name
+ slug
+ fb {
+ id
+ name
+ slug
+ kind
+ avatar
}
- });
- });
+ members {
+ member {
+ alias
+ }
+ }
+ }
+ }
+ `,
+ },
+ headers: {
+ authorization: authorization
+ }
+ })
};
-export default downloadBlob
+export default teams_FbByTeamMember
diff --git a/server/src/kubernetes/createClone.js b/server/src/kubernetes/createClone.js
index 9632ac6b8..3fed6c055 100644
--- a/server/src/kubernetes/createClone.js
+++ b/server/src/kubernetes/createClone.js
@@ -1,5 +1,5 @@
import logger from '../config/logger'
-import { toPlatformDatetime, getJobNameFromClone } from '../config/utils'
+import { toPlatformDatetime, getJobNameFromClone, isDefined } from '../config/utils'
import envConfig from '../config/envConfig'
import { KubernateError } from '../errors'
import { Client, config } from 'kubernetes-client'
@@ -7,97 +7,98 @@ import { Client, config } from 'kubernetes-client'
import deploymentManifest from '../config/clone-deployment.json'
import { BACKTEST} from '../enums/CloneMode';
-const createClone = async (clone) => {
+const createClone = async (clone, teamSlug, botSlug) => {
try {
- let cloneName = getJobNameFromClone(clone)
- logger.info("createClone %j", cloneName)
+ let cloneName = getJobNameFromClone(teamSlug, botSlug, clone.mode)
+ logger.info("createClone %s", cloneName)
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
// Make changes to base deployment config
deploymentManifest.metadata.name = cloneName
- logger.debug("createClone Set basic environment configuration.")
+ logger.debug("createClone Environment and Auth Configuration.")
let env = envConfig
- logger.debug("createClone Configuration for team, bot and process.")
+ logger.debug("createClone General Financial Being Configuration.")
env.push({
"name": "DEV_TEAM",
"value": clone.teamId
})
-
env.push({
"name": "BOT",
"value": clone.botId
})
-
- // TODO allow other types
+ env.push({
+ "name": "KIND",
+ "value": clone.kind
+ })
env.push({
"name": "PROCESS",
- "value": "Trading-Process"
+ "value": clone.processName
})
-
- // TODO allow other types
env.push({
- "name": "TYPE",
- "value": "Trading"
+ "name": "CLONE_ID",
+ "value": clone.id.toString()
+ })
+ env.push({
+ "name": "START_MODE",
+ "value": clone.mode.toLowerCase()
})
-
env.push({
"name": "RUN_AS_TEAM",
"value": clone.runAsTeam.toString()
})
-
env.push({
"name": "USER_LOGGED_IN",
"value": clone.userLoggedIn
})
- logger.debug("createClone Configuration for the execution.")
- env.push({
- "name": "CLONE_ID",
- "value": clone.id.toString()
- })
-
- env.push({
- "name": "START_MODE",
- "value": clone.mode.toLowerCase()
- })
-
- env.push({
- "name": "RESUME_EXECUTION",
- "value": clone.resumeExecution.toString()
- })
-
- if(clone.mode === BACKTEST){
+ logger.debug("createClone Trading Configuration.")
+ if(clone.kind === 'Trading'){
env.push({
- "name": "BEGIN_DATE_TIME",
- "value": toPlatformDatetime(clone.beginDatetime)
+ "name": "RESUME_EXECUTION",
+ "value": clone.resumeExecution.toString()
})
- env.push({
- "name": "END_DATE_TIME",
- "value": toPlatformDatetime(clone.endDatetime)
- })
-
- env.push({
- "name": "WAIT_TIME",
- "value": clone.waitTime.toString()
- })
+ if(clone.mode === BACKTEST){
+ env.push({
+ "name": "BEGIN_DATE_TIME",
+ "value": toPlatformDatetime(clone.beginDatetime)
+ })
+ env.push({
+ "name": "END_DATE_TIME",
+ "value": toPlatformDatetime(clone.endDatetime)
+ })
+ env.push({
+ "name": "WAIT_TIME",
+ "value": clone.waitTime.toString()
+ })
+ }
+ } else if(clone.kind === 'Indicator' || clone.kind === 'Extractor'){
+ env.push({
+ "name": "MIN_YEAR",
+ "value": isDefined(clone.startYear) ? clone.startYear.toString() : ''
+ })
+ env.push({
+ "name": "MAX_YEAR",
+ "value": isDefined(clone.endYear) ? clone.endYear.toString() : ''
+ })
+ env.push({
+ "name": "MONTH",
+ "value": isDefined(clone.month) ? clone.month.toString() : ''
+ })
+ env.push({
+ "name": "INTERVAL",
+ "value": isDefined(clone.interval) ? clone.interval.toString() : ''
+ })
}
deploymentManifest.spec.template.spec.containers[0].env = env
- // Create a new Deployment
const create = await client.apis.batch.v1.namespaces('default').jobs.post(
{ body: deploymentManifest })
- logger.debug("Create: %j ", create)
- // Fetch the Deployment we just created
- const deployment = await client.apis.batch.v1.namespaces('default').jobs(
- deploymentManifest.metadata.name).get()
- logger.debug("Job: %j ", deployment)
-
- logger.info("createClone %j succesfull", cloneName)
+ logger.info("createClone %s on Kubernates success.", cloneName)
} catch (err) {
throw new KubernateError(err)
}
diff --git a/server/src/kubernetes/getClonePodLogs.js b/server/src/kubernetes/getClonePodLogs.js
index a3f8d129a..af6c3c134 100644
--- a/server/src/kubernetes/getClonePodLogs.js
+++ b/server/src/kubernetes/getClonePodLogs.js
@@ -4,7 +4,7 @@ import { Client, config } from 'kubernetes-client'
const getClonePodStatus = async (cloneName) => {
try {
- logger.info("getClonePodStatus %j", cloneName)
+ logger.debug("getClonePodStatus on kubernates started.")
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
let query = {
@@ -12,9 +12,8 @@ const getClonePodStatus = async (cloneName) => {
"labelSelector": "job-name="+cloneName
}
}
- // Get the pod
+
const pod = await client.api.v1.namespaces('default').pods.get(query)
- // logger.debug("pod: %j ", pod)
let queryLogs = {
"qs":{
@@ -24,7 +23,8 @@ const getClonePodStatus = async (cloneName) => {
const logs = await client.api.v1.namespaces('default')
.pods(pod.body.items[0].metadata.name).log.get(queryLogs)
let logsArray = logs.body.split('\n')
- // logger.debug("logs: %j ", logsArray)
+
+ logger.debug("getClonePodStatus on kubernates successful.")
return JSON.stringify(logsArray, null, 2)
} catch (err) {
diff --git a/server/src/kubernetes/getClonePodStatus.js b/server/src/kubernetes/getClonePodStatus.js
index ee4dfb835..37293c460 100644
--- a/server/src/kubernetes/getClonePodStatus.js
+++ b/server/src/kubernetes/getClonePodStatus.js
@@ -4,7 +4,7 @@ import { Client, config } from 'kubernetes-client'
const getClonePodStatus = async (cloneName) => {
try {
- logger.info("getClonePodStatus %j", cloneName)
+ logger.info("getClonePodStatus on kubernates started. ")
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
let query = {
@@ -12,18 +12,16 @@ const getClonePodStatus = async (cloneName) => {
"labelSelector": "job-name="+cloneName
}
}
- // Get The Pod
+
const pod = await client.api.v1.namespaces('default').pods.get(query)
- logger.debug("pod: %j ", pod)
let clonePodStatus = pod.body.items[0].status.containerStatuses[0].state
- logger.debug("clonePodStatus: %j ", clonePodStatus)
if (clonePodStatus.hasOwnProperty('terminated')
&& clonePodStatus.terminated.hasOwnProperty('containerID')) {
delete clonePodStatus.terminated.containerID
}
- logger.info("getClonePodStatus %j succesfull", cloneName)
+ logger.info("getClonePodStatus %s on kubernates successful", cloneName)
return JSON.stringify(clonePodStatus, null, 2)
} catch (err) {
throw new KubernateError(err)
diff --git a/server/src/kubernetes/removeClone.js b/server/src/kubernetes/removeClone.js
index 4bb2a3e8d..536eac16a 100644
--- a/server/src/kubernetes/removeClone.js
+++ b/server/src/kubernetes/removeClone.js
@@ -4,7 +4,7 @@ import { Client, config } from 'kubernetes-client'
const removeClone = async (cloneName) => {
try {
- logger.info("removeClone %j", cloneName)
+ logger.info("removeClone %s", cloneName)
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
let query = {
@@ -13,18 +13,14 @@ const removeClone = async (cloneName) => {
}
}
- // Remove the Job
const removed = await client.apis.batch.v1.namespaces('default').jobs(
cloneName).delete()
- logger.debug("Job Removed: %j ", removed)
-
- // Get the pod
+
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.debug("Pod Removed: %j ", podRemoved)
- logger.info("removeClone %j succesfull", cloneName)
+ logger.info("removeClone %s on kubernates succesful", cloneName)
} catch (err) {
throw new KubernateError(err)
}
diff --git a/server/src/schema/mutations/AddCloneMutation.js b/server/src/schema/mutations/AddCloneMutation.js
index e85c5f6f5..47577519f 100644
--- a/server/src/schema/mutations/AddCloneMutation.js
+++ b/server/src/schema/mutations/AddCloneMutation.js
@@ -1,7 +1,8 @@
import { CloneInputType } from '../types/input';
import { CloneType } from '../types';
import { Clone } from '../../models';
-import { CloneModeEnum , BACKTEST} from '../../enums/CloneMode';
+import { CloneModeEnum } from '../../enums/CloneMode';
+import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember';
import {
AuthentificationError,
@@ -10,10 +11,9 @@ import {
CustomError
} from '../../errors'
-import axios from 'axios';
import logger from '../../config/logger'
import createKubernetesClone from '../../kubernetes/createClone'
-import { isDefined } from '../../config/utils'
+import { isDefined, getSelectedBot } from '../../config/utils'
const args = {
clone: {type: CloneInputType }
@@ -30,59 +30,27 @@ const resolve = async(parent, { clone }, context) => {
throw new WrongArgumentsError('The mode selected is not valid.')
}
- // Disable execution dates if mode is not backtest
- if((clone.hasOwnProperty('beginDatetime')
- || clone.hasOwnProperty('endDatetime'))
- && clone.mode !== BACKTEST){
- throw new WrongArgumentsError('Ony Backtest mode allows begin and end date selection.')
- }
-
let existingClone = await Clone.find(
{
authId: context.userId,
mode: clone.mode,
+ botId: clone.botId,
active: true
})
logger.debug('addClone -> Checking existing clone %j', existingClone)
- if(isDefined(existingClone) && existingClone.length > 0){
+ if(existingClone.length > 0){
throw new CustomError('You can only have one active clone by mode. Remove the'
+ ' existing clone of type: ' + clone.mode + ' and try again.')
}
try{
- const userTeam = await axios({
- url: process.env.GATEWAY_ENDPOINT,
- method: 'post',
- data: {
- query: `
- query Teams_FbByTeamMember {
- teams_FbByTeamMember {
- id
- name
- slug
- fb {
- id
- name
- slug
- }
- members {
- member {
- alias
- }
- }
- }
- }
- `,
- },
- headers: {
- authorization: context.authorization
- }
- })
-
- clone.teamId = userTeam.data.data.teams_FbByTeamMember.slug
- clone.botId = userTeam.data.data.teams_FbByTeamMember.fb[0].slug
- clone.userLoggedIn = userTeam.data.data.teams_FbByTeamMember.members[0].member.alias
+ let botsByUser = await teams_FbByTeamMember(context.authorization)
+ console.log("botsByUser: ", botsByUser)
+ 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
@@ -90,7 +58,7 @@ const resolve = async(parent, { clone }, context) => {
let newClone = new Clone(clone)
newClone.id = newClone._id
newClone.authId = context.userId
- await createKubernetesClone(newClone)
+ await createKubernetesClone(newClone, botsByUser.data.data.teams_FbByTeamMember.slug, selectedBot.slug )
return new Promise((resolve, reject) => {
newClone.save((err) => {
@@ -105,8 +73,8 @@ const resolve = async(parent, { clone }, context) => {
}
})
})
- } catch(error) {
- logger.debug('addClone -> Error: %j', error)
+ } catch(err) {
+ logger.error('addClone -> Error: %s', err.stack)
throw new OperationsError('There has been an error creating the clone.')
}
}
diff --git a/server/src/schema/queries/ListClones.js b/server/src/schema/queries/ListClones.js
index f1fb34e25..5a1292e56 100644
--- a/server/src/schema/queries/ListClones.js
+++ b/server/src/schema/queries/ListClones.js
@@ -3,7 +3,8 @@ import {
} from 'graphql'
import {
- AuthentificationError
+ AuthentificationError,
+ OperationsError
} from '../../errors'
import { CloneType } from '../types'
@@ -11,13 +12,15 @@ import { Clone } from '../../models'
import logger from '../../config/logger'
import getCloneStatus from '../../kubernetes/getClonePodStatus'
import getCloneLogs from '../../kubernetes/getClonePodLogs'
-import { getJobNameFromClone } from '../../config/utils'
+import { getJobNameFromClone, getSelectedBot } from '../../config/utils'
+import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember'
+import teamAvatar from '../../graphQLCalls/teamAvatar'
const args = {}
const resolve = async(parent, args, context) => {
logger.debug('List Clones -> Entering Fuction.')
-
+ try{
if (!context.userId) {
throw new AuthentificationError()
}
@@ -27,13 +30,30 @@ const resolve = async(parent, args, context) => {
active: true
})
+ let botsByUser = await teams_FbByTeamMember(context.authorization)
+
for (var i = 0; i < clones.length; i++) {
logger.debug('List Clones -> clone: %j', clones[i])
- let cloneName = getJobNameFromClone(clones[i])
+
+ // TODO Refactor this code once financial beings api is ready
+ let selectedBot = getSelectedBot(botsByUser.data.data.teams_FbByTeamMember, clones[i].botId)
+ let cloneName = getJobNameFromClone(botsByUser.data.data.teams_FbByTeamMember.slug, selectedBot.slug, clones[i].mode)
+ clones[i].kind = selectedBot.kind
+ clones[i].teamName = botsByUser.data.data.teams_FbByTeamMember.name
+ clones[i].botName = selectedBot.name
+ clones[i].botAvatar = selectedBot.avatar
+
+ let team = await teamAvatar(context.authorization, botsByUser.data.data.teams_FbByTeamMember.id)
+ clones[i].teamAvatar = team.data.data.teams_TeamById.profile.avatar
+
clones[i].state = await getCloneStatus(cloneName)
clones[i].lastLogs = await getCloneLogs(cloneName)
}
return clones
+ } catch (err){
+ logger.error('List Clones error: %s', err.stack)
+ throw new OperationsError('There has been an error listing the clones.')
+ }
}
const query = {
diff --git a/server/src/schema/types/CloneType.js b/server/src/schema/types/CloneType.js
index d6e679c66..6c8db2b18 100644
--- a/server/src/schema/types/CloneType.js
+++ b/server/src/schema/types/CloneType.js
@@ -31,7 +31,12 @@ const CloneType = new GraphQLObjectType({
combinedProfitsA: {type: GraphQLFloat},
combinedProfitsB: {type: GraphQLFloat},
assetA: {type: GraphQLString},
- assetB: {type: GraphQLString}
+ assetB: {type: GraphQLString},
+ kind: {type: GraphQLString},
+ teamName: {type: GraphQLString},
+ botName: {type: GraphQLString},
+ botAvatar: {type: GraphQLString},
+ teamAvatar: {type: GraphQLString}
})
})
export default CloneType
diff --git a/server/src/schema/types/input/Clone.js b/server/src/schema/types/input/Clone.js
index 62f626625..ef59f0a87 100644
--- a/server/src/schema/types/input/Clone.js
+++ b/server/src/schema/types/input/Clone.js
@@ -10,14 +10,19 @@ const Type = new GraphQLInputObjectType({
name: 'CloneInput',
description: 'Payload for clone input',
fields: () => ({
- teamId: { type: new GraphQLNonNull(GraphQLString) },
botId: { type: new GraphQLNonNull(GraphQLString) },
mode: { type: GraphQLString },
resumeExecution: { type: GraphQLBoolean },
beginDatetime: { type: GraphQLInt },
endDatetime: { type: GraphQLInt },
waitTime: { type: GraphQLInt },
- runAsTeam: { type: GraphQLBoolean }
+ runAsTeam: { type: GraphQLBoolean },
+ startYear: { type: GraphQLInt },
+ endYear: { type: GraphQLInt },
+ month: { type: GraphQLInt },
+ interval: { type: GraphQLInt },
+ processName: { type: GraphQLString },
+ kind: { type: GraphQLString }
})
});
From ee91e02cecaf3f22ab3fbbc1acd4c01ec2b6ccd6 Mon Sep 17 00:00:00 2001
From: "mati108@gmail.com" <34651529+matbenitez@users.noreply.github.com>
Date: Tue, 29 Jan 2019 18:04:00 -0300
Subject: [PATCH 4/4] Fixing history, remove, and logs views
---
client/src/views/Clone/List/ListClones.js | 34 +++++++++++--------
client/src/views/Clone/List/styles.js | 10 +++---
server/src/kubernetes/getClonePodLogs.js | 12 +++----
server/src/kubernetes/removeClone.js | 6 ++--
.../src/schema/mutations/AddCloneMutation.js | 2 +-
.../schema/mutations/RemoveCloneMutation.js | 24 +++++++++++--
server/src/schema/queries/HistoryClones.js | 10 ++++++
server/src/schema/queries/ListClones.js | 25 +++++---------
server/src/schema/queries/cloneDetails.js | 21 ++++++++++++
9 files changed, 96 insertions(+), 48 deletions(-)
create mode 100644 server/src/schema/queries/cloneDetails.js
diff --git a/client/src/views/Clone/List/ListClones.js b/client/src/views/Clone/List/ListClones.js
index 3dcfeda24..433890125 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') &&
+ { (clone.kind === 'TRADER' && 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') &&
+ { ((clone.kind === 'INDICATOR' || clone.kind === 'EXTRACTOR') && isDefined(clone.assetA) ) &&
{clone.buyAverage}
{clone.sellAverage}
@@ -188,19 +188,23 @@ 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.
+
+
+
+
({
marginRight: theme.spacing.unit,
},
textArea: {
+ width:'1750px',
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
whiteSpace: 'pre',
- overflowY:'auto',
- overflowX:'hidden'
},
heading: {
fontSize: theme.typography.pxToRem(18),
@@ -52,10 +51,13 @@ const styles = theme => ({
padding: `0px ${theme.spacing.unit * 2}px`,
},
logsDialog:{
- width:'1750px',
- overflowY:'auto',
+ overflowY:'hidden',
overflowX:'hidden'
},
+ logsWrapper:{
+ overflowY:'auto',
+ overflowX:'auto'
+ },
details:{
display:'flex'
},
diff --git a/server/src/kubernetes/getClonePodLogs.js b/server/src/kubernetes/getClonePodLogs.js
index af6c3c134..9730f300c 100644
--- a/server/src/kubernetes/getClonePodLogs.js
+++ b/server/src/kubernetes/getClonePodLogs.js
@@ -2,9 +2,9 @@ import logger from '../config/logger'
import { KubernateError } from '../errors'
import { Client, config } from 'kubernetes-client'
-const getClonePodStatus = async (cloneName) => {
+const getClonePodLogs = async (cloneName) => {
try {
- logger.debug("getClonePodStatus on kubernates started.")
+ logger.debug("getClonePodLogs on kubernates started.")
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
let query = {
@@ -12,23 +12,23 @@ const getClonePodStatus = async (cloneName) => {
"labelSelector": "job-name="+cloneName
}
}
-
+
const pod = await client.api.v1.namespaces('default').pods.get(query)
let queryLogs = {
"qs":{
- "tailLines": "15"
+ "tailLines": "20"
}
}
const logs = await client.api.v1.namespaces('default')
.pods(pod.body.items[0].metadata.name).log.get(queryLogs)
let logsArray = logs.body.split('\n')
- logger.debug("getClonePodStatus on kubernates successful.")
+ logger.debug("getClonePodLogs on kubernates successful.")
return JSON.stringify(logsArray, null, 2)
} catch (err) {
throw new KubernateError(err)
}
}
-export default getClonePodStatus
+export default getClonePodLogs
diff --git a/server/src/kubernetes/removeClone.js b/server/src/kubernetes/removeClone.js
index 536eac16a..2ec404572 100644
--- a/server/src/kubernetes/removeClone.js
+++ b/server/src/kubernetes/removeClone.js
@@ -1,9 +1,11 @@
import logger from '../config/logger'
import { KubernateError } from '../errors'
import { Client, config } from 'kubernetes-client'
+import { getJobNameFromClone } from '../config/utils'
-const removeClone = async (cloneName) => {
+const removeClone = async (clone, teamSlug, botSlug) => {
try {
+ let cloneName = getJobNameFromClone(teamSlug, botSlug, clone.mode)
logger.info("removeClone %s", cloneName)
const client = new Client({config: config.fromKubeconfig(), version: '1.9'})
@@ -15,7 +17,7 @@ const removeClone = async (cloneName) => {
const removed = await client.apis.batch.v1.namespaces('default').jobs(
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()
diff --git a/server/src/schema/mutations/AddCloneMutation.js b/server/src/schema/mutations/AddCloneMutation.js
index 47577519f..5aceb940b 100644
--- a/server/src/schema/mutations/AddCloneMutation.js
+++ b/server/src/schema/mutations/AddCloneMutation.js
@@ -45,8 +45,8 @@ const resolve = async(parent, { clone }, context) => {
}
try{
+ // Authorization is handled by the teams module
let botsByUser = await teams_FbByTeamMember(context.authorization)
- console.log("botsByUser: ", botsByUser)
let selectedBot = getSelectedBot(botsByUser.data.data.teams_FbByTeamMember, clone.botId)
clone.teamId = botsByUser.data.data.teams_FbByTeamMember.id
clone.botId = selectedBot.id
diff --git a/server/src/schema/mutations/RemoveCloneMutation.js b/server/src/schema/mutations/RemoveCloneMutation.js
index 88152cf54..dcd51faae 100644
--- a/server/src/schema/mutations/RemoveCloneMutation.js
+++ b/server/src/schema/mutations/RemoveCloneMutation.js
@@ -12,6 +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'
const args = {
id: { type: new GraphQLNonNull(GraphQLID) }
@@ -24,7 +26,22 @@ const resolve = async (parent, { id }, context) => {
throw new AuthentificationError()
}
- await removeKuberneteClone(id)
+ let clone = await Clone.findOne({
+ _id: id,
+ authId: context.userId,
+ active: true
+ })
+
+ if(!isDefined(clone)){
+ throw new CustomError('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)
+
+ logger.debug('removeClone -> Removing Clone from Kubernates.')
+ await removeKuberneteClone(clone, botsByUser.data.data.teams_FbByTeamMember.slug, selectedBot.slug)
const query = {
_id: id,
@@ -33,12 +50,13 @@ const resolve = async (parent, { id }, context) => {
const options = { new: true }
const update = { active: false }
- logger.debug('removeClone -> Removing Clone from DB.')
Clone.findOneAndUpdate(query, update, options, (err, doc) => {
if (err){
+ logger.error('removeClone -> Error removing clone from the DB. %s', err.stack)
throw new OperationsError(err)
}else{
- resolve('Clone Removed.')
+ logger.debug('removeClone -> Clone Removed from the DB.')
+ return('Clone Removed.')
}
})
}
diff --git a/server/src/schema/queries/HistoryClones.js b/server/src/schema/queries/HistoryClones.js
index e413997ec..5c20df2bb 100644
--- a/server/src/schema/queries/HistoryClones.js
+++ b/server/src/schema/queries/HistoryClones.js
@@ -11,6 +11,8 @@ 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'
const args = {}
@@ -26,6 +28,14 @@ const resolve = async(parent, args, context) => {
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)
+
+ for (var i = 0; i < clones.length; i++) {
+ clones[i] = await cloneDetails(context.authorization, botsByUser, clones[i])
+ }
+
return clones
}
diff --git a/server/src/schema/queries/ListClones.js b/server/src/schema/queries/ListClones.js
index 5a1292e56..c6cbe9500 100644
--- a/server/src/schema/queries/ListClones.js
+++ b/server/src/schema/queries/ListClones.js
@@ -12,9 +12,8 @@ import { Clone } from '../../models'
import logger from '../../config/logger'
import getCloneStatus from '../../kubernetes/getClonePodStatus'
import getCloneLogs from '../../kubernetes/getClonePodLogs'
-import { getJobNameFromClone, getSelectedBot } from '../../config/utils'
import teams_FbByTeamMember from '../../graphQLCalls/teams_FbByTeamMember'
-import teamAvatar from '../../graphQLCalls/teamAvatar'
+import cloneDetails from './cloneDetails'
const args = {}
@@ -30,24 +29,16 @@ const resolve = async(parent, args, context) => {
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)
for (var i = 0; i < clones.length; i++) {
- logger.debug('List Clones -> clone: %j', clones[i])
-
- // TODO Refactor this code once financial beings api is ready
- let selectedBot = getSelectedBot(botsByUser.data.data.teams_FbByTeamMember, clones[i].botId)
- let cloneName = getJobNameFromClone(botsByUser.data.data.teams_FbByTeamMember.slug, selectedBot.slug, clones[i].mode)
- clones[i].kind = selectedBot.kind
- clones[i].teamName = botsByUser.data.data.teams_FbByTeamMember.name
- clones[i].botName = selectedBot.name
- clones[i].botAvatar = selectedBot.avatar
-
- let team = await teamAvatar(context.authorization, botsByUser.data.data.teams_FbByTeamMember.id)
- clones[i].teamAvatar = team.data.data.teams_TeamById.profile.avatar
-
- clones[i].state = await getCloneStatus(cloneName)
- clones[i].lastLogs = await getCloneLogs(cloneName)
+ clones[i] = await cloneDetails(context.authorization, botsByUser, 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)
+ clones[i].lastLogs = lastLogs.substring(1, lastLogs.length-1)
}
return clones
} catch (err){
diff --git a/server/src/schema/queries/cloneDetails.js b/server/src/schema/queries/cloneDetails.js
new file mode 100644
index 000000000..06c3ed99c
--- /dev/null
+++ b/server/src/schema/queries/cloneDetails.js
@@ -0,0 +1,21 @@
+import logger from '../../config/logger'
+import { getJobNameFromClone, getSelectedBot } from '../../config/utils'
+import teamAvatar from '../../graphQLCalls/teamAvatar'
+
+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.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
+ logger.debug('Retrieving clone details complete.')
+ return clone
+};
+
+export default cloneDetails