UI - console refresh (#4679)
* add router service polyfill * add refresh command * move async code into ember-concurrency task and implement refresh that way * use ember-concurrency derived state to show a loading spinner when the task is running * scroll after appending to log too
This commit is contained in:
parent
36db9818ab
commit
a2fb9ae331
|
@ -11,9 +11,6 @@ export default Ember.Component.extend({
|
|||
value: null,
|
||||
isFullscreen: null,
|
||||
|
||||
didRender() {
|
||||
this.element.scrollIntoView();
|
||||
},
|
||||
actions: {
|
||||
handleKeyUp(event) {
|
||||
const keyCode = event.keyCode;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
import { task } from 'ember-concurrency';
|
||||
import {
|
||||
parseCommand,
|
||||
extractDataAndFlags,
|
||||
|
@ -8,22 +9,31 @@ import {
|
|||
executeUICommand,
|
||||
} from 'vault/lib/console-helpers';
|
||||
|
||||
const { inject, computed } = Ember;
|
||||
const { inject, computed, getOwner, run } = Ember;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: 'console-ui-panel-scroller',
|
||||
classNameBindings: ['isFullscreen:fullscreen'],
|
||||
isFullscreen: false,
|
||||
console: inject.service(),
|
||||
router: inject.service(),
|
||||
inputValue: null,
|
||||
log: computed.alias('console.log'),
|
||||
|
||||
logAndOutput(command, logContent) {
|
||||
this.set('inputValue', '');
|
||||
this.get('console').logAndOutput(command, logContent);
|
||||
didRender() {
|
||||
this._super(...arguments);
|
||||
this.scrollToBottom();
|
||||
},
|
||||
|
||||
executeCommand(command, shouldThrow = false) {
|
||||
logAndOutput(command, logContent) {
|
||||
this.get('console').logAndOutput(command, logContent);
|
||||
run.schedule('afterRender', () => this.scrollToBottom());
|
||||
},
|
||||
|
||||
isRunning: computed.or('executeCommand.isRunning', 'refreshRoute.isRunning'),
|
||||
|
||||
executeCommand: task(function*(command, shouldThrow = false) {
|
||||
this.set('inputValue', '');
|
||||
let service = this.get('console');
|
||||
let serviceArgs;
|
||||
|
||||
|
@ -32,7 +42,8 @@ export default Ember.Component.extend({
|
|||
command,
|
||||
args => this.logAndOutput(args),
|
||||
args => service.clearLog(args),
|
||||
() => this.toggleProperty('isFullscreen')
|
||||
() => this.toggleProperty('isFullscreen'),
|
||||
() => this.get('refreshRoute').perform()
|
||||
)
|
||||
) {
|
||||
return;
|
||||
|
@ -61,16 +72,26 @@ export default Ember.Component.extend({
|
|||
this.logAndOutput(command, inputError);
|
||||
return;
|
||||
}
|
||||
let serviceFn = service[method];
|
||||
serviceFn
|
||||
.call(service, path, data, flags.wrapTTL)
|
||||
.then(resp => {
|
||||
this.logAndOutput(command, logFromResponse(resp, path, method, flags));
|
||||
})
|
||||
.catch(error => {
|
||||
this.logAndOutput(command, logFromError(error, path, method));
|
||||
});
|
||||
},
|
||||
try {
|
||||
let resp = yield service[method].call(service, path, data, flags.wrapTTL);
|
||||
this.logAndOutput(command, logFromResponse(resp, path, method, flags));
|
||||
} catch(error) {
|
||||
this.logAndOutput(command, logFromError(error, path, method));
|
||||
}
|
||||
}),
|
||||
|
||||
refreshRoute:task(function*() {
|
||||
let owner = getOwner(this);
|
||||
let routeName = this.get('router.currentRouteName');
|
||||
let route = owner.lookup(`route:${routeName}`);
|
||||
|
||||
try {
|
||||
yield route.refresh();
|
||||
this.logAndOutput(null, { type: 'success', content: 'The current screen has been refreshed!' });
|
||||
} catch (error) {
|
||||
this.logAndOutput(null, { type: 'error', content: 'The was a problem refreshing the current screen.' });
|
||||
}
|
||||
}),
|
||||
|
||||
shiftCommandIndex(keyCode) {
|
||||
this.get('console').shiftCommandIndex(keyCode, val => {
|
||||
|
@ -78,12 +99,16 @@ export default Ember.Component.extend({
|
|||
});
|
||||
},
|
||||
|
||||
scrollToBottom() {
|
||||
this.element.scrollTop = this.element.scrollHeight;
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleFullscreen() {
|
||||
this.toggleProperty('isFullscreen');
|
||||
},
|
||||
executeCommand(val) {
|
||||
this.executeCommand(val, true);
|
||||
this.get('executeCommand').perform(val, true);
|
||||
},
|
||||
shiftCommandIndex(direction) {
|
||||
this.shiftCommandIndex(direction);
|
||||
|
|
|
@ -2,7 +2,7 @@ import keys from 'vault/lib/keycodes';
|
|||
import argTokenizer from 'yargs-parser-tokenizer';
|
||||
|
||||
const supportedCommands = ['read', 'write', 'list', 'delete'];
|
||||
const uiCommands = ['clearall', 'clear', 'fullscreen'];
|
||||
const uiCommands = ['clearall', 'clear', 'fullscreen', 'refresh'];
|
||||
|
||||
export function extractDataAndFlags(data, flags) {
|
||||
return data.concat(flags).reduce((accumulator, val) => {
|
||||
|
@ -29,7 +29,7 @@ export function extractDataAndFlags(data, flags) {
|
|||
}, { data: {}, flags: {} });
|
||||
}
|
||||
|
||||
export function executeUICommand(command, logAndOutput, clearLog, toggleFullscreen) {
|
||||
export function executeUICommand(command, logAndOutput, clearLog, toggleFullscreen, refreshFn) {
|
||||
const isUICommand = uiCommands.includes(command);
|
||||
if (isUICommand) {
|
||||
logAndOutput(command);
|
||||
|
@ -44,6 +44,9 @@ export function executeUICommand(command, logAndOutput, clearLog, toggleFullscre
|
|||
case 'fullscreen':
|
||||
toggleFullscreen();
|
||||
break;
|
||||
case 'refresh':
|
||||
refreshFn();
|
||||
break;
|
||||
}
|
||||
|
||||
return isUICommand;
|
||||
|
|
|
@ -27,10 +27,10 @@ export default IdentityModel.extend({
|
|||
readOnly: true,
|
||||
}),
|
||||
numMemberEntities: attr('number', {
|
||||
readOnly: true
|
||||
readOnly: true,
|
||||
}),
|
||||
numParentGroups: attr('number', {
|
||||
readOnly: true
|
||||
readOnly: true,
|
||||
}),
|
||||
metadata: attr('object', {
|
||||
editType: 'kv',
|
||||
|
|
|
@ -19,9 +19,8 @@ export default ApplicationSerializer.extend({
|
|||
return model;
|
||||
});
|
||||
delete payload.data.key_info;
|
||||
return list.sort((a,b) => {
|
||||
return list.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -61,8 +61,10 @@ export default Service.extend({
|
|||
|
||||
logAndOutput(command, logContent) {
|
||||
let log = this.get('log');
|
||||
log.pushObject({ type: 'command', content: command });
|
||||
this.set('commandIndex', null);
|
||||
if (command) {
|
||||
log.pushObject({ type: 'command', content: command });
|
||||
this.set('commandIndex', null);
|
||||
}
|
||||
if (logContent) {
|
||||
log.pushObject(logContent);
|
||||
}
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
|
||||
input {
|
||||
background-color: rgba($black, 0.5);
|
||||
border: 0;
|
||||
|
@ -152,3 +151,18 @@ header .navbar-sections {
|
|||
transform: translate3d(0, 0, 0);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.console-spinner.control {
|
||||
height: 21px;
|
||||
width: 21px;
|
||||
transform: scale(.75);
|
||||
transform-origin: center;
|
||||
&::after {
|
||||
height: auto;
|
||||
width: auto;
|
||||
right: .25rem;
|
||||
left: .25rem;
|
||||
top: .25rem;
|
||||
bottom: .25rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</div>
|
||||
<div class="navbar-end is-divider-list is-flex">
|
||||
<div class="navbar-item">
|
||||
<button type="button" class="button is-transparent" {{action 'toggleConsole'}}>
|
||||
<button type="button" class="button is-transparent" {{action 'toggleConsole'}} data-test-console-toggle>
|
||||
{{#if consoleOpen}}
|
||||
{{i-con glyph="console-active" size=24}}
|
||||
{{i-con glyph="chevron-up" aria-hidden="true" size=8 class="has-text-white auto-width is-status-chevron"}}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
{{i-con glyph="chevron-right" size=12}}
|
||||
{{#if isRunning}}
|
||||
<div class="control console-spinner is-loading"></div>
|
||||
{{else}}
|
||||
{{i-con glyph="chevron-right" size=12 }}
|
||||
{{/if}}
|
||||
<input onkeyup={{action 'handleKeyUp'}} value={{value}} autocomplete="off" spellcheck="false" />
|
||||
{{#tool-tip horizontalPosition="auto-right" verticalPosition=(if isFullscreen "above" "below") as |d|}}
|
||||
{{#d.trigger tagName="button" type="button" class=(concat "button is-compact" (if isFullscreen " active")) click=(action "fullscreen") data-test-tool-tip-trigger=true}}
|
||||
|
|
|
@ -12,5 +12,6 @@ Web CLI Commands:
|
|||
fullscreen Toggle fullscreen display
|
||||
clear Clear output from the log
|
||||
clearall Clear output and command history
|
||||
refresh Refresh the data on the current screen under the CLI window
|
||||
</pre>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
{{console/output-log log=log}}
|
||||
{{console/command-input
|
||||
isFullscreen=isFullscreen
|
||||
isRunning=isRunning
|
||||
value=inputValue
|
||||
onValueUpdate=(action (mut inputValue))
|
||||
onFullscreen=(action 'toggleFullscreen')
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
"bulma": "^0.5.2",
|
||||
"bulma-switch": "^0.0.1",
|
||||
"codemirror": "5.15.2",
|
||||
"columnify": "^1.5.4",
|
||||
"cool-checkboxes-for-bulma.io": "^1.1.0",
|
||||
"ember-ajax": "^3.0.0",
|
||||
"ember-api-actions": "^0.1.8",
|
||||
|
@ -83,6 +84,7 @@
|
|||
"ember-qunit-assert-helpers": "^0.1.3",
|
||||
"ember-radio-button": "^1.1.1",
|
||||
"ember-resolver": "^4.0.0",
|
||||
"ember-router-service-polyfill": "^1.0.2",
|
||||
"ember-sinon": "^1.0.1",
|
||||
"ember-source": "~2.14.0",
|
||||
"ember-test-selectors": "^0.3.6",
|
||||
|
@ -96,7 +98,6 @@
|
|||
"qunit-dom": "^0.6.2",
|
||||
"string.prototype.startswith": "mathiasbynens/String.prototype.startsWith",
|
||||
"text-encoder-lite": "1.0.0",
|
||||
"columnify": "^1.5.4",
|
||||
"yargs-parser": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { test } from 'qunit';
|
||||
import moduleForAcceptance from 'vault/tests/helpers/module-for-acceptance';
|
||||
import enginesPage from 'vault/tests/pages/secrets/backends';
|
||||
|
||||
moduleForAcceptance('Acceptance | console', {
|
||||
beforeEach() {
|
||||
return authLogin();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
test('refresh reloads the current route\'s data', function(assert) {
|
||||
let numEngines;
|
||||
enginesPage.visit();
|
||||
andThen(() => {
|
||||
numEngines = enginesPage.rows().count;
|
||||
enginesPage.consoleToggle();
|
||||
let now = Date.now();
|
||||
[1, 2, 3].forEach(num => {
|
||||
let inputString = `write sys/mounts/${now+num} type=kv`;
|
||||
enginesPage.console.consoleInput(inputString);
|
||||
enginesPage.console.enter();
|
||||
});
|
||||
});
|
||||
andThen(() => {
|
||||
enginesPage.console.consoleInput('refresh');
|
||||
enginesPage.console.enter();
|
||||
});
|
||||
andThen(() => {
|
||||
assert.equal(enginesPage.rows().count, numEngines + 3, 'new engines were added to the page');
|
||||
});
|
||||
});
|
|
@ -1,6 +1,9 @@
|
|||
import { create, visitable, collection, clickable, text } from 'ember-cli-page-object';
|
||||
import uiPanel from 'vault/tests/pages/components/console/ui-panel';
|
||||
|
||||
export default create({
|
||||
console: uiPanel,
|
||||
consoleToggle: clickable('[data-test-console-toggle]'),
|
||||
visit: visitable('/vault/secrets'),
|
||||
rows: collection({
|
||||
itemScope: '[data-test-secret-backend-row]',
|
||||
|
|
|
@ -3451,6 +3451,12 @@ ember-router-generator@^1.0.0:
|
|||
dependencies:
|
||||
recast "^0.11.3"
|
||||
|
||||
ember-router-service-polyfill@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/ember-router-service-polyfill/-/ember-router-service-polyfill-1.0.2.tgz#6e5565f196fa7045cbe06a6fab861f9e766fe62a"
|
||||
dependencies:
|
||||
ember-cli-babel "^6.8.2"
|
||||
|
||||
ember-runtime-enumerable-includes-polyfill@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/ember-runtime-enumerable-includes-polyfill/-/ember-runtime-enumerable-includes-polyfill-1.0.4.tgz#16a7612e347a2edf07da8b2f2f09dbfee70deba0"
|
||||
|
|
Loading…
Reference in New Issue