// ==========================================================================
// 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();
}
}
}