From 9e0c9e64cd0cfdc89d0f1e5128ac3c8adaf1cc3e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 13 Jun 2019 16:49:59 +0100 Subject: [PATCH] Contributor fix. --- .../Apps/AppGrain.cs | 63 ++++++++--- .../Invitation/InviteUserCommandMiddleware.cs | 4 +- .../Apps/Invitation/InvitedResult.cs | 4 +- .../Apps/AppContributorsController.cs | 23 ++-- .../Apps/Models/ContributorAssignedDto.cs | 30 ------ .../Controllers/Apps/Models/ContributorDto.cs | 16 ++- .../Apps/Models/ContributorsDto.cs | 36 ++++++- .../Apps/Models/ContributorsMetadata.cs | 17 +++ .../Api/Controllers/Assets/Models/AssetDto.cs | 7 +- .../contributors-page.component.html | 15 +-- .../contributors-page.component.ts | 6 +- .../services/contributors.service.spec.ts | 101 ++++++++++++------ .../shared/services/contributors.service.ts | 63 +++++++---- .../shared/state/contributors.state.spec.ts | 76 ++++--------- .../app/shared/state/contributors.state.ts | 70 ++++-------- .../Apps/AppGrainTests.cs | 32 +++--- .../InviteUserCommandMiddlewareTests.cs | 8 +- 17 files changed, 320 insertions(+), 251 deletions(-) delete mode 100644 src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorAssignedDto.cs create mode 100644 src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsMetadata.cs diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs index 63ac53ab9..11f93323c 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs @@ -60,11 +60,13 @@ namespace Squidex.Domain.Apps.Entities.Apps switch (command) { case CreateApp createApp: - return CreateAsync(createApp, c => + return CreateReturnAsync(createApp, async c => { GuardApp.CanCreate(c); Create(c); + + return await GetRawStateAsync(); }); case AssignContributor assignContributor: @@ -74,111 +76,137 @@ namespace Squidex.Domain.Apps.Entities.Apps AssignContributor(c, !Snapshot.Contributors.ContainsKey(assignContributor.ContributorId)); - return EntityCreatedResult.Create(c.ContributorId, Version); + return await GetRawStateAsync(); }); case RemoveContributor removeContributor: - return UpdateAsync(removeContributor, c => + return UpdateReturnAsync(removeContributor, async c => { GuardAppContributors.CanRemove(Snapshot.Contributors, c); RemoveContributor(c); + + return await GetRawStateAsync(); }); case AttachClient attachClient: - return UpdateAsync(attachClient, c => + return UpdateReturnAsync(attachClient, async c => { GuardAppClients.CanAttach(Snapshot.Clients, c); AttachClient(c); + + return await GetRawStateAsync(); }); case UpdateClient updateClient: - return UpdateAsync(updateClient, c => + return UpdateReturnAsync(updateClient, async c => { GuardAppClients.CanUpdate(Snapshot.Clients, c, Snapshot.Roles); UpdateClient(c); + + return await GetRawStateAsync(); }); case RevokeClient revokeClient: - return UpdateAsync(revokeClient, c => + return UpdateReturnAsync(revokeClient, async c => { GuardAppClients.CanRevoke(Snapshot.Clients, c); RevokeClient(c); + + return await GetRawStateAsync(); }); case AddLanguage addLanguage: - return UpdateAsync(addLanguage, c => + return UpdateReturnAsync(addLanguage, async c => { GuardAppLanguages.CanAdd(Snapshot.LanguagesConfig, c); AddLanguage(c); + + return await GetRawStateAsync(); }); case RemoveLanguage removeLanguage: - return UpdateAsync(removeLanguage, c => + return UpdateReturnAsync(removeLanguage, async c => { GuardAppLanguages.CanRemove(Snapshot.LanguagesConfig, c); RemoveLanguage(c); + + return await GetRawStateAsync(); }); case UpdateLanguage updateLanguage: - return UpdateAsync(updateLanguage, c => + return UpdateReturnAsync(updateLanguage, async c => { GuardAppLanguages.CanUpdate(Snapshot.LanguagesConfig, c); UpdateLanguage(c); + + return await GetRawStateAsync(); }); case AddRole addRole: - return UpdateAsync(addRole, c => + return UpdateReturnAsync(addRole, async c => { GuardAppRoles.CanAdd(Snapshot.Roles, c); AddRole(c); + + return await GetRawStateAsync(); }); case DeleteRole deleteRole: - return UpdateAsync(deleteRole, c => + return UpdateReturnAsync(deleteRole, async c => { GuardAppRoles.CanDelete(Snapshot.Roles, c, Snapshot.Contributors, Snapshot.Clients); DeleteRole(c); + + return await GetRawStateAsync(); }); case UpdateRole updateRole: - return UpdateAsync(updateRole, c => + return UpdateReturnAsync(updateRole, async c => { GuardAppRoles.CanUpdate(Snapshot.Roles, c); UpdateRole(c); + + return await GetRawStateAsync(); }); case AddPattern addPattern: - return UpdateAsync(addPattern, c => + return UpdateReturnAsync(addPattern, async c => { GuardAppPatterns.CanAdd(Snapshot.Patterns, c); AddPattern(c); + + return await GetRawStateAsync(); }); case DeletePattern deletePattern: - return UpdateAsync(deletePattern, c => + return UpdateReturnAsync(deletePattern, async c => { GuardAppPatterns.CanDelete(Snapshot.Patterns, c); DeletePattern(c); + + return await GetRawStateAsync(); }); case UpdatePattern updatePattern: - return UpdateAsync(updatePattern, c => + return UpdateReturnAsync(updatePattern, async c => { GuardAppPatterns.CanUpdate(Snapshot.Patterns, c); UpdatePattern(c); + + return await GetRawStateAsync(); }); case ChangePlan changePlan: @@ -384,6 +412,11 @@ namespace Squidex.Domain.Apps.Entities.Apps return new AppContributorAssigned { ContributorId = actor.Identifier, Role = Role.Owner }; } + public Task GetRawStateAsync() + { + return Task.FromResult(Snapshot); + } + public Task> GetStateAsync() { return J.AsTask(Snapshot); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InviteUserCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InviteUserCommandMiddleware.cs index 0bf99f271..7bde0a4cd 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InviteUserCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InviteUserCommandMiddleware.cs @@ -35,9 +35,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation await next(); - if (assignContributor.IsCreated && context.PlainResult is EntityCreatedResult id) + if (assignContributor.IsCreated && context.PlainResult is IAppEntity app) { - context.Complete(new InvitedResult { Id = id }); + context.Complete(new InvitedResult { App = app }); } return; diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitedResult.cs b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitedResult.cs index 695be0a4b..45c6df7b9 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitedResult.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitedResult.cs @@ -5,12 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Infrastructure.Commands; - namespace Squidex.Domain.Apps.Entities.Apps.Invitation { public sealed class InvitedResult { - public EntityCreatedResult Id { get; set; } + public IAppEntity App { get; set; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs b/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs index 98187cf77..acc83e266 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; using Squidex.Areas.Api.Controllers.Apps.Models; +using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Invitation; using Squidex.Domain.Apps.Entities.Apps.Services; @@ -47,7 +48,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [ApiCosts(0)] public IActionResult GetContributors(string app) { - var response = ContributorsDto.FromApp(App, appPlansProvider); + var response = ContributorsDto.FromApp(App, appPlansProvider, this, false); Response.Headers[HeaderNames.ETag] = App.Version.ToString(); @@ -66,7 +67,7 @@ namespace Squidex.Areas.Api.Controllers.Apps /// [HttpPost] [Route("apps/{app}/contributors/")] - [ProducesResponseType(typeof(ContributorAssignedDto), 201)] + [ProducesResponseType(typeof(ContributorsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppContributorsAssign)] [ApiCosts(1)] @@ -75,15 +76,15 @@ namespace Squidex.Areas.Api.Controllers.Apps var command = request.ToCommand(); var context = await CommandBus.PublishAsync(command); - var response = (ContributorAssignedDto)null; + var response = (ContributorsDto)null; - if (context.PlainResult is EntityCreatedResult idOrValue) + if (context.PlainResult is IAppEntity newApp) { - response = ContributorAssignedDto.FromId(idOrValue.IdOrValue, false); + response = ContributorsDto.FromApp(newApp, appPlansProvider, this, false); } else if (context.PlainResult is InvitedResult invited) { - response = ContributorAssignedDto.FromId(invited.Id.IdOrValue, true); + response = ContributorsDto.FromApp(invited.App, appPlansProvider, this, true); } return Ok(response); @@ -95,20 +96,24 @@ namespace Squidex.Areas.Api.Controllers.Apps /// The name of the app. /// The id of the contributor. /// - /// 204 => User removed from app. + /// 200 => User removed from app. /// 400 => User is not assigned to the app. /// 404 => Contributor or app not found. /// [HttpDelete] [Route("apps/{app}/contributors/{id}/")] + [ProducesResponseType(typeof(ContributorsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppContributorsRevoke)] [ApiCosts(1)] public async Task DeleteContributor(string app, string id) { - await CommandBus.PublishAsync(new RemoveContributor { ContributorId = id }); + var command = new RemoveContributor { ContributorId = id }; + var context = await CommandBus.PublishAsync(command); - return NoContent(); + var response = ContributorsDto.FromApp(context.Result(), appPlansProvider, this, false); + + return Ok(response); } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorAssignedDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorAssignedDto.cs deleted file mode 100644 index 871fcc6e9..000000000 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorAssignedDto.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.ComponentModel.DataAnnotations; - -namespace Squidex.Areas.Api.Controllers.Apps.Models -{ - public sealed class ContributorAssignedDto - { - /// - /// The id of the user that has been assigned as contributor. - /// - [Required] - public string ContributorId { get; set; } - - /// - /// Indicates if the user was created. - /// - public bool IsCreated { get; set; } - - public static ContributorAssignedDto FromId(string id, bool isCreated) - { - return new ContributorAssignedDto { ContributorId = id, IsCreated = isCreated }; - } - } -} diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs index 42ea190e5..30814f92d 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs @@ -5,11 +5,13 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.ComponentModel.DataAnnotations; +using Squidex.Web; namespace Squidex.Areas.Api.Controllers.Apps.Models { - public sealed class ContributorDto + public sealed class ContributorDto : Resource { /// /// The id of the user that contributes to the app. @@ -21,5 +23,17 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models /// The role of the contributor. /// public string Role { get; set; } + + public static ContributorDto FromIdAndRole(string id, string role, ApiController controller, string app) + { + var result = new ContributorDto { ContributorId = id, Role = role }; + + return CreateLinks(result, controller, app); + } + + private static ContributorDto CreateLinks(ContributorDto result, ApiController controller, string app) + { + return result; + } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs index e482b1a69..cbeaab92c 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs @@ -6,13 +6,15 @@ // ========================================================================== using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Infrastructure; +using Squidex.Web; namespace Squidex.Areas.Api.Controllers.Apps.Models { - public sealed class ContributorsDto + public sealed class ContributorsDto : Resource { /// /// The contributors. @@ -25,13 +27,37 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models /// public int MaxContributors { get; set; } - public static ContributorsDto FromApp(IAppEntity app, IAppPlansProvider plans) + /// + /// The metadata. + /// + [JsonProperty("_meta")] + public ContributorsMetadata Metadata { get; set; } + + public static ContributorsDto FromApp(IAppEntity app, IAppPlansProvider plans, ApiController controller, bool isInvited) { - var plan = plans.GetPlanForApp(app); + var contributors = app.Contributors.ToArray(x => ContributorDto.FromIdAndRole(x.Key, x.Value, controller, app.Name)); + + var result = new ContributorsDto + { + Contributors = contributors, + }; - var contributors = app.Contributors.ToArray(x => new ContributorDto { ContributorId = x.Key, Role = x.Value }); + if (isInvited) + { + result.Metadata = new ContributorsMetadata + { + IsInvited = isInvited.ToString() + }; + } - return new ContributorsDto { Contributors = contributors, MaxContributors = plan.MaxContributors }; + result.MaxContributors = plans.GetPlanForApp(app).MaxContributors; + + return CreateLinks(result, controller, app.Name); + } + + private static ContributorsDto CreateLinks(ContributorsDto result, ApiController controller, string app) + { + return result; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsMetadata.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsMetadata.cs new file mode 100644 index 000000000..0d0950441 --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsMetadata.cs @@ -0,0 +1,17 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Areas.Api.Controllers.Apps.Models +{ + public sealed class ContributorsMetadata + { + /// + /// Indicates whether the user has been invited. + /// + public string IsInvited { get; set; } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs b/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs index 67d292a78..f44c9d25a 100644 --- a/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs @@ -112,8 +112,11 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models /// public long Version { get; set; } + /// + /// The metadata. + /// [JsonProperty("_meta")] - public AssetMetadata Meta { get; set; } + public AssetMetadata Metadata { get; set; } public static AssetDto FromAsset(IAssetEntity asset, ApiController controller, string app, bool isDuplicate = false) { @@ -121,7 +124,7 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models if (isDuplicate) { - response.Meta = new AssetMetadata { IsDuplicate = "true" }; + response.Metadata = new AssetMetadata { IsDuplicate = "true" }; } return CreateLinks(response, controller, app); diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html index 5019d2d82..83a3223b2 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html @@ -23,21 +23,24 @@ - + @@ -47,7 +50,7 @@
- + - {{contributorInfo.contributor.contributorId | sqxUserName}} + {{contributor.contributorId | sqxUserName}} - -
-