// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschränkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; using NodaTime; using Squidex.Areas.Api.Controllers.Rules.Models; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Extensions.Actions; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Reflection; using Squidex.Pipeline; using Squidex.Shared; namespace Squidex.Areas.Api.Controllers.Rules { /// /// Manages and retrieves information about schemas. /// [ApiExplorerSettings(GroupName = nameof(Rules))] public sealed class RulesController : ApiController { private static readonly string RuleActionsEtag = string.Join(";", RuleActionRegistry.Actions.Select(x => x.Key)).Sha256Base64(); private readonly IAppProvider appProvider; private readonly IRuleEventRepository ruleEventsRepository; public RulesController(ICommandBus commandBus, IAppProvider appProvider, IRuleEventRepository ruleEventsRepository) : base(commandBus) { this.appProvider = appProvider; this.ruleEventsRepository = ruleEventsRepository; } /// /// Get the supported rule actions. /// /// /// 200 => Rule actions returned. /// [HttpGet] [Route("rules/actions/")] [ProducesResponseType(typeof(Dictionary), 200)] [ApiPermission] [ApiCosts(0)] public IActionResult GetActions() { var response = RuleActionRegistry.Actions.ToDictionary(x => x.Key, x => SimpleMapper.Map(x.Value, new RuleElementDto())); Response.Headers[HeaderNames.ETag] = RuleActionsEtag; return Ok(response); } /// /// Get rules. /// /// The name of the app. /// /// 200 => Rules returned. /// 404 => App not found. /// [HttpGet] [Route("apps/{app}/rules/")] [ProducesResponseType(typeof(RuleDto[]), 200)] [ApiPermission(Permissions.AppRulesRead)] [ApiCosts(1)] public async Task GetRules(string app) { var entities = await appProvider.GetRulesAsync(AppId); var response = entities.Select(RuleDto.FromRule).ToArray(); Response.Headers[HeaderNames.ETag] = response.ToManyEtag(0); return Ok(response); } /// /// Create a new rule. /// /// The name of the app. /// The rule object that needs to be added to the app. /// /// 201 => Rule created. /// 400 => Rule is not valid. /// 404 => App not found. /// [HttpPost] [Route("apps/{app}/rules/")] [ProducesResponseType(typeof(EntityCreatedDto), 201)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppRulesCreate)] [ApiCosts(1)] public async Task PostRule(string app, [FromBody] CreateRuleDto request) { var context = await CommandBus.PublishAsync(request.ToCommand()); var result = context.Result>(); var response = EntityCreatedDto.FromResult(result); return CreatedAtAction(nameof(GetRules), new { app }, response); } /// /// Update a rule. /// /// The name of the app. /// The id of the rule to update. /// The rule object that needs to be added to the app. /// /// 204 => Rule updated. /// 400 => Rule is not valid. /// 404 => Rule or app not found. /// /// /// All events for the specified schemas will be sent to the url. The timeout is 2 seconds. /// [HttpPut] [Route("apps/{app}/rules/{id}/")] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppRulesUpdate)] [ApiCosts(1)] public async Task PutRule(string app, Guid id, [FromBody] UpdateRuleDto request) { await CommandBus.PublishAsync(request.ToCommand(id)); return NoContent(); } /// /// Enable a rule. /// /// The name of the app. /// The id of the rule to enable. /// /// 204 => Rule enabled. /// 400 => Rule already enabled. /// 404 => Rule or app not found. /// [HttpPut] [Route("apps/{app}/rules/{id}/enable/")] [ApiPermission(Permissions.AppRulesDisable)] [ApiCosts(1)] public async Task EnableRule(string app, Guid id) { await CommandBus.PublishAsync(new EnableRule { RuleId = id }); return NoContent(); } /// /// Disable a rule. /// /// The name of the app. /// The id of the rule to disable. /// /// 204 => Rule disabled. /// 400 => Rule already disabled. /// 404 => Rule or app not found. /// [HttpPut] [Route("apps/{app}/rules/{id}/disable/")] [ApiPermission(Permissions.AppRulesDisable)] [ApiCosts(1)] public async Task DisableRule(string app, Guid id) { await CommandBus.PublishAsync(new DisableRule { RuleId = id }); return NoContent(); } /// /// Delete a rule. /// /// The name of the app. /// The id of the rule to delete. /// /// 204 => Rule has been deleted. /// 404 => Rule or app not found. /// [HttpDelete] [Route("apps/{app}/rules/{id}/")] [ApiPermission(Permissions.AppRulesDelete)] [ApiCosts(1)] public async Task DeleteRule(string app, Guid id) { await CommandBus.PublishAsync(new DeleteRule { RuleId = id }); return NoContent(); } /// /// Get rule events. /// /// The name of the app. /// The number of events to skip. /// The number of events to take. /// /// 200 => Rule events returned. /// 404 => App not found. /// [HttpGet] [Route("apps/{app}/rules/events/")] [ProducesResponseType(typeof(RuleEventsDto), 200)] [ApiPermission(Permissions.AppRulesRead)] [ApiCosts(0)] public async Task GetEvents(string app, [FromQuery] int skip = 0, [FromQuery] int take = 20) { var taskForItems = ruleEventsRepository.QueryByAppAsync(AppId, skip, take); var taskForCount = ruleEventsRepository.CountByAppAsync(AppId); await Task.WhenAll(taskForItems, taskForCount); var response = RuleEventsDto.FromRuleEvents(taskForItems.Result, taskForCount.Result); return Ok(response); } /// /// Retry the event immediately. /// /// The name of the app. /// The event to enqueue. /// /// 200 => Rule enqueued. /// 404 => App or rule event not found. /// [HttpPut] [Route("apps/{app}/rules/events/{id}/")] [ApiPermission(Permissions.AppRulesEvents)] [ApiCosts(0)] public async Task PutEvent(string app, Guid id) { var entity = await ruleEventsRepository.FindAsync(id); if (entity == null) { return NotFound(); } await ruleEventsRepository.EnqueueAsync(id, SystemClock.Instance.GetCurrentInstant()); return NoContent(); } /// /// Cancels the event and retries. /// /// The name of the app. /// The event to enqueue. /// /// 200 => Rule deqeued. /// 404 => App or rule event not found. /// [HttpDelete] [Route("apps/{app}/rules/events/{id}/")] [ApiPermission(Permissions.AppRulesEvents)] [ApiCosts(0)] public async Task DeleteEvent(string app, Guid id) { var entity = await ruleEventsRepository.FindAsync(id); if (entity == null) { return NotFound(); } await ruleEventsRepository.CancelAsync(id); return NoContent(); } } }