1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 16:51:11 +03:00

maintain correct page counts on running jobs

This commit is contained in:
Jake McDermott 2018-05-15 23:37:32 -04:00
parent 503668141b
commit 11e7dcd0dc
No known key found for this signature in database
GPG Key ID: 3B02CAD476EECB35
4 changed files with 187 additions and 81 deletions

View File

@ -0,0 +1,132 @@
const PAGE_LIMIT = 5;
const PAGE_SIZE = 50;
const BASE_PARAMS = {
order_by: 'start_line',
page_size: PAGE_SIZE,
};
const merge = (...objs) => _.merge({}, ...objs);
const getInitialState = params => ({
results: [],
count: 0,
previous: 1,
page: 1,
next: 1,
last: 1,
params: merge(BASE_PARAMS, params),
});
function JobEventsApiService ($http, $q) {
this.init = (endpoint, params) => {
this.keys = [];
this.cache = {};
this.pageSizes = {};
this.endpoint = endpoint;
this.state = getInitialState(params);
};
this.getLastPage = count => Math.ceil(count / this.state.params.page_size);
this.fetch = () => {
delete this.cache;
delete this.keys;
delete this.pageSizes;
this.cache = {};
this.keys = [];
this.pageSizes = {};
return this.getPage(1).then(() => this);
};
this.getPage = number => {
if (number < 1 || number > this.state.last) {
return $q.resolve();
}
if (this.cache[number]) {
if (this.pageSizes[number] === PAGE_SIZE) {
return this.cache[number];
}
delete this.pageSizes[number];
delete this.cache[number];
this.keys.splice(this.keys.indexOf(number));
}
const { params } = this.state;
delete params.page;
params.page = number;
const promise = $http.get(this.endpoint, { params })
.then(({ data }) => {
const { results, count } = data;
this.state.results = results;
this.state.count = count;
this.state.page = number;
this.state.last = this.getLastPage(count);
this.state.previous = Math.max(1, number - 1);
this.state.next = Math.min(this.state.last, number + 1);
this.pageSizes[number] = results.length;
return { results, page: number };
});
this.cache[number] = promise;
this.keys.push(number);
if (this.keys.length > PAGE_LIMIT) {
delete this.cache[this.keys.shift()];
}
return promise;
};
this.first = () => this.getPage(1);
this.next = () => this.getPage(this.state.next);
this.previous = () => this.getPage(this.state.previous);
this.last = () => {
const params = merge({}, this.state.params);
delete params.page;
delete params.order_by;
params.page = 1;
params.order_by = '-start_line';
const promise = $http.get(this.endpoint, { params })
.then(({ data }) => {
const { results, count } = data;
const lastPage = this.getLastPage(count);
results.reverse();
const shifted = results.splice(count % PAGE_SIZE);
this.state.results = shifted;
this.state.count = count;
this.state.page = lastPage;
this.state.next = lastPage;
this.state.last = lastPage;
this.state.previous = Math.max(1, this.state.page - 1);
return { results: shifted, page: lastPage };
});
return promise;
};
}
JobEventsApiService.$inject = [
'$http',
'$q'
];
export default JobEventsApiService;

View File

@ -7,7 +7,6 @@ let resource;
let scroll;
let engine;
let status;
let $http;
let vm;
let streaming;
@ -23,7 +22,6 @@ function JobsIndexController (
_$compile_,
_$q_,
_status_,
_$http_,
) {
vm = this || {};
@ -37,7 +35,6 @@ function JobsIndexController (
render = _render_;
engine = _engine_;
status = _status_;
$http = _$http_;
// Development helper(s)
vm.clear = devClear;
@ -128,37 +125,20 @@ function handleJobEvent (data) {
}
function attachToRunningJob () {
const target = `${resource.model.get('url')}${resource.related}/`;
const params = { order_by: '-created', page_size: resource.page.size };
if (!status.state.running) {
return $q.resolve();
}
scroll.pause();
return render.clear()
.then(() => $http.get(target, { params }))
.then(res => {
const { results } = res.data;
const minLine = 1 + Math.max(...results.map(event => event.end_line));
const maxCount = Math.max(...results.map(event => event.counter));
const lastPage = resource.model.updateCount(maxCount);
page.emptyCache(lastPage);
page.addPage(lastPage, [], true);
engine.setMinLine(minLine);
if (resource.model.page.current === lastPage) {
return page.last()
.then(events => {
if (!events) {
return $q.resolve();
}
return append(results);
})
.then(() => {
scroll.setScrollPosition(scroll.getScrollHeight());
scroll.resume();
const minLine = 1 + Math.max(...events.map(event => event.end_line));
return $q.resolve();
return render.clear()
.then(() => engine.setMinLine(minLine));
});
}
@ -293,11 +273,11 @@ function scrollEnd () {
}
return render.clear()
.then(() => append(events))
.then(() => {
scroll.setScrollPosition(scroll.getScrollHeight());
scroll.resume();
});
.then(() => append(events));
})
.then(() => {
scroll.setScrollPosition(scroll.getScrollHeight());
scroll.resume();
});
}
@ -379,7 +359,6 @@ JobsIndexController.$inject = [
'$compile',
'$q',
'JobStatusService',
'$http',
];
module.exports = JobsIndexController;

View File

@ -9,6 +9,7 @@ import ScrollService from '~features/output/scroll.service';
import EngineService from '~features/output/engine.service';
import StatusService from '~features/output/status.service';
import MessageService from '~features/output/message.service';
import EventsApiService from '~features/output/api.events.service';
import LegacyRedirect from '~features/output/legacy.route';
import DetailsComponent from '~features/output/details.component';
@ -35,7 +36,8 @@ function resolveResource (
InventoryUpdate,
$stateParams,
qs,
Wait
Wait,
eventsApi,
) {
const { id, type, handleErrors } = $stateParams;
const { job_event_search } = $stateParams; // eslint-disable-line camelcase
@ -86,23 +88,29 @@ function resolveResource (
Object.assign(config.params, query);
}
let model;
Wait('start');
const resourcePromise = new Resource(['get', 'options'], [id, id])
.then(model => {
const promises = [model.getStats()];
.then(job => {
const endpoint = `${job.get('url')}${related}/`;
eventsApi.init(endpoint, config.params);
if (model.has('related.labels')) {
promises.push(model.extend('get', 'labels'));
const promises = [job.getStats(), eventsApi.fetch()];
if (job.has('related.labels')) {
promises.push(job.extend('get', 'labels'));
}
promises.push(model.extend('get', related, config));
model = job;
return Promise.all(promises);
})
.then(([stats, model]) => ({
.then(([stats, events]) => ({
id,
type,
stats,
model,
events,
related,
ws: {
events: `${WS_PREFIX}-${key}-${id}`,
@ -217,6 +225,7 @@ function JobsRun ($stateRegistry, strings) {
'$stateParams',
'QuerySet',
'Wait',
'JobEventsApiService',
resolveResource
],
breadcrumbLabel: [
@ -246,6 +255,7 @@ angular
.service('JobEventEngine', EngineService)
.service('JobStatusService', StatusService)
.service('JobMessageService', MessageService)
.service('JobEventsApiService', EventsApiService)
.component('atJobSearch', SearchComponent)
.component('atJobStats', StatsComponent)
.component('atJobDetails', DetailsComponent)

View File

@ -1,6 +1,7 @@
function JobPageService ($q) {
this.init = ({ resource }) => {
this.resource = resource;
this.api = this.resource.events;
this.page = {
limit: this.resource.page.pageLimit,
@ -125,8 +126,9 @@ function JobPageService ($q) {
number = number || reference.state.current;
reference.state.first = number;
reference.state.last = number;
reference.state.current = number;
reference.state.last = number;
reference.cache.splice(0, reference.cache.length);
};
@ -203,9 +205,9 @@ function JobPageService ($q) {
this.next = () => {
const reference = this.getActiveReference();
const config = this.buildRequestConfig(reference.state.last + 1);
const number = reference.state.last + 1;
return this.resource.model.goToPage(config)
return this.api.getPage(number)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
@ -219,9 +221,8 @@ function JobPageService ($q) {
this.previous = () => {
const reference = this.getActiveReference();
const config = this.buildRequestConfig(reference.state.first - 1);
return this.resource.model.goToPage(config)
return this.api.getPage(reference.state.first - 1)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
@ -233,45 +234,29 @@ function JobPageService ($q) {
});
};
this.last = () => {
const config = this.buildRequestConfig('last');
this.last = () => this.api.last()
.then(data => {
if (!data || !data.results || !data.results.length > 0) {
return $q.resolve();
}
return this.resource.model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
}
this.emptyCache(data.page);
this.addPage(data.page, [], true);
this.emptyCache(data.page);
this.addPage(data.page, [], true);
return data.results;
});
return data.results;
});
};
this.first = () => this.api.first()
.then(data => {
if (!data || !data.results) {
return $q.resolve();
}
this.first = () => {
const config = this.buildRequestConfig('first');
this.emptyCache(data.page);
this.addPage(data.page, [], false);
return this.resource.model.goToPage(config)
.then(data => {
if (!data || !data.results) {
return $q.resolve();
}
this.emptyCache(data.page);
this.addPage(data.page, [], false);
return data.results;
});
};
this.buildRequestConfig = number => ({
page: number,
related: this.resource.related,
params: {
order_by: 'start_line'
}
});
return data.results;
});
this.getActiveReference = () => (this.isBookmarkSet() ?
this.getReference(true) : this.getReference());