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; 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) public async Task<IContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = -1)
{ {
Guard.NotNull(context, nameof(context)); Guard.NotNull(context, nameof(context));
var schema = await GetSchemaAsync(context, schemaIdOrName); var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context.User, schema); CheckPermission(context.User, schema);
@ -110,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
Guard.NotNull(context, nameof(context)); Guard.NotNull(context, nameof(context));
var schema = await GetSchemaAsync(context, schemaIdOrName); var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context.User, schema); 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)); 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; ISchemaEntity schema = null;

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

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents namespace Squidex.Domain.Apps.Entities.Contents
@ -16,12 +17,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
int DefaultPageSizeGraphQl { get; } 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<IResultList<IContentEntity>> QueryAsync(QueryContext context, string schemaIdOrName, Q query);
Task<IContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = EtagVersion.Any); 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;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.GraphQL; using Squidex.Domain.Apps.Entities.Contents.GraphQL;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Shared; using Squidex.Shared;
using Squidex.Web; using Squidex.Web;
@ -26,15 +27,18 @@ namespace Squidex.Areas.Api.Controllers.Contents
{ {
private readonly IOptions<MyContentsControllerOptions> controllerOptions; private readonly IOptions<MyContentsControllerOptions> controllerOptions;
private readonly IContentQueryService contentQuery; private readonly IContentQueryService contentQuery;
private readonly IContentWorkflow contentWorkflow;
private readonly IGraphQLService graphQl; private readonly IGraphQLService graphQl;
public ContentsController(ICommandBus commandBus, public ContentsController(ICommandBus commandBus,
IContentQueryService contentQuery, IContentQueryService contentQuery,
IContentWorkflow contentWorkflow,
IGraphQLService graphQl, IGraphQLService graphQl,
IOptions<MyContentsControllerOptions> controllerOptions) IOptions<MyContentsControllerOptions> controllerOptions)
: base(commandBus) : base(commandBus)
{ {
this.contentQuery = contentQuery; this.contentQuery = contentQuery;
this.contentWorkflow = contentWorkflow;
this.controllerOptions = controllerOptions; this.controllerOptions = controllerOptions;
this.graphQl = graphQl; this.graphQl = graphQl;
@ -123,8 +127,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
{ {
var context = Context(); var context = Context();
var contents = await contentQuery.QueryAsync(context, Q.Empty.WithIds(ids).Ids); 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) if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
{ {
@ -159,7 +164,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = Context(); var context = Context();
var contents = await contentQuery.QueryAsync(context, name, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString())); 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)) if (ShouldProvideSurrogateKeys(response))
{ {
@ -194,7 +201,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = Context(); var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id); 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) if (controllerOptions.Value.EnableSurrogateKeys)
{ {
@ -230,7 +237,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = Context(); var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id, version); 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) if (controllerOptions.Value.EnableSurrogateKeys)
{ {
@ -264,7 +271,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostContent(string app, string name, [FromBody] NamedContentData request, [FromQuery] bool publish = false) 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))) if (publish && !this.HasPermission(Helper.StatusPermission(app, name, Status.Published)))
{ {
@ -301,7 +308,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutContent(string app, string name, Guid id, [FromBody] NamedContentData request, [FromQuery] bool asDraft = false) 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 }; var command = new UpdateContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
@ -333,7 +340,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PatchContent(string app, string name, Guid id, [FromBody] NamedContentData request, [FromQuery] bool asDraft = false) 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 }; var command = new PatchContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
@ -364,9 +371,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutContentStatus(string app, string name, Guid id, ChangeStatusDto request) 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(); return new ForbidResult();
} }
@ -399,7 +406,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DiscardDraft(string app, string name, Guid id) 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 }; var command = new DiscardChanges { ContentId = id };
@ -427,7 +434,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DeleteContent(string app, string name, Guid id) 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 }; var command = new DeleteContent { ContentId = id };
@ -441,7 +448,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
var context = await CommandBus.PublishAsync(command); var context = await CommandBus.PublishAsync(command);
var result = context.Result<IContentEntity>(); 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; 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); 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;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.ConvertContent;
@ -79,7 +80,11 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// </summary> /// </summary>
public long Version { get; set; } 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()); 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()); 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 }; 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)); 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)); 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)) 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) 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)); 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))) 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Shared; using Squidex.Shared;
using Squidex.Web; using Squidex.Web;
@ -34,7 +35,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// The possible statuses. /// The possible statuses.
/// </summary> /// </summary>
[Required] [Required]
public string[] Statuses { get; set; } public Status2[] Statuses { get; set; }
public string ToEtag() public string ToEtag()
{ {
@ -46,20 +47,37 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
return Items.ToSurrogateKeys(); 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, ApiController controller,
string app, ISchemaEntity schema,
string schema) IContentWorkflow contentWorkflow)
{ {
var result = new ContentsDto var result = new ContentsDto
{ {
Total = total, Total = contents.Total,
Items = contents.Select(x => ContentDto.FromContent(x, context, controller, app, schema)).ToArray(), 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) 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(); SetupSchemaFound();
var result = await sut.GetSchemaAsync(context, schemaId.Name); var result = await sut.GetSchemaOrThrowAsync(context, schemaId.Name);
Assert.Equal(schema, result); Assert.Equal(schema, result);
} }
@ -113,7 +113,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
SetupSchemaFound(); SetupSchemaFound();
var result = await sut.GetSchemaAsync(context, schemaId.Name); var result = await sut.GetSchemaOrThrowAsync(context, schemaId.Name);
Assert.Equal(schema, result); Assert.Equal(schema, result);
} }
@ -125,7 +125,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
var ctx = context; var ctx = context;
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaAsync(ctx, schemaId.Name)); await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaOrThrowAsync(ctx, schemaId.Name));
} }
[Fact] [Fact]
@ -135,7 +135,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
var ctx = context; var ctx = context;
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.ThrowIfSchemaNotExistsAsync(ctx, schemaId.Name)); await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaOrThrowAsync(ctx, schemaId.Name));
} }
[Fact] [Fact]

Loading…
Cancel
Save