Browse Source

Read only mode for rule events.

pull/892/head
Sebastian 4 years ago
parent
commit
c0965bf4ca
  1. 4
      backend/src/Squidex.Shared/Permissions.cs
  2. 11
      backend/src/Squidex.Web/Resources.cs
  3. 5
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs
  4. 7
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs
  5. 19
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs
  6. 8
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs
  7. 20
      backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs
  8. 4
      frontend/src/app/features/rules/pages/events/rule-event.component.html
  9. 1
      frontend/src/app/features/rules/pages/events/rule-event.component.scss
  10. 6
      frontend/src/app/features/rules/pages/rules/rule.component.html
  11. 6
      frontend/src/app/shared/services/rules.service.ts

4
backend/src/Squidex.Shared/Permissions.cs

@ -135,6 +135,10 @@ namespace Squidex.Shared
public const string AppRules = "squidex.apps.{app}.rules"; public const string AppRules = "squidex.apps.{app}.rules";
public const string AppRulesRead = "squidex.apps.{app}.rules.read"; public const string AppRulesRead = "squidex.apps.{app}.rules.read";
public const string AppRulesEvents = "squidex.apps.{app}.rules.events"; public const string AppRulesEvents = "squidex.apps.{app}.rules.events";
public const string AppRulesEventsRun = "squidex.apps.{app}.rules.events.run";
public const string AppRulesEventsRead = "squidex.apps.{app}.rules.events.read";
public const string AppRulesEventsUpdate = "squidex.apps.{app}.rules.events.update";
public const string AppRulesEventsDelete = "squidex.apps.{app}.rules.events.delete";
public const string AppRulesCreate = "squidex.apps.{app}.rules.create"; public const string AppRulesCreate = "squidex.apps.{app}.rules.create";
public const string AppRulesUpdate = "squidex.apps.{app}.rules.update"; public const string AppRulesUpdate = "squidex.apps.{app}.rules.update";
public const string AppRulesDisable = "squidex.apps.{app}.rules.disable"; public const string AppRulesDisable = "squidex.apps.{app}.rules.disable";

11
backend/src/Squidex.Web/Resources.cs

@ -114,7 +114,16 @@ namespace Squidex.Web
public bool CanDeleteRule => IsAllowed(Permissions.AppRulesDelete); public bool CanDeleteRule => IsAllowed(Permissions.AppRulesDelete);
[Lazy] [Lazy]
public bool CanReadRuleEvents => IsAllowed(Permissions.AppRulesEvents); public bool CanReadRuleEvents => IsAllowed(Permissions.AppRulesEventsRead);
[Lazy]
public bool CanUpdateRuleEvents => IsAllowed(Permissions.AppRulesEventsUpdate);
[Lazy]
public bool CanRunRuleEvents => IsAllowed(Permissions.AppRulesEventsRun);
[Lazy]
public bool CanDeleteRuleEvents => IsAllowed(Permissions.AppRulesEventsDelete);
// Users // Users
[Lazy] [Lazy]

5
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs

@ -126,7 +126,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
AddPutLink("update", resources.Url<RulesController>(x => nameof(x.PutRule), values)); AddPutLink("update", resources.Url<RulesController>(x => nameof(x.PutRule), values));
} }
if (resources.CanReadRuleEvents) if (resources.CanRunRuleEvents)
{ {
AddPutLink("trigger", resources.Url<RulesController>(x => nameof(x.TriggerRule), values)); AddPutLink("trigger", resources.Url<RulesController>(x => nameof(x.TriggerRule), values));
@ -141,7 +141,10 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
AddPutLink("run/snapshots", resources.Url<RulesController>(x => nameof(x.PutRuleRun), snaphshotValues)); AddPutLink("run/snapshots", resources.Url<RulesController>(x => nameof(x.PutRuleRun), snaphshotValues));
} }
}
if (resources.CanReadRuleEvents)
{
AddGetLink("logs", resources.Url<RulesController>(x => nameof(x.GetEvents), values)); AddGetLink("logs", resources.Url<RulesController>(x => nameof(x.GetEvents), values));
} }

7
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs

@ -78,9 +78,12 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
{ {
var values = new { app = resources.App, id = Id }; var values = new { app = resources.App, id = Id };
AddPutLink("update", resources.Url<RulesController>(x => nameof(x.PutEvent), values)); if (resources.CanUpdateRuleEvents)
{
AddPutLink("update", resources.Url<RulesController>(x => nameof(x.PutEvent), values));
}
if (NextAttempt != null) if (resources.CanDeleteRuleEvents && NextAttempt != null)
{ {
AddDeleteLink("cancel", resources.Url<RulesController>(x => nameof(x.DeleteEvent), values)); AddDeleteLink("cancel", resources.Url<RulesController>(x => nameof(x.DeleteEvent), values));
} }

19
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs

@ -42,15 +42,18 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
AddSelfLink(resources.Url<RulesController>(x => nameof(x.GetEvents), values)); AddSelfLink(resources.Url<RulesController>(x => nameof(x.GetEvents), values));
if (ruleId != null) if (resources.CanDeleteRuleEvents)
{ {
var routeValeus = new { values.app, id = ruleId }; if (ruleId != null)
{
AddDeleteLink("cancel", resources.Url<RulesController>(x => nameof(x.DeleteRuleEvents), routeValeus)); var routeValues = new { values.app, id = ruleId };
}
else AddDeleteLink("cancel", resources.Url<RulesController>(x => nameof(x.DeleteRuleEvents), routeValues));
{ }
AddDeleteLink("cancel", resources.Url<RulesController>(x => nameof(x.DeleteEvents), values)); else
{
AddDeleteLink("cancel", resources.Url<RulesController>(x => nameof(x.DeleteEvents), values));
}
} }
return this; return this;

8
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs

@ -54,11 +54,11 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
if (resources.CanReadRuleEvents) if (resources.CanReadRuleEvents)
{ {
AddGetLink("events", resources.Url<RulesController>(x => nameof(x.GetEvents), values)); AddGetLink("events", resources.Url<RulesController>(x => nameof(x.GetEvents), values));
}
if (runningRuleId != null) if (resources.CanDeleteRuleEvents && runningRuleId != null)
{ {
AddDeleteLink("run/cancel", resources.Url<RulesController>(x => nameof(x.DeleteRuleRun), values)); AddDeleteLink("run/cancel", resources.Url<RulesController>(x => nameof(x.DeleteRuleRun), values));
}
} }
return this; return this;

20
backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs

@ -140,7 +140,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
[HttpDelete] [HttpDelete]
[Route("apps/{app}/rules/run")] [Route("apps/{app}/rules/run")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DeleteRuleRun(string app) public async Task<IActionResult> DeleteRuleRun(string app)
{ {
@ -231,7 +231,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/rules/{id}/trigger/")] [Route("apps/{app}/rules/{id}/trigger/")]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRun)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> TriggerRule(string app, DomainId id) public async Task<IActionResult> TriggerRule(string app, DomainId id)
{ {
@ -254,7 +254,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
[HttpPut] [HttpPut]
[Route("apps/{app}/rules/{id}/run")] [Route("apps/{app}/rules/{id}/run")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRun)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutRuleRun(string app, DomainId id, [FromQuery] bool fromSnapshots = false) public async Task<IActionResult> PutRuleRun(string app, DomainId id, [FromQuery] bool fromSnapshots = false)
{ {
@ -274,7 +274,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
[HttpDelete] [HttpDelete]
[Route("apps/{app}/rules/{id}/events/")] [Route("apps/{app}/rules/{id}/events/")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsDelete)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DeleteRuleEvents(string app, DomainId id) public async Task<IActionResult> DeleteRuleEvents(string app, DomainId id)
{ {
@ -295,7 +295,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
[HttpPost] [HttpPost]
[Route("apps/{app}/rules/simulate/")] [Route("apps/{app}/rules/simulate/")]
[ProducesResponseType(typeof(SimulatedRuleEventsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SimulatedRuleEventsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRead)]
[ApiCosts(5)] [ApiCosts(5)]
public async Task<IActionResult> Simulate(string app, [FromBody] CreateRuleDto request) public async Task<IActionResult> Simulate(string app, [FromBody] CreateRuleDto request)
{ {
@ -320,7 +320,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
[HttpGet] [HttpGet]
[Route("apps/{app}/rules/{id}/simulate/")] [Route("apps/{app}/rules/{id}/simulate/")]
[ProducesResponseType(typeof(SimulatedRuleEventsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SimulatedRuleEventsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRead)]
[ApiCosts(5)] [ApiCosts(5)]
public async Task<IActionResult> Simulate(string app, DomainId id) public async Task<IActionResult> Simulate(string app, DomainId id)
{ {
@ -372,7 +372,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
[HttpGet] [HttpGet]
[Route("apps/{app}/rules/events/")] [Route("apps/{app}/rules/events/")]
[ProducesResponseType(typeof(RuleEventsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(RuleEventsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppRulesRead)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRead)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> GetEvents(string app, [FromQuery] DomainId? ruleId = null, [FromQuery] int skip = 0, [FromQuery] int take = 20) public async Task<IActionResult> GetEvents(string app, [FromQuery] DomainId? ruleId = null, [FromQuery] int skip = 0, [FromQuery] int take = 20)
{ {
@ -394,7 +394,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/rules/events/{id}/")] [Route("apps/{app}/rules/events/{id}/")]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsUpdate)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> PutEvent(string app, DomainId id) public async Task<IActionResult> PutEvent(string app, DomainId id)
{ {
@ -420,7 +420,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
[HttpDelete] [HttpDelete]
[Route("apps/{app}/rules/events/")] [Route("apps/{app}/rules/events/")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsDelete)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DeleteEvents(string app) public async Task<IActionResult> DeleteEvents(string app)
{ {
@ -440,7 +440,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
/// </returns> /// </returns>
[HttpDelete] [HttpDelete]
[Route("apps/{app}/rules/events/{id}/")] [Route("apps/{app}/rules/events/{id}/")]
[ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiPermissionOrAnonymous(Permissions.AppRulesEventsDelete)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> DeleteEvent(string app, DomainId id) public async Task<IActionResult> DeleteEvent(string app, DomainId id)
{ {

4
frontend/src/app/features/rules/pages/events/rule-event.component.html

@ -34,7 +34,7 @@
{{ 'rules.ruleEvents.nextAttemptLabel' | sqxTranslate }}: <ng-container *ngIf="event.nextAttempt">{{event.nextAttempt | sqxFromNow}}</ng-container> {{ 'rules.ruleEvents.nextAttemptLabel' | sqxTranslate }}: <ng-container *ngIf="event.nextAttempt">{{event.nextAttempt | sqxFromNow}}</ng-container>
</div> </div>
<div class="col-3 text-end"> <div class="col-3 text-end">
<button type="button" class="btn btn-danger me-1" [class.hidden]="!event.nextAttempt" <button type="button" class="btn btn-danger me-1" *ngIf="event.canDelete"
(sqxConfirmClick)="cancel.emit()" (sqxConfirmClick)="cancel.emit()"
confirmTitle="i18n:rules.ruleEvents.cancelConfirmTitle" confirmTitle="i18n:rules.ruleEvents.cancelConfirmTitle"
confirmText="i18n:rules.ruleEvents.cancelConfirmText" confirmText="i18n:rules.ruleEvents.cancelConfirmText"
@ -42,7 +42,7 @@
{{ 'common.cancel' | sqxTranslate }} {{ 'common.cancel' | sqxTranslate }}
</button> </button>
<button type="button" class="btn btn-success" (click)="enqueue.emit()"> <button type="button" class="btn btn-success" (click)="enqueue.emit()" *ngIf="event.canUpdate">
{{ 'rules.ruleEvents.enqueue' | sqxTranslate }} {{ 'rules.ruleEvents.enqueue' | sqxTranslate }}
</button> </button>
</div> </div>

1
frontend/src/app/features/rules/pages/events/rule-event.component.scss

@ -30,6 +30,7 @@ td {
&-stats { &-stats {
font-size: $font-smallest; font-size: $font-smallest;
font-weight: normal; font-weight: normal;
height: 4rem;
} }
&-header { &-header {

6
frontend/src/app/features/rules/pages/rules/rule.component.html

@ -10,7 +10,7 @@
[isRequired]="false"> [isRequired]="false">
</sqx-editable-title> </sqx-editable-title>
</div> </div>
<div class="col-auto" *ngIf="rule.canDelete || rule.canRun"> <div class="col-auto" [class.invisible]="!rule.canDelete && !rule.canRun">
<button type="button" class="btn btn-text-secondary" (click)="dropdown.toggle()" #buttonOptions> <button type="button" class="btn btn-text-secondary" (click)="dropdown.toggle()" #buttonOptions>
<i class="icon-dots"></i> <i class="icon-dots"></i>
</button> </button>
@ -109,8 +109,8 @@
<div class="col"> <div class="col">
{{ 'common.lastExecuted' | sqxTranslate }}: <span>{{rule.lastExecuted | sqxFromNow:'-'}}</span> {{ 'common.lastExecuted' | sqxTranslate }}: <span>{{rule.lastExecuted | sqxFromNow:'-'}}</span>
</div> </div>
<div class="col-auto"> <div class="col-auto" *ngIf="rule.canReadLogs">
<a routerLink="events" [queryParams]="{ ruleId: rule.id }" *ngIf="rule.canTrigger"> <a routerLink="events" [queryParams]="{ ruleId: rule.id }">
{{ 'common.logs' | sqxTranslate }} {{ 'common.logs' | sqxTranslate }}
</a> </a>

6
frontend/src/app/shared/services/rules.service.ts

@ -127,6 +127,7 @@ export class RuleDto {
public readonly canDelete: boolean; public readonly canDelete: boolean;
public readonly canDisable: boolean; public readonly canDisable: boolean;
public readonly canEnable: boolean; public readonly canEnable: boolean;
public readonly canReadLogs: boolean;
public readonly canRun: boolean; public readonly canRun: boolean;
public readonly canRunFromSnapshots: boolean; public readonly canRunFromSnapshots: boolean;
public readonly canTrigger: boolean; public readonly canTrigger: boolean;
@ -155,9 +156,10 @@ export class RuleDto {
this.canDelete = hasAnyLink(links, 'delete'); this.canDelete = hasAnyLink(links, 'delete');
this.canDisable = hasAnyLink(links, 'disable'); this.canDisable = hasAnyLink(links, 'disable');
this.canEnable = hasAnyLink(links, 'enable'); this.canEnable = hasAnyLink(links, 'enable');
this.canReadLogs = hasAnyLink(links, 'logs');
this.canRun = hasAnyLink(links, 'run'); this.canRun = hasAnyLink(links, 'run');
this.canRunFromSnapshots = hasAnyLink(links, 'run/snapshots'); this.canRunFromSnapshots = hasAnyLink(links, 'run/snapshots');
this.canTrigger = hasAnyLink(links, 'logs'); this.canTrigger = hasAnyLink(links, 'trigger');
this.canUpdate = hasAnyLink(links, 'update'); this.canUpdate = hasAnyLink(links, 'update');
} }
} }
@ -186,7 +188,7 @@ export class RuleEventDto extends Model<RuleEventDto> {
this._links = links; this._links = links;
this.canDelete = hasAnyLink(links, 'delete'); this.canDelete = hasAnyLink(links, 'cancel');
this.canUpdate = hasAnyLink(links, 'update'); this.canUpdate = hasAnyLink(links, 'update');
} }
} }

Loading…
Cancel
Save