/*
Copyright (C) 2017 Cloudbase Solutions SRL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './AddCloudConnection.scss';
import Reflux from 'reflux';
import ConnectionsStore from '../../stores/ConnectionsStore';
import ConnectionsActions from '../../actions/ConnectionsActions';
import NotificationActions from '../../actions/NotificationActions';
import Dropdown from '../NewDropdown';
import LoadingIcon from "../LoadingIcon/LoadingIcon";
import ValidateEndpoint from '../ValidateEndpoint';
const title = 'Add Cloud Endpoint';
class AddCloudConnection extends Reflux.Component {
static contextTypes = {
onSetTitle: PropTypes.func.isRequired,
};
static defaultProps = {
cloud: null,
connection: null,
type: "new"
}
constructor(props) {
super(props)
this.store = ConnectionsStore
this.state = {
type: props.type, // type of operation: new/edit
connection: props.connection, // connection object (on edit)
connectionName: "", // connection name field
description: "", // connection description field
currentCloud: this.props.cloud, // chosen cloud - if adding a new endpoint
currentCloudData: null, // endpoint field data
validateEndpoint: false, // holds the endpoint object when validation
requiredFields: [], // array that holds all the endpoint required fields - used for field validation
cloudFormsSubmitted: false // flag that indicates if the form has been submitted - used for field validation
}
}
componentWillMount() {
super.componentWillMount.call(this)
this.context.onSetTitle(title);
}
componentDidMount() {
if (this.state.connection) {
this.state.allClouds.forEach(item => {
if (item.name === this.state.connection.type) {
let credentials = this.state.connection.credentials
let newCredentials = {}
for (let i in credentials) {
if (typeof credentials[i] == "object") {
newCredentials['login_type'] = i
// credentials['user_credentials'] = {}
for (let j in credentials[i]) {
// credentials['user_credentials'][j] = credentials[i][j]
newCredentials[j] = credentials[i][j]
}
} else {
newCredentials[i] = credentials[i] + ""
}
}
this.setState({
currentCloudData: newCredentials,
connectionName: this.state.connection.name,
description: this.state.connection.description
}, () => {
this.chooseCloud(item)
})
}
})
} else if (this.props.cloud) {
this.chooseCloud(this.props.cloud)
}
}
/**
* Function called upon saving an endpoint - handles both new and edit operations
*/
handleSave() {
let valid = true
let requiredFields = this.state.requiredFields
// If Azure, validate fields manually
if (this.state.currentCloud.name == "azure") {
if (this.state.currentCloudData.login_type == "user_credentials") {
requiredFields = ["subscription_id", "username", "password"]
} else if (this.state.currentCloudData.login_type == "service_principal_credentials") {
requiredFields = ["subscription_id", "tenant_id", "client_id", "client_secret"]
} else {
valid = false
NotificationActions.notify("Please choose the login type", "error")
}
this.setState({ requiredFields: requiredFields })
}
for (let i in this.state.currentCloudData) {
if (requiredFields.indexOf(i) > -1 && !this.state.currentCloudData[i]) {
valid = false
}
}
requiredFields.forEach((field) => {
if (!this.state.currentCloudData[field]) {
valid = false
}
})
if (this.state.connectionName.trim().length == 0) {
valid = false
}
if (!valid) {
NotificationActions.notify("Please fill all required fields", "error")
this.setState({ cloudFormsSubmitted: true })
} else {
let credentials = Object.assign({}, this.state.currentCloudData)
for (let key in credentials) {
if (credentials[key].label) {
credentials[key] = credentials[key].value
}
let field = this.state.currentCloud.endpoint.fields.find(function findByName(f) { return f.name == this }, key);
// Convert datatype
switch (field.dataType) {
case 'boolean':
credentials[key] = (credentials[key] === true ||
((typeof credentials[key] === 'string' || credentials[key] instanceof String) &&
credentials[key].toLowerCase() == "true"));
break;
case 'integer':
let value = parseInt(credentials[key], 10);
if (value.toString() != credentials[key]) {
valid = false;
NotificationActions.notify('"' + key + '" needs to be an integer', 'error');
}
credentials[key] = value;
break;
default:
// retain original value
break;
}
}
if (!valid) {
return;
}
// If Azure, explicitly setting the fields right
if (this.state.currentCloud.name == "azure") {
credentials = {}
credentials["subscription_id"] = this.state.currentCloudData["subscription_id"]
if (this.state.currentCloudData["login_type"] == "user_credentials") {
credentials["user_credentials"] = {
username: this.state.currentCloudData["username"],
password: this.state.currentCloudData["password"]
}
} else {
credentials["service_principal_credentials"] = {
tenant_id: this.state.currentCloudData["tenant_id"],
client_id: this.state.currentCloudData["client_id"],
client_secret: this.state.currentCloudData["client_secret"]
}
}
}
// Remove the login_type since it is not needed
if (this.state.currentCloud.name == "azure") {
delete credentials.login_type
}
// If endpoint is new
if (this.state.type == "new") {
ConnectionsActions.newEndpoint({
name: this.state.connectionName,
description: this.state.description,
type: this.state.currentCloud.name,
connection_info: credentials
}, (response) => {
this.setState({
validateEndpoint: response.data.endpoint,
type: "edit",
connection: response.data.endpoint
})
})
this.props.addHandle(this.state.connectionName);
} else { // If editing an endpoint
ConnectionsActions.editEndpoint(this.state.connection, {
name: this.state.connectionName,
description: this.state.description,
connection_info: credentials
}, (response) => {
this.setState({
validateEndpoint: response.data.endpoint,
type: "edit",
connection: response.data.endpoint
})
this.props.updateHandle({
name: this.state.connectionName,
description: this.state.description
})
})
}
}
}
/**
* Handles change `name` property
* @param e
*/
handleChangeName(e) {
this.setState({ connectionName: e.target.value })
}
/**
* Handles change `description` property
* @param e
*/
handleChangeDescription(e) {
this.setState({ description: e.target.value })
}
/**
* Handler to choose the cloud which the endpoint will be assigned to
* @param cloud
*/
chooseCloud(cloud) {
let currentCloudData = {}
if (this.state.currentCloudData !== null) {
currentCloudData = this.state.currentCloudData
}
let requiredFields = []
cloud.endpoint.fields.forEach(field => {
if (typeof currentCloudData[field.name] == "undefined") {
if (field.type == "dropdown") {
let defaultOption = field.options.find(function isDefaultOption(option) { return option.default; })
if (defaultOption) {
currentCloudData[field.name] = defaultOption.value;
} else {
currentCloudData[field.name] = null;
}
} else {
currentCloudData[field.name] = ""
}
}
if (field.required) {
requiredFields.push(field.name)
}
})
this.setState({
currentCloud: cloud,
currentCloudData: currentCloudData,
requiredFields: requiredFields
}, this.setDefaultValues)
}
/**
* Function that goes back from endpoint validation to edit mode
*/
backToEdit() {
this.setState({ validateEndpoint: null })
}
/**
* Handles back operation when adding a new endpoint and want to switch cloud. Resets all previous cloud data.
*/
handleBack() {
this.setState({
currentCloudData: null,
currentCloud: null,
requiredFields: null,
connectionName: "",
description: null
})
}
/**
* Sets default values for cloud fields
*/
setDefaultValues() {
this.state.currentCloud.endpoint.fields.forEach(field => {
let currentCloudData = this.state.currentCloudData
switch (field.type) {
case 'dropdown':
field.options.forEach(option => {
if (option.default === true && typeof currentCloudData[field.name] == "undefined") {
currentCloudData[field.name] = option.value
this.setState({ currentCloudData: currentCloudData })
}
}, this)
break
case 'switch-radio':
field.options.forEach(option => {
if (option.default && typeof currentCloudData[field.name] == "undefined") {
currentCloudData[field.name] = option.value
this.setState({ currentCloudData: currentCloudData })
}
}, this)
break;
case 'text':
if (field.default && typeof currentCloudData[field.name] == "undefined") {
currentCloudData[field.name] = field.default
this.setState({ currentCloudData: currentCloudData })
}
break
default:
break;
}
}, this)
}
/**
* Checks wether the field is valid. Only goes through validation if field is required
* @param field
* @returns {boolean}
*/
isValid(field) {
if (field.required && this.state.cloudFormsSubmitted) {
if (this.state.currentCloudData[field.name]) {
if (this.state.currentCloudData[field.name] && this.state.currentCloudData[field.name].length == 0) {
return false
} else {
return true
}
} else {
return false
}
} else {
return true
}
}
/**
* Handles cancel edit/add endpoint
*/
handleCancel() {
this.props.closeHandle();
}
/**
* Handler to change the endpoint field
* @param e
* @param field
*/
handleCloudFieldChange(e, field) {
let currentCloudData = this.state.currentCloudData
if (field.type == 'dropdown') {
currentCloudData[field.name] = e.value
} else {
currentCloudData[field.name] = e.target.value
}
this.setState({ currentCloudData: currentCloudData })
}
/**
* Renders the cloud list
* @returns {XML}
*/
renderCloudList() {
let clouds = this.state.allClouds.map((cloud, index) => {
let colorType = ""
if (cloud.credentials != null && cloud.credentials.length != 0) {
colorType = ""
}
return (
this.chooseCloud(cloud)}
>
)
}, this)
return (
{clouds}
)
}
/**
* Renders individual cloud fields
* @param field
* @returns {XML}
*/
renderField(field) {
let returnValue
switch (field.type) {
case "text":
returnValue = (