Files
awx/awx/ui/client/features/output/index.controller.js
2018-04-05 02:46:56 -04:00

413 lines
9.0 KiB
JavaScript

const JOB_START = 'playbook_on_start';
const JOB_END = 'playbook_on_stats';
let vm;
let $compile;
let $scope;
let $q;
let page;
let render;
let scroll;
let resource;
let $state;
let qs;
let chain;
function JobsIndexController (
_resource_,
_page_,
_scroll_,
_render_,
_$scope_,
_$compile_,
_$q_,
_$state_,
_qs_,
) {
vm = this || {};
$compile = _$compile_;
$scope = _$scope_;
$q = _$q_;
resource = _resource_;
page = _page_;
scroll = _scroll_;
render = _render_;
// Development helper(s)
vm.clear = devClear;
// Stdout Navigation
vm.scroll = {
showBackToTop: false,
home: scrollHome,
end: scrollEnd,
down: scrollPageDown,
up: scrollPageUp
};
// Expand/collapse
vm.toggle = toggle;
vm.expand = expand;
vm.isExpanded = true;
// Real-time (active between JOB_START and JOB_END events only)
vm.stream = {
active: false,
rendering: false,
paused: false
};
const stream = false; // TODO: Set in route
chain = $q.resolve();
// search
$state = _$state_;
qs = _qs_;
vm.searchValue = '';
vm.searchRejected = null;
vm.searchKey = false;
vm.searchKeyExamples = searchKeyExamples;
vm.searchKeyFields = searchKeyFields;
vm.clearSearch = clearSearch;
vm.search = search;
vm.toggleSearchKey = toggleSearchKey;
vm.removeSearchTag = removeSearchTag;
vm.searchTags = getSearchTags(getCurrentQueryset());
render.requestAnimationFrame(() => init());
}
function init (stream) {
page.init(resource);
render.init({
get: () => resource.model.get(`related.${resource.related}.results`),
compile: html => $compile(html)($scope)
});
scroll.init({
isAtRest: scrollIsAtRest,
previous,
next
});
if (stream) {
$scope.$on(resource.ws.namespace, process);
} else {
next();
}
}
function process (scope, data) {
chain = chain.then(() => {
if (data.event === JOB_START) {
vm.stream.active = true;
scroll.lock();
} else if (data.event === JOB_END) {
vm.stream.active = false;
}
const pageAdded = page.addToBuffer(data);
if (pageAdded && !scroll.isLocked()) {
vm.stream.paused = true;
}
if (vm.stream.paused && scroll.isLocked()) {
vm.stream.paused = false;
}
if (vm.stream.rendering || vm.stream.paused) {
return;
}
const events = page.emptyBuffer();
return renderStream(events);
})
}
function renderStream (events) {
vm.stream.rendering = true;
return shift()
.then(() => append(events, true))
.then(() => {
if (scroll.isLocked()) {
scroll.setScrollPosition(scroll.getScrollHeight());
}
if (!vm.stream.active) {
const buffer = page.emptyBuffer();
if (buffer.length) {
return renderStream(buffer);
} else {
vm.stream.rendering = false;
scroll.unlock();
}
} else {
vm.stream.rendering = false;
}
});
}
function devClear () {
init(true);
render.clear();
vm.stream = {
active: false,
rendering: false,
paused: false
};
}
function next () {
return page.next()
.then(events => {
if (!events) {
return;
}
return shift()
.then(() => append(events));
});
}
function previous () {
let initialPosition = scroll.getScrollPosition();
let postPopHeight;
return page.previous()
.then(events => {
if (!events) {
return;
}
return pop()
.then(() => {
postPopHeight = scroll.getScrollHeight();
return prepend(events);
})
.then(() => {
const currentHeight = scroll.getScrollHeight();
scroll.setScrollPosition(currentHeight - postPopHeight + initialPosition);
});
});
}
function append (events, stream) {
return render.append(events)
.then(count => {
page.updateLineCount(count, stream);
});
}
function prepend (events) {
return render.prepend(events)
.then(count => {
page.updateLineCount(count);
});
}
function pop () {
if (!page.isOverCapacity()) {
return $q.resolve();
}
const lines = page.trim();
return render.pop(lines);
}
function shift () {
if (!page.isOverCapacity()) {
return $q.resolve();
}
const lines = page.trim(true);
return render.shift(lines);
}
function scrollHome () {
scroll.pause();
return page.first()
.then(events => {
if (!events) {
return;
}
return render.clear()
.then(() => prepend(events))
.then(() => {
scroll.resetScrollPosition();
scroll.resume();
});
});
}
function scrollEnd () {
if (scroll.isLocked()) {
page.setBookmark();
scroll.unlock();
return;
} else if (!scroll.isLocked() && vm.stream.active) {
page.removeBookmark();
scroll.lock();
return;
}
scroll.pause();
return page.last()
.then(events => {
if (!events) {
return;
}
return render.clear()
.then(() => append(events))
.then(() => {
scroll.setScrollPosition(scroll.getScrollHeight());
scroll.resume();
});
});
}
function scrollPageUp () {
scroll.pageUp();
}
function scrollPageDown () {
scroll.pageDown();
}
function scrollIsAtRest (isAtRest) {
vm.scroll.showBackToTop = !isAtRest;
}
function expand () {
vm.toggle(parent, true);
}
function showHostDetails (id) {
jobEvent.request('get', id)
.then(() => {
const title = jobEvent.get('host_name');
vm.host = {
menu: true,
stdout: jobEvent.get('stdout')
};
$scope.jobs.modal.show(title);
});
}
function toggle (uuid, menu) {
const lines = $(`.child-of-${uuid}`);
let icon = $(`#${uuid} .at-Stdout-toggle > i`);
if (menu || record[uuid].level === 1) {
vm.isExpanded = !vm.isExpanded;
}
if (record[uuid].children) {
icon = icon.add($(`#${record[uuid].children.join(', #')}`).find('.at-Stdout-toggle > i'));
}
if (icon.hasClass('fa-angle-down')) {
icon.addClass('fa-angle-right');
icon.removeClass('fa-angle-down');
lines.addClass('hidden');
} else {
icon.addClass('fa-angle-down');
icon.removeClass('fa-angle-right');
lines.removeClass('hidden');
}
//
// Search
//
const searchReloadOptions = { reload: true, inherit: false };
const searchKeyExamples = ['id:>1', 'task:set', 'created:>=2000-01-01'];
const searchKeyFields = ['changed', 'failed', 'host_name', 'stdout', 'task', 'role', 'playbook', 'play'];
function toggleSearchKey () {
vm.searchKey = !vm.searchKey;
}
function getCurrentQueryset() {
const { job_event_search } = $state.params;
return qs.decodeArr(job_event_search);
}
function getSearchTags (queryset) {
return qs.createSearchTagsFromQueryset(queryset)
.filter(tag => !tag.startsWith('event'))
.filter(tag => !tag.startsWith('-event'))
.filter(tag => !tag.startsWith('page_size'))
.filter(tag => !tag.startsWith('order_by'));
}
function removeSearchTag (index) {
const searchTerm = vm.searchTags[index];
const currentQueryset = getCurrentQueryset();
const modifiedQueryset = qs.removeTermsFromQueryset(currentQueryset, searchTerm);
vm.searchTags = getSearchTags(modifiedQueryset);
$state.params.job_event_search = qs.encodeArr(modifiedQueryset);
$state.transitionTo($state.current, $state.params, searchReloadOptions);
}
function search () {
const searchInputQueryset = qs.getSearchInputQueryset(vm.searchValue);
const currentQueryset = getCurrentQueryset();
const modifiedQueryset = qs.mergeQueryset(currentQueryset, searchInputQueryset);
vm.searchTags = getSearchTags(modifiedQueryset);
$state.params.job_event_search = qs.encodeArr(modifiedQueryset);
$state.transitionTo($state.current, $state.params, searchReloadOptions);
}
function clearSearch () {
vm.searchTags = [];
$state.params.job_event_search = '';
$state.transitionTo($state.current, $state.params, searchReloadOptions);
}
JobsIndexController.$inject = [
'resource',
'JobPageService',
'JobScrollService',
'JobRenderService',
'$scope',
'$compile',
'$q',
'$state',
'QuerySet',
];
module.exports = JobsIndexController;