diff --git a/backend/i18n/frontend_en.json b/backend/i18n/frontend_en.json index 0a2c6655e..401f02b6f 100644 --- a/backend/i18n/frontend_en.json +++ b/backend/i18n/frontend_en.json @@ -220,6 +220,7 @@ "common.bookmarks": "Bookmarks", "common.bytes": "bytes", "common.cancel": "Cancel", + "common.cancelAll": "Cancel All", "common.category": "Category", "common.clear": "Clear", "common.clientId": "Client Id", @@ -251,6 +252,7 @@ "common.delete": "Delete", "common.description": "Description", "common.designer": "Designer", + "common.details": "Details", "common.disable": "Disable", "common.disabled": "Disabled", "common.displayName": "Display Name", @@ -667,7 +669,11 @@ "rules.refreshTooltip": "Refresh Rules", "rules.reloaded": "Rules reloaded.", "rules.restarted": "Rule will start to run in a few seconds.", - "rules.ruleEvents.cancelFailed": "Failed to cancel rule event. Please reload.", + "rules.ruleEvents.cancelAllConfirmText": "Do you really want to cancel all events?", + "rules.ruleEvents.cancelAllConfirmTitle": "Cancel all events", + "rules.ruleEvents.cancelConfirmText": "Do you really want to cancel this event?", + "rules.ruleEvents.cancelConfirmTitle": "Cancel event", + "rules.ruleEvents.cancelFailed": "Failed to cancel rule event(s). Please reload.", "rules.ruleEvents.enqueue": "Enqueue", "rules.ruleEvents.enqueued": "Events enqueued. Will be resend in a few seconds.", "rules.ruleEvents.enqueueFailed": "Failed to enqueue rule event. Please reload.", diff --git a/backend/i18n/frontend_it.json b/backend/i18n/frontend_it.json index e8ec3b583..579cda1d3 100644 --- a/backend/i18n/frontend_it.json +++ b/backend/i18n/frontend_it.json @@ -220,6 +220,7 @@ "common.bookmarks": "Bookmarks", "common.bytes": "byte", "common.cancel": "Annulla", + "common.cancelAll": "Cancel All", "common.category": "Categoria", "common.clear": "Pulisci", "common.clientId": "Client Id", @@ -251,6 +252,7 @@ "common.delete": "Cancella", "common.description": "Descrizione", "common.designer": "Designer", + "common.details": "Details", "common.disable": "Disable", "common.disabled": "Disabled", "common.displayName": "Nome visualizzato", @@ -667,6 +669,10 @@ "rules.refreshTooltip": "Aggiorna le Regole", "rules.reloaded": "Regole ricaricate.", "rules.restarted": "La Regola sarà eseguita fra pochi secondi.", + "rules.ruleEvents.cancelAllConfirmText": "Do you really want to cancel all events?", + "rules.ruleEvents.cancelAllConfirmTitle": "Cancel all events", + "rules.ruleEvents.cancelConfirmText": "Do you really want to cancel this event?", + "rules.ruleEvents.cancelConfirmTitle": "Cancel event", "rules.ruleEvents.cancelFailed": "Non è stato possibile cancellare l'evento della regola. Per favore ricarica.", "rules.ruleEvents.enqueue": "Metti in coda", "rules.ruleEvents.enqueued": "Eventi messo in coda. L'evento potrà essere rieseguito fra pochi secondi.", diff --git a/backend/i18n/frontend_nl.json b/backend/i18n/frontend_nl.json index 10bcb5e9d..81cbeff1b 100644 --- a/backend/i18n/frontend_nl.json +++ b/backend/i18n/frontend_nl.json @@ -220,6 +220,7 @@ "common.bookmarks": "Bookmarks", "common.bytes": "bytes", "common.cancel": "Annuleren", + "common.cancelAll": "Cancel All", "common.category": "Category", "common.clear": "Wissen", "common.clientId": "Client-ID", @@ -251,6 +252,7 @@ "common.delete": "Verwijderen", "common.description": "Beschrijving", "common.designer": "Designer", + "common.details": "Details", "common.disable": "Disable", "common.disabled": "Disabled", "common.displayName": "Weergavenaam", @@ -667,6 +669,10 @@ "rules.refreshTooltip": "Vernieuwingsregels", "rules.reloaded": "Regels herladen.", "rules.restarted": "Regel begint over een paar seconden te lopen.", + "rules.ruleEvents.cancelAllConfirmText": "Do you really want to cancel all events?", + "rules.ruleEvents.cancelAllConfirmTitle": "Cancel all events", + "rules.ruleEvents.cancelConfirmText": "Do you really want to cancel this event?", + "rules.ruleEvents.cancelConfirmTitle": "Cancel event", "rules.ruleEvents.cancelFailed": "Annuleren van regelgebeurtenis is mislukt. Laad opnieuw.", "rules.ruleEvents.enqueue": "Enqueue", "rules.ruleEvents.enqueued": "Evenementen in de wachtrij geplaatst. Worden over enkele seconden opnieuw verzonden.", diff --git a/backend/i18n/frontend_zh.json b/backend/i18n/frontend_zh.json index e1fc9eb9a..0d46e6c92 100644 --- a/backend/i18n/frontend_zh.json +++ b/backend/i18n/frontend_zh.json @@ -220,6 +220,7 @@ "common.bookmarks": "书签", "common.bytes": "bytes", "common.cancel": "取消", + "common.cancelAll": "Cancel All", "common.category": "类别", "common.clear": "清除", "common.clientId": "客户端 ID", @@ -251,6 +252,7 @@ "common.delete": "删除", "common.description": "说明", "common.designer": "设计师", + "common.details": "Details", "common.disable": "Disable", "common.disabled": "已禁用", "common.displayName": "显示名称", @@ -667,6 +669,10 @@ "rules.refreshTooltip": "刷新规则", "rules.reloaded": "规则重新加载。", "rules.restarted": "规则将在几秒钟后开始运行。", + "rules.ruleEvents.cancelAllConfirmText": "Do you really want to cancel all events?", + "rules.ruleEvents.cancelAllConfirmTitle": "Cancel all events", + "rules.ruleEvents.cancelConfirmText": "Do you really want to cancel this event?", + "rules.ruleEvents.cancelConfirmTitle": "Cancel event", "rules.ruleEvents.cancelFailed": "取消规则事件失败。请重新加载。", "rules.ruleEvents.enqueue": "入队", "rules.ruleEvents.enqueued": "事件已入队。将在几秒钟后重新发送。", diff --git a/backend/i18n/source/frontend_en.json b/backend/i18n/source/frontend_en.json index 0a2c6655e..401f02b6f 100644 --- a/backend/i18n/source/frontend_en.json +++ b/backend/i18n/source/frontend_en.json @@ -220,6 +220,7 @@ "common.bookmarks": "Bookmarks", "common.bytes": "bytes", "common.cancel": "Cancel", + "common.cancelAll": "Cancel All", "common.category": "Category", "common.clear": "Clear", "common.clientId": "Client Id", @@ -251,6 +252,7 @@ "common.delete": "Delete", "common.description": "Description", "common.designer": "Designer", + "common.details": "Details", "common.disable": "Disable", "common.disabled": "Disabled", "common.displayName": "Display Name", @@ -667,7 +669,11 @@ "rules.refreshTooltip": "Refresh Rules", "rules.reloaded": "Rules reloaded.", "rules.restarted": "Rule will start to run in a few seconds.", - "rules.ruleEvents.cancelFailed": "Failed to cancel rule event. Please reload.", + "rules.ruleEvents.cancelAllConfirmText": "Do you really want to cancel all events?", + "rules.ruleEvents.cancelAllConfirmTitle": "Cancel all events", + "rules.ruleEvents.cancelConfirmText": "Do you really want to cancel this event?", + "rules.ruleEvents.cancelConfirmTitle": "Cancel event", + "rules.ruleEvents.cancelFailed": "Failed to cancel rule event(s). Please reload.", "rules.ruleEvents.enqueue": "Enqueue", "rules.ruleEvents.enqueued": "Events enqueued. Will be resend in a few seconds.", "rules.ruleEvents.enqueueFailed": "Failed to enqueue rule event. Please reload.", diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs index 3859ed3fb..ad9abf31a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs @@ -109,7 +109,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules await Collection.InsertOneIfNotExistsAsync(entity); } - public Task CancelAsync(DomainId id) + public Task CancelByEventAsync(DomainId id) { return Collection.UpdateOneAsync(x => x.DocumentId == id, Update @@ -117,6 +117,22 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules .Set(x => x.JobResult, RuleJobResult.Cancelled)); } + public Task CancelByRuleAsync(DomainId ruleId) + { + return Collection.UpdateOneAsync(x => x.RuleId == ruleId, + Update + .Set(x => x.NextAttempt, null) + .Set(x => x.JobResult, RuleJobResult.Cancelled)); + } + + public Task CancelByAppAsync(DomainId appId) + { + return Collection.UpdateOneAsync(x => x.DocumentId == appId, + Update + .Set(x => x.NextAttempt, null) + .Set(x => x.JobResult, RuleJobResult.Cancelled)); + } + public Task UpdateAsync(RuleJob job, RuleJobUpdate update) { Guard.NotNull(job, nameof(job)); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs index 33c8defa3..5a8b30077 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs @@ -44,7 +44,11 @@ namespace Squidex.Domain.Apps.Entities.Rules.Repositories Task EnqueueAsync(DomainId id, Instant nextAttempt); - Task CancelAsync(DomainId id); + Task CancelByEventAsync(DomainId eventId); + + Task CancelByRuleAsync(DomainId ruleId); + + Task CancelByAppAsync(DomainId appId); Task QueryPendingAsync(Instant now, Func callback, CancellationToken ct = default); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs index 26656e2dc..475692649 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs @@ -82,7 +82,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models if (NextAttempt != null) { - AddDeleteLink("delete", resources.Url(x => nameof(x.DeleteEvent), values)); + AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteEvent), values)); } return this; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs index 673582320..f1c8ba27b 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs @@ -26,7 +26,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models /// public long Total { get; set; } - public static RuleEventsDto FromRuleEvents(IResultList ruleEvents, Resources resources) + public static RuleEventsDto FromRuleEvents(IResultList ruleEvents, Resources resources, DomainId? ruleId) { var result = new RuleEventsDto { @@ -34,15 +34,26 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models Items = ruleEvents.Select(x => RuleEventDto.FromRuleEvent(x, resources)).ToArray() }; - return result.CreateLinks(resources); + return result.CreateLinks(resources, ruleId); } - private RuleEventsDto CreateLinks(Resources resources) + private RuleEventsDto CreateLinks(Resources resources, DomainId? ruleId) { var values = new { app = resources.App }; AddSelfLink(resources.Url(x => nameof(x.GetEvents), values)); + if (ruleId != null) + { + var routeValeus = new { values.app, id = ruleId }; + + AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteRuleEvents), routeValeus)); + } + else + { + AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteEvents), values)); + } + return this; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index a5e7c20a3..e8f459610 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs @@ -264,6 +264,26 @@ namespace Squidex.Areas.Api.Controllers.Rules return NoContent(); } + /// + /// Cancels all rule events. + /// + /// The name of the app. + /// The id of the rule to cancel. + /// + /// 204 => Rule events cancelled. + /// + [HttpDelete] + [Route("apps/{app}/rules/{id}/events/")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiCosts(1)] + public async Task DeleteRuleEvents(string app, DomainId id) + { + await ruleEventsRepository.CancelByRuleAsync(id); + + return NoContent(); + } + /// /// Simulate a rule. /// @@ -334,7 +354,7 @@ namespace Squidex.Areas.Api.Controllers.Rules { var ruleEvents = await ruleEventsRepository.QueryByAppAsync(AppId, ruleId, skip, take); - var response = RuleEventsDto.FromRuleEvents(ruleEvents, Resources); + var response = RuleEventsDto.FromRuleEvents(ruleEvents, Resources, ruleId); return Ok(response); } @@ -366,6 +386,25 @@ namespace Squidex.Areas.Api.Controllers.Rules return NoContent(); } + /// + /// Cancels all events. + /// + /// The name of the app. + /// + /// 204 => Events cancelled. + /// + [HttpDelete] + [Route("apps/{app}/rules/events/")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiCosts(1)] + public async Task DeleteEvents(string app) + { + await ruleEventsRepository.CancelByAppAsync(App.Id); + + return NoContent(); + } + /// /// Cancels an event. /// @@ -388,7 +427,7 @@ namespace Squidex.Areas.Api.Controllers.Rules return NotFound(); } - await ruleEventsRepository.CancelAsync(id); + await ruleEventsRepository.CancelByRuleAsync(id); return NoContent(); } diff --git a/frontend/app/features/rules/pages/events/rule-event.component.html b/frontend/app/features/rules/pages/events/rule-event.component.html index acb4e2f61..f174e99b4 100644 --- a/frontend/app/features/rules/pages/events/rule-event.component.html +++ b/frontend/app/features/rules/pages/events/rule-event.component.html @@ -1,4 +1,4 @@ - + {{event.jobResult}} @@ -34,11 +34,15 @@ {{ 'rules.ruleEvents.nextAttemptLabel' | sqxTranslate }}: {{event.nextAttempt | sqxFromNow}}
- -
diff --git a/frontend/app/features/rules/pages/events/rule-event.component.scss b/frontend/app/features/rules/pages/events/rule-event.component.scss index 79e8dd057..7684f212e 100644 --- a/frontend/app/features/rules/pages/events/rule-event.component.scss +++ b/frontend/app/features/rules/pages/events/rule-event.component.scss @@ -18,21 +18,22 @@ td { } .event { - &-stats { - font-size: $font-smallest; - } - &-header, &-dump, &-stats { padding: .75rem 1.25rem; } + &-stats { + font-size: $font-smallest; + font-weight: normal; + padding-bottom: 0; + } + &-header { background: $color-border-light; border: 0; border-bottom: 2px solid $color-border; - padding: .75rem 1.25rem; position: relative; h4 { diff --git a/frontend/app/features/rules/pages/events/rule-events-page.component.html b/frontend/app/features/rules/pages/events/rule-events-page.component.html index 9f17acaa4..09593ef40 100644 --- a/frontend/app/features/rules/pages/events/rule-events-page.component.html +++ b/frontend/app/features/rules/pages/events/rule-events-page.component.html @@ -1,10 +1,20 @@ - + + + + + diff --git a/frontend/app/features/rules/pages/events/rule-events-page.component.scss b/frontend/app/features/rules/pages/events/rule-events-page.component.scss index e69de29bb..2d2b21043 100644 --- a/frontend/app/features/rules/pages/events/rule-events-page.component.scss +++ b/frontend/app/features/rules/pages/events/rule-events-page.component.scss @@ -0,0 +1,6 @@ +:host ::ng-deep { + .panel2-slice { + border-left: 1px solid $color-border; + border-right: 1px solid $color-border; + } +} \ No newline at end of file diff --git a/frontend/app/features/rules/pages/events/rule-events-page.component.ts b/frontend/app/features/rules/pages/events/rule-events-page.component.ts index 834c35615..b966717e8 100644 --- a/frontend/app/features/rules/pages/events/rule-events-page.component.ts +++ b/frontend/app/features/rules/pages/events/rule-events-page.component.ts @@ -51,6 +51,10 @@ export class RuleEventsPageComponent extends ResourceOwner implements OnInit { this.ruleEventsState.enqueue(event); } + public cancelAll() { + this.ruleEventsState.cancelAll(); + } + public cancel(event: RuleEventDto) { this.ruleEventsState.cancel(event); } diff --git a/frontend/app/features/rules/pages/simulator/rule-simulator-page.component.scss b/frontend/app/features/rules/pages/simulator/rule-simulator-page.component.scss index 5bf85d403..2d2b21043 100644 --- a/frontend/app/features/rules/pages/simulator/rule-simulator-page.component.scss +++ b/frontend/app/features/rules/pages/simulator/rule-simulator-page.component.scss @@ -1,38 +1,6 @@ -h3 { - margin-bottom: 1rem; -} - -.expanded { - border-bottom: 0; -} - -.event { - &-stats { - font-size: $font-smallest; - } - - &-dump { - @include text-code; - height: 20rem; - margin-bottom: 0; - margin-top: 1rem; - } - - &-header { - background: $color-white; - border: 0; - margin: -.75rem -1.25rem; - margin-bottom: 1rem; - padding: .75rem 1.25rem; - position: relative; - - &::before { - @include caret-top($color-white); - @include absolute(-1.1rem, 1.8rem, auto, auto); - } - - h3 { - margin: 0; - } +:host ::ng-deep { + .panel2-slice { + border-left: 1px solid $color-border; + border-right: 1px solid $color-border; } } \ No newline at end of file diff --git a/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.html b/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.html index 9a9be882f..12f5b6fd1 100644 --- a/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.html +++ b/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.html @@ -1,4 +1,4 @@ - + {{status}} @@ -15,17 +15,23 @@ - -
- - - + +
+

{{ 'common.details' | sqxTranslate }}

- -
- - - + +
+
+ + + +
+ +
+ + + +
diff --git a/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.scss b/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.scss index 65dc8410a..ec5f5a4da 100644 --- a/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.scss +++ b/frontend/app/features/rules/pages/simulator/simulated-rule-event.component.scss @@ -1,4 +1,38 @@ +td { + &.details { + border-top: 0; + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; + padding: 0 !important; + } +} -.expanded { - border-bottom: 0; +.table-items-row { + &.expanded { + td { + border-bottom: 0; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; + } + } +} + +.event { + &-header, + &-dump { + padding: .75rem 1.25rem; + } + + &-header { + background: $color-border-light; + border: 0; + border-bottom: 2px solid $color-border; + position: relative; + + h4 { + font-size: 1rem; + font-weight: 500; + margin: 0; + } + } } \ No newline at end of file diff --git a/frontend/app/shared/services/rules.service.spec.ts b/frontend/app/shared/services/rules.service.spec.ts index 8b5f9b49f..18b63859f 100644 --- a/frontend/app/shared/services/rules.service.spec.ts +++ b/frontend/app/shared/services/rules.service.spec.ts @@ -343,17 +343,17 @@ describe('RulesService', () => { req.flush({}); })); - it('should make delete request to cancel rule event', + it('should make delete request to cancel all rule events', inject([RulesService, HttpTestingController], (rulesService: RulesService, httpMock: HttpTestingController) => { const resource: Resource = { _links: { - delete: { method: 'DELETE', href: '/api/apps/my-app/rules/events/123' }, + cancel: { method: 'DELETE', href: '/api/apps/my-app/rules/events' }, }, }; - rulesService.cancelEvent('my-app', resource).subscribe(); + rulesService.cancelEvents('my-app', resource).subscribe(); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/rules/events/123'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/rules/events'); expect(req.request.method).toEqual('DELETE'); expect(req.request.headers.get('If-Match')).toBeNull(); diff --git a/frontend/app/shared/services/rules.service.ts b/frontend/app/shared/services/rules.service.ts index f9493cdc5..ebd653cc2 100644 --- a/frontend/app/shared/services/rules.service.ts +++ b/frontend/app/shared/services/rules.service.ts @@ -375,14 +375,14 @@ export class RulesService { pretifyError('i18n:rules.ruleEvents.enqueueFailed')); } - public cancelEvent(appName: string, resource: Resource): Observable { - const link = resource._links['delete']; + public cancelEvents(appName: string, resource: Resource): Observable { + const link = resource._links['cancel']; const url = this.apiUrl.buildUrl(link.href); return HTTP.requestVersioned(this.http, link.method, url).pipe( tap(() => { - this.analytics.trackEvent('Rule', 'EventDequeued', appName); + this.analytics.trackEvent('Rule', 'EventsCancelled', appName); }), pretifyError('i18n:rules.ruleEvents.cancelFailed')); } diff --git a/frontend/app/shared/state/rule-events.state.spec.ts b/frontend/app/shared/state/rule-events.state.spec.ts index b98ae4ce2..e1779bc5b 100644 --- a/frontend/app/shared/state/rule-events.state.spec.ts +++ b/frontend/app/shared/state/rule-events.state.spec.ts @@ -111,13 +111,24 @@ describe('RuleEventsState', () => { }); it('should call service if cancelling event', () => { - rulesService.setup(x => x.cancelEvent(app, oldRuleEvents[0])) + rulesService.setup(x => x.cancelEvents(app, oldRuleEvents[0])) .returns(() => of({})); ruleEventsState.cancel(oldRuleEvents[0]).subscribe(); expect().nothing(); - rulesService.verify(x => x.cancelEvent(app, oldRuleEvents[0]), Times.once()); + rulesService.verify(x => x.cancelEvents(app, oldRuleEvents[0]), Times.once()); + }); + + it('should call service if cancelling all events', () => { + rulesService.setup(x => x.cancelEvents(app, It.isAny())) + .returns(() => of({})); + + ruleEventsState.cancelAll().subscribe(); + + expect().nothing(); + + rulesService.verify(x => x.cancelEvents(app, It.isAny()), Times.once()); }); }); diff --git a/frontend/app/shared/state/rule-events.state.ts b/frontend/app/shared/state/rule-events.state.ts index 062a01c47..f01bf2f83 100644 --- a/frontend/app/shared/state/rule-events.state.ts +++ b/frontend/app/shared/state/rule-events.state.ts @@ -6,7 +6,7 @@ */ import { Injectable } from '@angular/core'; -import { DialogService, getPagingInfo, ListState, shareSubscribed, State } from '@app/framework'; +import { DialogService, getPagingInfo, hasAnyLink, ListState, ResourceLinks, shareSubscribed, State } from '@app/framework'; import { EMPTY, Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { RuleEventDto, RulesService } from './../services/rules.service'; @@ -18,6 +18,9 @@ interface Snapshot extends ListState { // The current rule id. ruleId?: string; + + // The resource links. + links: ResourceLinks; } @Injectable() @@ -37,12 +40,16 @@ export class RuleEventsState extends State { public isLoading = this.project(x => x.isLoading === true); + public canCancelAll = + this.project(x => hasAnyLink(x.links, 'cancel')); + constructor( private readonly appsState: AppsState, private readonly dialogs: DialogService, private readonly rulesService: RulesService, ) { super({ + links: {}, ruleEvents: [], page: 0, pageSize: 30, @@ -67,7 +74,7 @@ export class RuleEventsState extends State { pageSize, pageSize * page, ruleId).pipe( - tap(({ total, items: ruleEvents }) => { + tap(({ total, items: ruleEvents, _links: links }) => { if (isReload) { this.dialogs.notifyInfo('i18n:rules.ruleEvents.reloaded'); } @@ -75,6 +82,7 @@ export class RuleEventsState extends State { return this.next({ isLoaded: true, isLoading: false, + links, ruleEvents, total, }, 'Loading Success'); @@ -93,8 +101,20 @@ export class RuleEventsState extends State { shareSubscribed(this.dialogs)); } + public cancelAll(): Observable { + return this.rulesService.cancelEvents(this.appsState.appName, { _links: this.snapshot.links }).pipe( + tap(() => { + return this.next(s => { + const ruleEvents = s.ruleEvents.map(x => setCancelled(x)); + + return { ...s, ruleEvents, isLoaded: true }; + }, 'CancelAll'); + }), + shareSubscribed(this.dialogs)); + } + public cancel(event: RuleEventDto): Observable { - return this.rulesService.cancelEvent(this.appsState.appName, event).pipe( + return this.rulesService.cancelEvents(this.appsState.appName, event).pipe( tap(() => { return this.next(s => { const ruleEvents = s.ruleEvents.replacedBy('id', setCancelled(event));