Skip to content

Commit

Permalink
UI changes for blacklisted tables and other minor improvements
Browse files Browse the repository at this point in the history
Keyspace and table selection is now assisted with autocompletion and tag like input.
Progress bars have been redesigned on the repair screen.
Repair ID and incremental have been removed from the compact view
in favor of start time and ETA.

Squash me
  • Loading branch information
adejanovski committed Oct 11, 2017
1 parent cfe3040 commit 7078584
Show file tree
Hide file tree
Showing 9 changed files with 473 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public RepairRunStatus(
new Duration(startTime.toInstant(), endTime.toInstant()).getMillis(), true, false);
}

if (startTime == null || endTime != null) {
if (startTime == null || (endTime != null && endTime.isAfter(startTime))) {
estimatedTimeOfArrival = null;
} else {
if (state == RepairRun.RunState.ERROR
Expand Down
30 changes: 15 additions & 15 deletions src/server/src/main/resources/assets/deps.js

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions src/server/src/main/resources/assets/index.js

Large diffs are not rendered by default.

18 changes: 12 additions & 6 deletions src/server/src/main/resources/assets/repair.js

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions src/server/src/main/resources/assets/schedules.js

Large diffs are not rendered by default.

222 changes: 191 additions & 31 deletions src/ui/app/jsx/repair-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ const repairForm = React.createClass({

return {
addRepairResultMsg: null, clusterNames: [], submitEnabled: false,
clusterName: this.props.currentCluster!="all"?this.props.currentCluster:this.props.clusterNames[0], keyspace: null, tables: null, owner: null, segments: null,
parallism: null, intensity: null, cause: null, incrementalRepair: null, formCollapsed: true, nodes: "", datacenters: "",
nodeList: [], datacenterList: [], clusterStatus: {}, urlPrefix: URL_PREFIX, nodeSuggestions: [], datacenterSuggestions: []

clusterName: this.props.currentCluster!="all"?this.props.currentCluster:this.props.clusterNames[0], keyspace: "", tables: "", owner: null, segments: null,
parallism: null, intensity: null, cause: null, incrementalRepair: null, formCollapsed: true, nodes: "", datacenters: "", blacklistedTables: "",
nodeList: [], datacenterList: [], clusterStatus: {}, urlPrefix: URL_PREFIX, nodeSuggestions: [], datacenterSuggestions: [], tableSuggestions: [],
clusterTables: {}, blacklistSuggestions: [], tableList: [], blacklistList: [], keyspaceList: [], keyspaceSuggestions: [],
blacklistReadOnly: false, tablelistReadOnly: false
};
},

Expand Down Expand Up @@ -62,6 +63,15 @@ const repairForm = React.createClass({
this.component._getSuggestions();
}
});
$.ajax({
url: this.state.urlPrefix + '/cluster/' + encodeURIComponent(clusterName) + '/tables',
method: 'GET',
component: this,
complete: function(data) {
this.component.setState({clusterTables: $.parseJSON(data.responseText)});
this.component._getKeyspaceSuggestions();
}
});
},

_getSuggestions: function() {
Expand All @@ -71,7 +81,15 @@ const repairForm = React.createClass({

var datacenters = Object.keys(this.state.clusterStatus.nodes_status.endpointStates[0].endpoints);
datacenters.sort;
this.state.datacenterSuggestions = datacenters;
this.setState({datacenterSuggestions: datacenters});
},

_getKeyspaceSuggestions: function() {
this.setState({keyspaceSuggestions: Object.keys(this.state.clusterTables)});
},

_getTableSuggestions: function(ks) {
this.setState({tableSuggestions: this.state.clusterTables[ks]});
},

_onAdd: function(e) {
Expand All @@ -87,6 +105,7 @@ const repairForm = React.createClass({
if(this.state.incrementalRepair) repair.incrementalRepair = this.state.incrementalRepair;
if(this.state.nodes) repair.nodes = this.state.nodes;
if(this.state.datacenters) repair.datacenters = this.state.datacenters;
if(this.state.blacklistedTables) repair.blacklistedTables = this.state.blacklistedTables;

// Force incremental repair to FALSE if empty
if(!this.state.incrementalRepair) repair.incrementalRepair = "false";
Expand Down Expand Up @@ -127,15 +146,17 @@ const repairForm = React.createClass({
},

_handleAddition(node) {
let nodes = this.state.nodeList;
if ($.inArray(node, this.state.nodes.split(','))==-1) {
nodes.push({
id: nodes.length + 1,
text: node
});
this.setState({nodeList: nodes, nodes: nodes.map(node => node.text).join(',')});
this._checkValidity();
}
if (this.state.datacenterList.length == 0 && node.length > 1) {
let nodes = this.state.nodeList;
if ($.inArray(node, this.state.nodes.split(','))==-1) {
nodes.push({
id: nodes.length + 1,
text: node
});
this.setState({nodeList: nodes, nodes: nodes.map(node => node.text).join(',')});
this._checkValidity();
}
}
},

_handleDelete(i) {
Expand All @@ -146,15 +167,17 @@ const repairForm = React.createClass({
},

_handleDcAddition(dc) {
let datacenters = this.state.datacenterList;
if ($.inArray(dc, this.state.datacenters.split(','))==-1) {
datacenters.push({
id: datacenters.length + 1,
text: dc
});
this.setState({datacenterList: datacenters, datacenters: datacenters.map(dc => dc.text).join(',')});
this._checkValidity();
}
if (this.state.nodeList.length == 0 && dc.length > 1) {
let datacenters = this.state.datacenterList;
if ($.inArray(dc, this.state.datacenters.split(','))==-1) {
datacenters.push({
id: datacenters.length + 1,
text: dc
});
this.setState({datacenterList: datacenters, datacenters: datacenters.map(dc => dc.text).join(',')});
this._checkValidity();
}
}
},

_handleDcDelete(i) {
Expand Down Expand Up @@ -182,6 +205,108 @@ const repairForm = React.createClass({
})
},

// Blacklist tag list functions
_handleBlacklistAddition(table) {
if (this.state.tableList.length == 0 && table.length > 1) {
let blacklist = this.state.blacklistList;
if ($.inArray(table, this.state.blacklistedTables.split(','))==-1) {
blacklist.push({
id: this._create_UUID(),
text: table
});
this.setState({blacklistList: blacklist, blacklistedTables: blacklist.map(table => table.text).join(',')});
this._checkValidity();
this.setState({tablelistReadOnly: true});
}
}
},

_handleBlacklistDelete(i) {
let blacklist = this.state.blacklistList;
blacklist.splice(i, 1);
this.setState({blacklistList: blacklist, blacklistedTables: blacklist.map(table => table.text).join(',')});
this._checkValidity();
this.setState({tablelistReadOnly: (blacklist.length>0)});
},

_handleBlacklistFilterSuggestions(textInputValue, possibleSuggestionsArray) {
var lowerCaseQuery = textInputValue.toLowerCase();
let blacklist = this.state.blacklistedTables;
let tables = this.state.tables;

return possibleSuggestionsArray.filter(function(suggestion) {
return suggestion.toLowerCase().includes(lowerCaseQuery) && $.inArray(suggestion, blacklist.split(','))==-1
&& $.inArray(suggestion, tables.split(','))==-1;
})
},

// Tables tag list functions
_handleTableAddition(table) {
if (this.state.blacklistList.length == 0 && table.length > 1) {
let tables = this.state.tableList;
if ($.inArray(table, this.state.tables.split(','))==-1) {
tables.push({
id: this._create_UUID(),
text: table
});
this.setState({tableList: tables, tables: tables.map(table => table.text).join(',')});
this._checkValidity();
this.setState({blacklistReadOnly: true});
}
}
},

_handleTableDelete(i) {
let tables = this.state.tableList;
tables.splice(i, 1);
this.setState({tableList: tables, tables: tables.map(table => table.text).join(',')});
this._checkValidity();
this.setState({blacklistReadOnly: (tables.length > 0)});
},

// Keyspace tag list functions
_handleKeyspaceAddition(ks) {
let keyspaces = this.state.keyspaceList;
if (keyspaces.length==0) {
if ($.inArray(ks, this.state.keyspace.split(','))==-1) {
keyspaces.push({
id: this._create_UUID(),
text: ks
});
this.setState({keyspaceList: keyspaces, keyspace: ks, keyspaces: keyspaces.map(ks => ks.text).join(',')});
this._checkValidity();
this._getTableSuggestions(ks);
}
}
},

_handleKeyspaceDelete(i) {
let keyspaces = this.state.keyspaceList;
keyspaces.splice(i, 1);
this.setState({keyspaceList: keyspaces, keyspace: "", keyspaces: keyspaces.map(ks => ks.text).join(',')});
this._checkValidity();
this._getTableSuggestions("");
},

_handleKeyspaceFilterSuggestions(textInputValue, possibleSuggestionsArray) {
var lowerCaseQuery = textInputValue.toLowerCase();
let keyspaces = this.state.keyspaceList;

return possibleSuggestionsArray.filter(function(suggestion) {
return suggestion.toLowerCase().includes(lowerCaseQuery) && keyspaces.length==0;
})
},

_create_UUID(){
var dt = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (dt + Math.random()*16)%16 | 0;
dt = Math.floor(dt/16);
return (c=='x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
},

render: function() {

let addMsg = null;
Expand Down Expand Up @@ -209,17 +334,50 @@ const repairForm = React.createClass({
</div>

<div className="form-group">
<label htmlFor="in_keyspace" className="col-sm-3 control-label">Keyspace*</label>
<div className="col-sm-9 col-md-7 col-lg-5">
<input type="text" required className="form-control" value={this.state.keyspace}
onChange={this._handleChange} id="in_keyspace" placeholder="name of keyspace to repair"/>
<label htmlFor="in_keyspace" className="col-sm-3 control-label">Keyspace*</label>
<div className="col-sm-9 col-md-7 col-lg-5">
<ReactTags id={'in_keyspace'} tags={this.state.keyspaceList}
suggestions={this.state.keyspaceSuggestions}
labelField={'text'} handleAddition={this._handleKeyspaceAddition}
handleInputBlur={this._handleKeyspaceAddition}
handleDelete={this._handleKeyspaceDelete}
placeholder={'Add a keyspace'}
handleFilterSuggestions={this._handleKeyspaceFilterSuggestions}
classNames={{
tagInputField: 'form-control'
}}/>
</div>
</div>
<div className="form-group">
<label htmlFor="in_tables" className="col-sm-3 control-label">Tables</label>
<div className="col-sm-9 col-md-7 col-lg-5">
<input type="text" className="form-control" value={this.state.tables}
onChange={this._handleChange} id="in_tables" placeholder="table1, table2, table3"/>
<label htmlFor="in_tables" className="col-sm-3 control-label">Tables</label>
<div className="col-sm-9 col-md-7 col-lg-5">
<ReactTags id={'in_tables'} tags={this.state.tableList}
suggestions={this.state.tableSuggestions}
labelField={'text'} handleAddition={this._handleTableAddition}
handleInputBlur={this._handleTableAddition}
handleDelete={this._handleTableDelete}
readOnly={this.state.tablelistReadOnly}
placeholder={'Add a table (optional)'}
handleFilterSuggestions={this._handleBlacklistFilterSuggestions}
classNames={{
tagInputField: 'form-control'
}}/>
</div>
</div>
<div className="form-group">
<label htmlFor="in_blacklist" className="col-sm-3 control-label">Blacklist</label>
<div className="col-sm-9 col-md-7 col-lg-5">
<ReactTags id={'in_blacklist'} tags={this.state.blacklistList}
suggestions={this.state.tableSuggestions}
labelField={'text'} handleAddition={this._handleBlacklistAddition}
handleInputBlur={this._handleBlacklistAddition}
handleDelete={this._handleBlacklistDelete}
readOnly={this.state.blacklistReadOnly}
placeholder={'Add a table (optional)'}
handleFilterSuggestions={this._handleBlacklistFilterSuggestions}
classNames={{
tagInputField: 'form-control'
}}/>
</div>
</div>
<div className="form-group">
Expand All @@ -228,6 +386,7 @@ const repairForm = React.createClass({
<ReactTags id={'in_nodes'} tags={this.state.nodeList}
suggestions={this.state.nodeSuggestions}
labelField={'text'} handleAddition={this._handleAddition}
handleInputBlur={this._handleAddition}
handleDelete={this._handleDelete}
placeholder={'Add a node (optional)'}
handleFilterSuggestions={this._handleNodeFilterSuggestions}
Expand All @@ -242,6 +401,7 @@ const repairForm = React.createClass({
<ReactTags id={'in_datacenters'} tags={this.state.datacenterList}
suggestions={this.state.datacenterSuggestions}
labelField={'text'} handleAddition={this._handleDcAddition}
handleInputBlur={this._handleDcAddition}
handleDelete={this._handleDcDelete}
placeholder={'Add a datacenter (optional)'}
handleFilterSuggestions={this._handleDcFilterSuggestions}
Expand Down
Loading

0 comments on commit 7078584

Please sign in to comment.