Skip to content

Commit

Permalink
feat: cleanup task executions older than x days
Browse files Browse the repository at this point in the history
Resolves #1949
  • Loading branch information
klopfdreh authored Sep 6, 2023
1 parent 215c316 commit 85df243
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 58 deletions.
47 changes: 43 additions & 4 deletions ui/src/app/manage/tools/cleanup/cleanup.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
<clr-modal [(clrModalOpen)]="isOpen" [clrModalClosable]="!isRunning">
<h3 class="modal-title">{{ 'tools.modal.cleanUp.title' | translate }}</h3>
<div class="modal-body" *ngIf="!isRunning && !loading">
<p
[innerHTML]="'tools.modal.cleanUp.content' | translate: {count: status === 'all' ? count.all : count.completed}"
></p>
<div class="modal-body" *ngIf="!isRunning && !loading && form">
{{ 'tools.modal.cleanUp.content' | translate }}
<form
role="form"
(ngSubmit)="submit()"
clrForm
[formGroup]="form"
clrLayout="horizontal"
clrLabelSize="4"
style="padding: 0; margin: 0"
>
<clr-checkbox-container>
<clr-checkbox-wrapper>
<input formControlName="onlyComplete" type="checkbox" clrCheckbox name="onlyComplete" />
<label>{{ 'tools.modal.cleanUp.onlyComplete' | translate }}</label>
</clr-checkbox-wrapper>
</clr-checkbox-container>

<div *ngIf="form.get('onlyComplete').value">
<clr-checkbox-container>
<clr-checkbox-wrapper>
<input formControlName="activeDays" type="checkbox" clrCheckbox name="activeDays" />
<label>{{ 'tools.modal.cleanUp.olderExecution' | translate }}</label>
</clr-checkbox-wrapper>
</clr-checkbox-container>

<clr-input-container *ngIf="form.get('activeDays').value">
<label class="clr-col-1">{{ 'tools.modal.cleanUp.days' | translate }}:</label>
<input
clrInput
name="days"
formControlName="days"
class="clr-col-3"
style="width: 100%"
[dataflowFocus]="true"
pattern="[0-9]*"
placeholder="{{ 'tools.cleanUpDays' | translate }}"
/>
<clr-control-error>{{ 'tools.modal.cleanUp.daysInvalid' | translate }}</clr-control-error>
</clr-input-container>
</div>
</form>

</div>
<div class="modal-body" *ngIf="isRunning">
<clr-spinner clrInline clrSmall></clr-spinner>
Expand Down
14 changes: 0 additions & 14 deletions ui/src/app/manage/tools/cleanup/cleanup.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,4 @@ describe('manage/tools/cleanup/cleanup.component.ts', () => {
expect(component).toBeTruthy();
});

it('should clean up the task executions', async done => {
component.open('all');
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
const title = fixture.debugElement.query(By.css('.modal-title-wrapper')).nativeElement;
expect(title.textContent).toContain('Confirm Clean Up Task Execution(s)');
fixture.debugElement.query(By.css('.modal-footer .btn-danger')).nativeElement.click();
fixture.detectChanges();
await fixture.whenStable();
expect(NotificationServiceMock.mock.successNotifications[0].title).toBe('Clean up execution(s)');
expect(NotificationServiceMock.mock.successNotifications[0].message).toBe('12 execution(s) cleaned up.');
done();
});
});
35 changes: 25 additions & 10 deletions ui/src/app/manage/tools/cleanup/cleanup.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {TaskService} from '../../../shared/api/task.service';
import {NotificationService} from '../../../shared/service/notification.service';
import {UntypedFormGroup, UntypedFormControl, Validators} from '@angular/forms';

@Component({
selector: 'app-tools-cleanup',
Expand All @@ -11,8 +12,9 @@ export class CleanupComponent {
isOpen = false;
isRunning = false;
loading = true;
count: {completed: number; all: number};
status = 'all';
days: number;
form: UntypedFormGroup;
@Output() onCleaned = new EventEmitter();

constructor(
Expand All @@ -21,14 +23,21 @@ export class CleanupComponent {
private translate: TranslateService
) {}

open(status: string): void {
open(status: string, days: number): void {
this.status = status;
this.days = days;
this.loading = true;
this.taskService.getTaskExecutionsCount().subscribe(

this.form = new UntypedFormGroup({
onlyComplete: new UntypedFormControl(false, [Validators.required]),
activeDays: new UntypedFormControl(false, [Validators.required]),
days: new UntypedFormControl(null)
});

this.taskService.getTaskExecutionsCount(null).subscribe(
(count: {completed: number; all: number}) => {
this.count = count;
this.loading = false;
if (this.count.all === 0) {
if (count.all === 0) {
this.notificationService.warning(
this.translate.instant('tools.modal.cleanUp.message.warningNoExecutionTitle'),
this.translate.instant('tools.modal.cleanUp.message.warningNoExecutionContent')
Expand All @@ -42,21 +51,27 @@ export class CleanupComponent {
this.isOpen = false;
}
);
this.loading = false;
this.isRunning = false;
this.isOpen = true;
}

clean(): void {
this.isRunning = true;
this.taskService.taskExecutionsClean(null, this.status === 'completed').subscribe(
const values = this.form.getRawValue();
const days = values.activeDays && values.onlyComplete ? values.days : null;

if (this.form.invalid) {
return;
}

this.taskService.taskExecutionsClean(null, values.onlyComplete, days).subscribe(
() => {
this.notificationService.success(
this.translate.instant('tools.modal.cleanUp.message.successTitle'),
this.translate.instant('tools.modal.cleanUp.message.successContent', {
count: this.status === 'completed' ? this.count.completed : this.count.all
})
this.translate.instant('tools.modal.cleanUp.message.successContent')
);
this.onCleaned.emit(this.count);
this.onCleaned.emit();
this.isOpen = false;
},
error => {
Expand Down
12 changes: 5 additions & 7 deletions ui/src/app/manage/tools/tools.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,17 @@ <h1>{{ 'tools.title' | translate }}</h1>
</div>
<div class="clr-col-lg-12 clr-col-md-12 clr-col-12" appRole appFeature="tasks">
<app-view-card
[titleModal]="'tools.cleanUp' | translate"
[titleModal]="'tools.taskJob' | translate"
keyContext="import-export"
name="import-export"
id="cleanup"
>
<ng-template>
<div>
<clr-icon shape="angle" style="transform: rotate(90deg)"></clr-icon>
<a href="{{ baseApiUrl }}#" (click)="run('cleanup-all')">{{ 'tools.cleanUpAll' | translate }}</a>
</div>
<div>
<clr-icon shape="angle" style="transform: rotate(90deg)"></clr-icon>
<a href="{{ baseApiUrl }}#" (click)="run('cleanup-completed')">{{ 'tools.cleanUpCompleted' | translate }}</a>
<div>
<clr-icon shape="angle" style="transform: rotate(90deg)"></clr-icon>
<a href="{{ baseApiUrl }}#" (click)="run('cleanup-all')">{{ 'tools.cleanUpAll' | translate }}</a>
</div>
</div>
</ng-template>
</app-view-card>
Expand Down
5 changes: 3 additions & 2 deletions ui/src/app/manage/tools/tools.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class ToolsComponent {
@ViewChild('cleanupModal', {static: true})
cleanupModal: CleanupComponent;
baseApiUrl = UrlUtilities.calculateBaseApiUrl();
days: number;

constructor(private notificationService: NotificationService) {}

Expand All @@ -41,10 +42,10 @@ export class ToolsComponent {
this.taskImportModal.open();
break;
case 'cleanup-all':
this.cleanupModal.open('all');
this.cleanupModal.open('all', this.days);
break;
case 'cleanup-completed':
this.cleanupModal.open('completed');
this.cleanupModal.open('completed', this.days);
break;
}
return false;
Expand Down
23 changes: 12 additions & 11 deletions ui/src/app/shared/api/task.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,26 +169,27 @@ export class TaskService {
return this.httpClient.delete<any>(url, {headers, observe: 'response'}).pipe(catchError(ErrorUtils.catchError));
}

taskExecutionsClean(task: Task, completed: boolean): Observable<any> {
taskExecutionsClean(task: Task, completed: boolean, days: number): Observable<any> {
const headers = HttpUtils.getDefaultHttpHeaders();
const paramCompleted = completed ? '&completed=true' : '';
const paramDays = days != null ? '&days=' + days : '';
const paramTask = task ? `&name=${task.name}` : '';
const url =
UrlUtilities.calculateBaseApiUrl() + `tasks/executions?action=CLEANUP,REMOVE_DATA${paramCompleted}${paramTask}`;
return this.httpClient.delete<void>(url, {headers, observe: 'response'}).pipe(catchError(ErrorUtils.catchError));
UrlUtilities.calculateBaseApiUrl() +
`tasks/executions?action=CLEANUP,REMOVE_DATA${paramCompleted}${paramTask}${paramDays}`;
return this.httpClient.delete<any>(url, {headers, observe: 'response'}).pipe(catchError(ErrorUtils.catchError));
}

getTaskExecutionsCount(task?: Task): Observable<{completed: number; all: number} | unknown> {
getTaskExecutionsCount(task?: Task, days?: number): Observable<{completed: number; all: number} | unknown> {
const headers = HttpUtils.getDefaultHttpHeaders();
let url = UrlUtilities.calculateBaseApiUrl() + 'tasks/info/executions';
let url2 = `${url}?completed=true`;
if (task) {
url = UrlUtilities.calculateBaseApiUrl() + `tasks/info/executions?name=${task.name}`;
url2 = `${url}&completed=true`;
}
let url = UrlUtilities.calculateBaseApiUrl() + 'tasks/info/executions?p=v';

url += task != null ? '&name=' + task.name : '';
url += days != null ? '&days=' + days : '';

return this.httpClient.get<any>(url, {headers}).pipe(
mergeMap(data =>
this.httpClient.get<any>(url2, {headers}).pipe(
this.httpClient.get<any>(url + '&completed=true', {headers}).pipe(
map(data2 => ({
completed: +data2.totalExecutions,
all: +data.totalExecutions
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/tasks-jobs/tasks/cleanup/cleanup.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class CleanupComponent {

clean(): void {
this.isRunning = true;
this.taskService.taskExecutionsClean(this.task, this.status === 'completed').subscribe(
this.taskService.taskExecutionsClean(this.task, this.status === 'completed', null).subscribe(
() => {
this.notificationService.success(
this.translate.instant('tasks.cleanup.message.successTitle'),
Expand Down
12 changes: 9 additions & 3 deletions ui/src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,25 +335,31 @@
"title": "Tools",
"exportStreams": "Exportiere Stream(s): Erstellt eine JSON-Datei basierend auf den ausgewählten Streams",
"importStreams": "Importiere Stream(s): Importiere Streams von einer JSON-Datei",
"cleanUpDescription": "Räume Aufgaben auf - älter als",
"cleanUpDays": "Tage",
"cleanUpAll": "Räume alle Aufgabe/Job-Ausführungen auf",
"cleanUpCompleted": "Räume alle beendeten Aufgabe/Job-Ausführungen auf",
"exportTasks": "Exportiere Aufgabe(n): Erstellt eine JSON-Datei basierend auf den ausgewählten Aufgaben",
"importTasks": "Importiere Aufgabe(n): Importiere Aufgaben von einer JSON-Datei",
"streams": "Streams",
"tasks": "Aufgaben",
"cleanUp": "Aufgaben-Ausführung aufräumen",
"taskJob": "Aufgaben-Ausführung",
"modal": {
"cleanUp": {
"title": "Bestätige Aufräumen der Aufgaben-Ausführung(en)",
"content": "Diese Aktion entfernt <strong>{{ count }} Ausführung(en)</strong>. Sind Sie sicher?",
"content": "Sie können die Task-/Job-Ausführungen filtern, die Sie aufräumen wollen:",
"processing": "Entferne Daten...",
"cleanUp": "Aufräumen der Ausführung(en)",
"onlyComplete": "Nur beendete Ausführung(en)",
"olderExecution": "Aufräumen älterer Ausführung(en)",
"days": "Tage",
"daysInvalid": "Tage ungültig",
"removingAll": "Entferne alle Einträge.",
"message": {
"warningNoExecutionTitle": "Keine Ausführung",
"warningNoExecutionContent": "Es gibt keine Ausführung.",
"successTitle": "Aufräumen der Ausführung(en)",
"successContent": "{{count}} Ausführung(en) wurden aufgeräumt."
"successContent": "Ausführung(en) erfolgreich aufgeräumt."
}
},
"exportStreams": {
Expand Down
12 changes: 9 additions & 3 deletions ui/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,24 +339,30 @@
"title": "Tools",
"exportStreams": "Export stream(s): Create a JSON file with the selected streams",
"importStreams": "Import stream(s): Import streams from a JSON file",
"cleanUpDescription": "Cleanup tasks older than",
"cleanUpDays": "days",
"cleanUpAll": "Clean up all task/job executions",
"cleanUpCompleted": "Clean up all completed task/job executions",
"exportTasks": "Export task(s): Create a JSON file with the selected tasks",
"importTasks": "Import task(s): Import tasks from a JSON file",
"streams": "Streams",
"tasks": "Tasks",
"cleanUp": "Clean up Task Executions",
"taskJob": "Task/Job Executions",
"modal": {
"cleanUp": {
"title": "Confirm Clean Up Task Execution(s)",
"content": "This action will remove <strong>{{ count }} execution(s)</strong>. Are you sure?",
"content": "You can filters the task/job execution(s) you want to clean up:",
"cleanUp": "Clean up Execution(s)",
"onlyComplete": "Only complete execution(s)",
"olderExecution": "Cleanup older execution(s)",
"days": "Days",
"daysInvalid": "Invalid days",
"removingAll": "Removing all entries.",
"message": {
"warningNoExecutionTitle": "No execution",
"warningNoExecutionContent": "There is no execution.",
"successTitle": "Clean up execution(s)",
"successContent": "{{count}} execution(s) cleaned up."
"successContent": "Execution(s) cleaned up."
}
},
"exportStreams": {
Expand Down
12 changes: 9 additions & 3 deletions ui/src/assets/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,24 +335,30 @@
"title": "Инструменты",
"exportStreams": "Экспорт потоков: создайте файл JSON с выбранными потоками.",
"importStreams": "Импорт потоков: импорт потоков из файла JSON.",
"cleanUpDescription": "Задачи очистки старше",
"cleanUpDays": "дни",
"cleanUpAll": "Очистить все задачи/работы",
"cleanUpCompleted": "Очистить все выполненные задачи/работы",
"exportTasks": "Экспорт задачи: создайте файл JSON с выбранными задачами.",
"importTasks": "Импорт задач: импорт задач из файла JSON.",
"streams": "Потоки",
"tasks": "Задачи",
"cleanUp": "Очистить выполнение задач",
"taskJob": "выполнение задач/заданий",
"modal": {
"cleanUp": {
"title": "Подтвердить выполнение задачи по очистке",
"content": "Это действие удалит <strong>{{ count }} выполнение(я)</strong>. Вы уверены?",
"content": "Вы можете отфильтровать выполнение задач/заданий, которые хотите очистить:",
"cleanUp": "Очистить выполнение",
"onlyComplete": "Только полное выполнение(я)",
"olderExecution": "Очистка старых исполнений",
"days": "Дни",
"daysInvalid": "Неверные дни",
"removingAll": "Удаление всех записей",
"message": {
"warningNoExecutionTitle": "Без исполнения",
"warningNoExecutionContent": "Нет исполнения.",
"successTitle": "Очистить исполнение(я)",
"successContent": "{{count}} исполнение(я) очищено."
"successContent": "Исполнение(я) очищено."
}
},
"exportStreams": {
Expand Down

0 comments on commit 85df243

Please sign in to comment.