Browse Source

Use content workflow interface in controller.

pull/365/head
Sebastian Stehle 7 years ago
parent
commit
5002b7041e
  1. 13
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  2. 5
      src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs
  3. 31
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  4. 7
      src/Squidex/Areas/Api/Controllers/Contents/Helper.cs
  5. 26
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  6. 36
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs
  7. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs

13
src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs

@ -73,16 +73,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
this.scriptEngine = scriptEngine;
}
public Task ThrowIfSchemaNotExistsAsync(QueryContext context, string schemaIdOrName)
{
return GetSchemaAsync(context, schemaIdOrName);
}
public async Task<IContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = -1)
{
Guard.NotNull(context, nameof(context));
var schema = await GetSchemaAsync(context, schemaIdOrName);
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context.User, schema);
@ -110,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
Guard.NotNull(context, nameof(context));
var schema = await GetSchemaAsync(context, schemaIdOrName);
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context.User, schema);
@ -136,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
public async Task<IList<IContentEntity>> QueryAsync(QueryContext context, IReadOnlyList<Guid> ids)
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(QueryContext context, IReadOnlyList<Guid> ids)
{
Guard.NotNull(context, nameof(context));
@ -295,7 +290,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
public async Task<ISchemaEntity> GetSchemaAsync(QueryContext context, string schemaIdOrName)
public async Task<ISchemaEntity> GetSchemaOrThrowAsync(QueryContext context, string schemaIdOrName)
{
ISchemaEntity schema = null;

5
src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents
@ -16,12 +17,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
int DefaultPageSizeGraphQl { get; }
Task<IList<IContentEntity>> QueryAsync(QueryContext context, IReadOnlyList<Guid> ids);
Task<IReadOnlyList<IContentEntity>> QueryAsync(QueryContext context, IReadOnlyList<Guid> ids);
Task<IResultList<IContentEntity>> QueryAsync(QueryContext context, string schemaIdOrName, Q query);
Task<IContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = EtagVersion.Any);
Task ThrowIfSchemaNotExistsAsync(QueryContext context, string schemaIdOrName);
Task<ISchemaEntity> GetSchemaOrThrowAsync(QueryContext context, string schemaIdOrName);
}
}

31
src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -16,6 +16,7 @@ using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.GraphQL;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Shared;
using Squidex.Web;
@ -26,15 +27,18 @@ namespace Squidex.Areas.Api.Controllers.Contents
{
private readonly IOptions<MyContentsControllerOptions> controllerOptions;
private readonly IContentQueryService contentQuery;
private readonly IContentWorkflow contentWorkflow;
private readonly IGraphQLService graphQl;
public ContentsController(ICommandBus commandBus,
IContentQueryService contentQuery,
IContentWorkflow contentWorkflow,
IGraphQLService graphQl,
IOptions<MyContentsControllerOptions> controllerOptions)
: base(commandBus)
{
this.contentQuery = contentQuery;
this.contentWorkflow = contentWorkflow;
this.controllerOptions = controllerOptions;
this.graphQl = graphQl;
@ -123,8 +127,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
{
var context = Context();
var contents = await contentQuery.QueryAsync(context, Q.Empty.WithIds(ids).Ids);
var contentsList = ResultList.Create<IContentEntity>(contents.Count, contents);
var response = ContentsDto.FromContents(contents.Count, contents, context, this, app, null);
var response = await ContentsDto.FromContentsAsync(contentsList, context, this, null, contentWorkflow);
if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
{
@ -159,7 +164,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = Context();
var contents = await contentQuery.QueryAsync(context, name, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString()));
var response = ContentsDto.FromContents(contents.Total, contents, context, this, app, name);
var schema = await contentQuery.GetSchemaOrThrowAsync(context, name);
var response = await ContentsDto.FromContentsAsync(contents, context, this, schema, contentWorkflow);
if (ShouldProvideSurrogateKeys(response))
{
@ -194,7 +201,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id);
var response = ContentDto.FromContent(content, context, this, app, name);
var response = await ContentDto.FromContentAsync(context, content, contentWorkflow, this);
if (controllerOptions.Value.EnableSurrogateKeys)
{
@ -230,7 +237,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id, version);
var response = ContentDto.FromContent(content, context, this, app, name);
var response = await ContentDto.FromContentAsync(context, content, contentWorkflow, this);
if (controllerOptions.Value.EnableSurrogateKeys)
{
@ -264,7 +271,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PostContent(string app, string name, [FromBody] NamedContentData request, [FromQuery] bool publish = false)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
if (publish && !this.HasPermission(Helper.StatusPermission(app, name, Status.Published)))
{
@ -301,7 +308,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PutContent(string app, string name, Guid id, [FromBody] NamedContentData request, [FromQuery] bool asDraft = false)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
var command = new UpdateContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
@ -333,7 +340,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PatchContent(string app, string name, Guid id, [FromBody] NamedContentData request, [FromQuery] bool asDraft = false)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
var command = new PatchContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
@ -364,9 +371,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PutContentStatus(string app, string name, Guid id, ChangeStatusDto request)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
if (!this.HasPermission(Helper.StatusPermission(app, name, Status.Published)))
if (!this.HasPermission(Helper.StatusPermission(app, name, Status2.Published)))
{
return new ForbidResult();
}
@ -399,7 +406,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> DiscardDraft(string app, string name, Guid id)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
var command = new DiscardChanges { ContentId = id };
@ -427,7 +434,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> DeleteContent(string app, string name, Guid id)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
var command = new DeleteContent { ContentId = id };
@ -441,7 +448,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = await CommandBus.PublishAsync(command);
var result = context.Result<IContentEntity>();
var response = ContentDto.FromContent(result, null, this, app, schema);
var response = await ContentDto.FromContentAsync(null, result, contentWorkflow, this);
return response;
}

7
src/Squidex/Areas/Api/Controllers/Contents/Helper.cs

@ -19,5 +19,12 @@ namespace Squidex.Areas.Api.Controllers.Contents
return Permissions.ForApp(id, app, schema);
}
public static Permission StatusPermission(string app, string schema, Status2 status)
{
var id = Permissions.AppContentsStatus.Replace("{status}", status.Name);
return Permissions.ForApp(id, app, schema);
}
}
}

26
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs

@ -7,6 +7,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
@ -79,7 +80,11 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// </summary>
public long Version { get; set; }
public static ContentDto FromContent(IContentEntity content, QueryContext context, ApiController controller, string app, string schema)
public static ValueTask<ContentDto> FromContentAsync(
QueryContext context,
IContentEntity content,
IContentWorkflow contentWorkflow,
ApiController controller)
{
var response = SimpleMapper.Map(content, new ContentDto());
@ -99,10 +104,14 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
response.ScheduleJob = SimpleMapper.Map(content.ScheduleJob, new ScheduleJobDto());
}
return response.CreateLinks(controller, app, schema);
return response.CreateLinksAsync(content, controller, content.AppId.Name, content.SchemaId.Name, contentWorkflow);
}
private ContentDto CreateLinks(ApiController controller, string app, string schema)
private async ValueTask<ContentDto> CreateLinksAsync(IContentEntity content,
ApiController controller,
string app,
string schema,
IContentWorkflow contentWorkflow)
{
var values = new { app, name = schema, id = Id };
@ -122,7 +131,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
AddPutLink("draft/discard", controller.Url<ContentsController>(x => nameof(x.DiscardDraft), values));
}
if (controller.HasPermission(Helper.StatusPermission(app, schema, Status.Published)))
if (controller.HasPermission(Helper.StatusPermission(app, schema, Status2.Published)))
{
AddPutLink("draft/publish", controller.Url<ContentsController>(x => nameof(x.PutContentStatus), values));
}
@ -130,7 +139,10 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
if (controller.HasPermission(Permissions.AppContentsUpdate, app, schema))
{
AddPutLink("update", controller.Url<ContentsController>(x => nameof(x.PutContent), values));
if (await contentWorkflow.CanUpdateAsync(content))
{
AddPutLink("update", controller.Url<ContentsController>(x => nameof(x.PutContent), values));
}
if (Status == Status.Published)
{
@ -145,7 +157,9 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
AddPutLink("delete", controller.Url<ContentsController>(x => nameof(x.DeleteContent), values));
}
foreach (var next in StatusFlow.Next(Status))
var nextStatuses = await contentWorkflow.GetNextsAsync(content);
foreach (var next in nextStatuses)
{
if (controller.HasPermission(Helper.StatusPermission(app, schema, next)))
{

36
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs

@ -5,12 +5,13 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Shared;
using Squidex.Web;
@ -34,7 +35,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// The possible statuses.
/// </summary>
[Required]
public string[] Statuses { get; set; }
public Status2[] Statuses { get; set; }
public string ToEtag()
{
@ -46,20 +47,37 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
return Items.ToSurrogateKeys();
}
public static ContentsDto FromContents(long total, IEnumerable<IContentEntity> contents, QueryContext context,
public static async Task<ContentsDto> FromContentsAsync(IResultList<IContentEntity> contents, QueryContext context,
ApiController controller,
string app,
string schema)
ISchemaEntity schema,
IContentWorkflow contentWorkflow)
{
var result = new ContentsDto
{
Total = total,
Items = contents.Select(x => ContentDto.FromContent(x, context, controller, app, schema)).ToArray(),
Total = contents.Total,
Items = new ContentDto[contents.Count]
};
result.Statuses = new string[] { "Archived", "Draft", "Published" };
await Task.WhenAll(
result.AssignContentsAsync(contentWorkflow, contents, context, controller),
result.AssignStatusesAsync(contentWorkflow, schema));
return result.CreateLinks(controller, app, schema);
return result.CreateLinks(controller, schema.AppId.Name, schema.SchemaDef.Name);
}
private async Task AssignStatusesAsync(IContentWorkflow contentWorkflow, ISchemaEntity schema)
{
var allStatuses = await contentWorkflow.GetAllAsync(schema);
Statuses = allStatuses.ToArray();
}
private async Task AssignContentsAsync(IContentWorkflow contentWorkflow, IResultList<IContentEntity> contents, QueryContext context, ApiController controller)
{
for (var i = 0; i < Items.Length; i++)
{
Items[i] = await ContentDto.FromContentAsync(context, contents[i], contentWorkflow, controller);
}
}
private ContentsDto CreateLinks(ApiController controller, string app, string schema)

8
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs

@ -103,7 +103,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
SetupSchemaFound();
var result = await sut.GetSchemaAsync(context, schemaId.Name);
var result = await sut.GetSchemaOrThrowAsync(context, schemaId.Name);
Assert.Equal(schema, result);
}
@ -113,7 +113,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
SetupSchemaFound();
var result = await sut.GetSchemaAsync(context, schemaId.Name);
var result = await sut.GetSchemaOrThrowAsync(context, schemaId.Name);
Assert.Equal(schema, result);
}
@ -125,7 +125,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
var ctx = context;
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaAsync(ctx, schemaId.Name));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaOrThrowAsync(ctx, schemaId.Name));
}
[Fact]
@ -135,7 +135,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
var ctx = context;
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.ThrowIfSchemaNotExistsAsync(ctx, schemaId.Name));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaOrThrowAsync(ctx, schemaId.Name));
}
[Fact]

Loading…
Cancel
Save