From dfe840f05a2d8453de03d05757dfe8cfc50679d1 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 15 Jun 2019 00:24:20 +0200 Subject: [PATCH] Schemas updated. --- .../Api/Controllers/Rules/RulesController.cs | 10 +- .../Schemas/SchemaFieldsController.cs | 164 ++- .../Controllers/Schemas/SchemasController.cs | 82 +- .../administration/state/users.state.spec.ts | 12 +- .../app/shared/services/apps.service.spec.ts | 8 +- .../app/shared/services/apps.service.ts | 4 +- .../shared/services/assets.service.spec.ts | 10 +- .../app/shared/services/assets.service.ts | 10 +- .../shared/services/schemas.service.spec.ts | 951 ++++++++++-------- .../app/shared/services/schemas.service.ts | 424 ++++---- .../app/shared/state/schemas.state.spec.ts | 456 ++++----- src/Squidex/app/shared/state/schemas.state.ts | 330 ++---- 12 files changed, 1241 insertions(+), 1220 deletions(-) diff --git a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index 23c09c944..3e1ddb232 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs @@ -111,7 +111,7 @@ namespace Squidex.Areas.Api.Controllers.Rules { var command = request.ToCommand(); - var response = await InvokeCommand(app, command); + var response = await InvokeCommandAsync(app, command); return CreatedAtAction(nameof(GetRules), new { app }, response); } @@ -140,7 +140,7 @@ namespace Squidex.Areas.Api.Controllers.Rules { var command = request.ToCommand(id); - var response = await InvokeCommand(app, command); + var response = await InvokeCommandAsync(app, command); return Ok(response); } @@ -165,7 +165,7 @@ namespace Squidex.Areas.Api.Controllers.Rules { var command = new EnableRule { RuleId = id }; - var response = await InvokeCommand(app, command); + var response = await InvokeCommandAsync(app, command); return Ok(response); } @@ -190,7 +190,7 @@ namespace Squidex.Areas.Api.Controllers.Rules { var command = new DisableRule { RuleId = id }; - var response = await InvokeCommand(app, command); + var response = await InvokeCommandAsync(app, command); return Ok(response); } @@ -296,7 +296,7 @@ namespace Squidex.Areas.Api.Controllers.Rules return NoContent(); } - private async Task InvokeCommand(string app, RuleCommand command) + private async Task InvokeCommandAsync(string app, ICommand command) { var context = await CommandBus.PublishAsync(command); diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs b/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs index 74a36f916..7a5b1ca80 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Squidex.Areas.Api.Controllers.Schemas.Models; +using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure.Commands; using Squidex.Shared; @@ -40,17 +41,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPost] [Route("apps/{app}/schemas/{name}/fields/")] - [ProducesResponseType(typeof(EntityCreatedDto), 201)] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 409)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PostField(string app, string name, [FromBody] AddFieldDto request) { - var context = await CommandBus.PublishAsync(request.ToCommand()); + var command = request.ToCommand(); - var result = context.Result>(); - var response = EntityCreatedDto.FromResult(result); + var response = await InvokeCommandAsync(app, command); return StatusCode(201, response); } @@ -70,17 +70,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPost] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/")] - [ProducesResponseType(typeof(EntityCreatedDto), 201)] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 409)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PostNestedField(string app, string name, long parentId, [FromBody] AddFieldDto request) { - var context = await CommandBus.PublishAsync(request.ToCommand(parentId)); + var command = request.ToCommand(parentId); - var result = context.Result>(); - var response = EntityCreatedDto.FromResult(result); + var response = await InvokeCommandAsync(app, command); return StatusCode(201, response); } @@ -92,20 +91,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The request that contains the field ids. /// - /// 204 => Schema fields reorderd. + /// 200 => Schema fields reorderd. /// 400 => Schema field ids do not cover the fields of the schema. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/ordering/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchemaFieldOrdering(string app, string name, [FromBody] ReorderFieldsDto request) { - await CommandBus.PublishAsync(request.ToCommand()); + var command = request.ToCommand(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -116,20 +118,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The parent field id. /// The request that contains the field ids. /// - /// 204 => Schema fields reorderd. + /// 200 => Schema fields reorderd. /// 400 => Schema field ids do not cover the fields of the schema. /// 404 => Schema, field or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/ordering/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutNestedFieldOrdering(string app, string name, long parentId, [FromBody] ReorderFieldsDto request) { - await CommandBus.PublishAsync(request.ToCommand(parentId)); + var command = request.ToCommand(parentId); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -140,20 +145,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The id of the field to update. /// The field object that needs to be added to the schema. /// - /// 204 => Schema field updated. + /// 200 => Schema field updated. /// 400 => Schema field properties not valid or field is locked. /// 404 => Schema, field or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutField(string app, string name, long id, [FromBody] UpdateFieldDto request) { - await CommandBus.PublishAsync(request.ToCommand(id)); + var command = request.ToCommand(id); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -165,20 +173,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The id of the field to update. /// The field object that needs to be added to the schema. /// - /// 204 => Schema field updated. + /// 200 => Schema field updated. /// 400 => Schema field properties not valid or field is locked. /// 404 => Schema, field or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutNestedField(string app, string name, long parentId, long id, [FromBody] UpdateFieldDto request) { - await CommandBus.PublishAsync(request.ToCommand(id, parentId)); + var command = request.ToCommand(id, parentId); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -188,7 +199,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The id of the field to lock. /// - /// 204 => Schema field shown. + /// 200 => Schema field shown. /// 400 => Schema field already locked. /// 404 => Schema, field or app not found. /// @@ -197,14 +208,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/lock/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task LockField(string app, string name, long id) { - await CommandBus.PublishAsync(new LockField { FieldId = id }); + var command = new LockField { FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -215,7 +229,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The parent field id. /// The id of the field to lock. /// - /// 204 => Schema field hidden. + /// 200 => Schema field hidden. /// 400 => Schema field already hidden. /// 404 => Field, schema, or app not found. /// @@ -224,14 +238,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/lock/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task LockNestedField(string app, string name, long parentId, long id) { - await CommandBus.PublishAsync(new LockField { ParentFieldId = parentId, FieldId = id }); + var command = new LockField { ParentFieldId = parentId, FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -241,7 +258,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The id of the field to hide. /// - /// 204 => Schema field hidden. + /// 200 => Schema field hidden. /// 400 => Schema field already hidden. /// 404 => Schema, field or app not found. /// @@ -250,14 +267,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/hide/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task HideField(string app, string name, long id) { - await CommandBus.PublishAsync(new HideField { FieldId = id }); + var command = new HideField { FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -268,7 +288,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The parent field id. /// The id of the field to hide. /// - /// 204 => Schema field hidden. + /// 200 => Schema field hidden. /// 400 => Schema field already hidden. /// 404 => Field, schema, or app not found. /// @@ -277,14 +297,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/hide/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task HideNestedField(string app, string name, long parentId, long id) { - await CommandBus.PublishAsync(new HideField { ParentFieldId = parentId, FieldId = id }); + var command = new HideField { ParentFieldId = parentId, FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -294,7 +317,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The id of the field to show. /// - /// 204 => Schema field shown. + /// 200 => Schema field shown. /// 400 => Schema field already visible. /// 404 => Schema, field or app not found. /// @@ -303,14 +326,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/show/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task ShowField(string app, string name, long id) { - await CommandBus.PublishAsync(new ShowField { FieldId = id }); + var command = new ShowField { FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -321,7 +347,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The parent field id. /// The id of the field to show. /// - /// 204 => Schema field shown. + /// 200 => Schema field shown. /// 400 => Schema field already visible. /// 404 => Schema, field or app not found. /// @@ -335,9 +361,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas [ApiCosts(1)] public async Task ShowNestedField(string app, string name, long parentId, long id) { - await CommandBus.PublishAsync(new ShowField { ParentFieldId = parentId, FieldId = id }); + var command = new ShowField { ParentFieldId = parentId, FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -347,7 +375,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The id of the field to enable. /// - /// 204 => Schema field enabled. + /// 200 => Schema field enabled. /// 400 => Schema field already enabled. /// 404 => Schema, field or app not found. /// @@ -356,14 +384,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/enable/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task EnableField(string app, string name, long id) { - await CommandBus.PublishAsync(new EnableField { FieldId = id }); + var command = new EnableField { FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -374,7 +405,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The parent field id. /// The id of the field to enable. /// - /// 204 => Schema field enabled. + /// 200 => Schema field enabled. /// 400 => Schema field already enabled. /// 404 => Schema, field or app not found. /// @@ -383,14 +414,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/enable/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task EnableNestedField(string app, string name, long parentId, long id) { - await CommandBus.PublishAsync(new EnableField { ParentFieldId = parentId, FieldId = id }); + var command = new EnableField { ParentFieldId = parentId, FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -400,7 +434,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The id of the field to disable. /// - /// 204 => Schema field disabled. + /// 200 => Schema field disabled. /// 400 => Schema field already disabled. /// 404 => Schema, field or app not found. /// @@ -409,14 +443,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/disable/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DisableField(string app, string name, long id) { - await CommandBus.PublishAsync(new DisableField { FieldId = id }); + var command = new DisableField { FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -427,7 +464,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The parent field id. /// The id of the field to disable. /// - /// 204 => Schema field disabled. + /// 200 => Schema field disabled. /// 400 => Schema field already disabled. /// 404 => Schema, field or app not found. /// @@ -436,14 +473,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/disable/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DisableNestedField(string app, string name, long parentId, long id) { - await CommandBus.PublishAsync(new DisableField { ParentFieldId = parentId, FieldId = id }); + var command = new DisableField { ParentFieldId = parentId, FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -453,19 +493,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The id of the field to disable. /// - /// 204 => Schema field deleted. + /// 200 => Schema field deleted. /// 400 => Field is locked. /// 404 => Schema, field or app not found. /// [HttpDelete] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] + [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DeleteField(string app, string name, long id) { - await CommandBus.PublishAsync(new DeleteField { FieldId = id }); + var command = new DeleteField { FieldId = id }; - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -476,12 +520,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The parent field id. /// The id of the field to disable. /// - /// 204 => Schema field deleted. + /// 200 => Schema field deleted. /// 400 => Field is locked. /// 404 => Schema, field or app not found. /// [HttpDelete] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] + [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DeleteNestedField(string app, string name, long parentId, long id) @@ -490,5 +536,15 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + + private async Task InvokeCommandAsync(string app, ICommand command) + { + var context = await CommandBus.PublishAsync(command); + + var response = context.Result(); + var response = SchemaDetailsDto.FromSchemaWithDetails(response, this, app); + + return response; + } } } \ No newline at end of file diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs b/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs index 7320e4484..e13e82131 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs @@ -109,7 +109,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPost] [Route("apps/{app}/schemas/")] - [ProducesResponseType(typeof(EntityCreatedDto), 201)] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ProducesResponseType(typeof(ErrorDto), 409)] [ApiPermission(Permissions.AppSchemasCreate)] @@ -117,10 +117,8 @@ namespace Squidex.Areas.Api.Controllers.Schemas public async Task PostSchema(string app, [FromBody] CreateSchemaDto request) { var command = request.ToCommand(); - var context = await CommandBus.PublishAsync(command); - var result = context.Result>(); - var response = new EntityCreatedDto { Id = command.SchemaId.ToString(), Version = result.Version }; + var response = await InvokeCommandAsync(app, command); return CreatedAtAction(nameof(GetSchema), new { name = request.Name }, response); } @@ -138,13 +136,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] + [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchema(string app, string name, [FromBody] UpdateSchemaDto request) { - await CommandBus.PublishAsync(request.ToCommand()); + var command = request.ToCommand(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -154,19 +156,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The schema object that needs to updated. /// - /// 204 => Schema updated. + /// 200 => Schema updated. /// 400 => Schema properties are not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/sync")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] + [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchemaSync(string app, string name, [FromBody] SynchronizeSchemaDto request) { - await CommandBus.PublishAsync(request.ToCommand()); + var command = request.ToCommand(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -176,18 +182,22 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The schema object that needs to updated. /// - /// 204 => Schema updated. + /// 200 => Schema updated. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/category")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] + [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutCategory(string app, string name, [FromBody] ChangeCategoryDto request) { - await CommandBus.PublishAsync(request.ToCommand()); + var command = request.ToCommand(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -197,18 +207,22 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The preview urls for the schema. /// - /// 204 => Schema updated. + /// 200 => Schema updated. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/preview-urls")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] + [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutPreviewUrls(string app, string name, [FromBody] ConfigurePreviewUrlsDto request) { - await CommandBus.PublishAsync(request.ToCommand()); + var command = request.ToCommand(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -218,19 +232,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The schema scripts object that needs to updated. /// - /// 204 => Schema updated. + /// 200 => Schema updated. /// 400 => Schema properties are not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/scripts/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] + [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasScripts)] [ApiCosts(1)] public async Task PutSchemaScripts(string app, string name, [FromBody] SchemaScriptsDto request) { - await CommandBus.PublishAsync(request.ToCommand()); + var command = request.ToCommand(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -239,20 +257,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the app. /// The name of the schema to publish. /// - /// 204 => Schema has been published. + /// 200 => Schema has been published. /// 400 => Schema is already published. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/publish/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasPublish)] [ApiCosts(1)] public async Task PublishSchema(string app, string name) { - await CommandBus.PublishAsync(new PublishSchema()); + var command = new PublishSchema(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -261,20 +282,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the app. /// The name of the schema to unpublish. /// - /// 204 => Schema has been unpublished. + /// 200 => Schema has been unpublished. /// 400 => Schema is not published. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/unpublish/")] + [ProducesResponseType(typeof(SchemaDetailsDto), 200)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasPublish)] [ApiCosts(1)] public async Task UnpublishSchema(string app, string name) { - await CommandBus.PublishAsync(new UnpublishSchema()); + var command = new UnpublishSchema(); - return NoContent(); + var response = await InvokeCommandAsync(app, command); + + return Ok(response); } /// @@ -296,5 +320,15 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + + private async Task InvokeCommandAsync(string app, ICommand command) + { + var context = await CommandBus.PublishAsync(command); + + var result = context.Result(); + var response = SchemaDetailsDto.FromSchemaWithDetails(result, this, app); + + return response; + } } } \ No newline at end of file diff --git a/src/Squidex/app/features/administration/state/users.state.spec.ts b/src/Squidex/app/features/administration/state/users.state.spec.ts index 279ff67c2..257bf4c72 100644 --- a/src/Squidex/app/features/administration/state/users.state.spec.ts +++ b/src/Squidex/app/features/administration/state/users.state.spec.ts @@ -25,6 +25,8 @@ describe('UsersState', () => { const user1 = createUser(1); const user2 = createUser(2); + const oldUsers = new UsersDto(200, [user1, user2]); + const newUser = createUser(3); let dialogs: IMock; @@ -45,7 +47,7 @@ describe('UsersState', () => { describe('Loading', () => { it('should load users', () => { usersService.setup(x => x.getUsers(10, 0, undefined)) - .returns(() => of(new UsersDto(200, [user1, user2]))).verifiable(); + .returns(() => of(oldUsers)).verifiable(); usersState.load().subscribe(); @@ -58,7 +60,7 @@ describe('UsersState', () => { it('should show notification on load when reload is true', () => { usersService.setup(x => x.getUsers(10, 0, undefined)) - .returns(() => of(new UsersDto(200, [user1, user2]))).verifiable(); + .returns(() => of(oldUsers)).verifiable(); usersState.load(true).subscribe(); @@ -74,7 +76,7 @@ describe('UsersState', () => { ]; usersService.setup(x => x.getUsers(10, 0, undefined)) - .returns(() => of(new UsersDto(200, [user1, user2]))).verifiable(Times.exactly(2)); + .returns(() => of(oldUsers)).verifiable(Times.exactly(2)); usersService.setup(x => x.getUsers(10, 0, undefined)) .returns(() => of(new UsersDto(200, newUsers))); @@ -88,7 +90,7 @@ describe('UsersState', () => { it('should load next page and prev page when paging', () => { usersService.setup(x => x.getUsers(10, 0, undefined)) - .returns(() => of(new UsersDto(200, [user1, user2]))).verifiable(Times.exactly(2)); + .returns(() => of(oldUsers)).verifiable(Times.exactly(2)); usersService.setup(x => x.getUsers(10, 10, undefined)) .returns(() => of(new UsersDto(200, []))).verifiable(); @@ -113,7 +115,7 @@ describe('UsersState', () => { describe('Updates', () => { beforeEach(() => { usersService.setup(x => x.getUsers(10, 0, undefined)) - .returns(() => of(new UsersDto(200, [user1, user2]))).verifiable(); + .returns(() => of(oldUsers)).verifiable(); usersState.load().subscribe(); }); diff --git a/src/Squidex/app/shared/services/apps.service.spec.ts b/src/Squidex/app/shared/services/apps.service.spec.ts index ef1b4f3bb..cc254694b 100644 --- a/src/Squidex/app/shared/services/apps.service.spec.ts +++ b/src/Squidex/app/shared/services/apps.service.spec.ts @@ -102,8 +102,8 @@ describe('AppsService', () => { id: `id${id}`, name: `name${id}${suffix}`, permissions: ['Owner'], - created: `${id % 1000 + 2000}-12-12T10:10`, - lastModified: `${id % 1000 + 2000}-11-11T10:10`, + created: `${id % 1000 + 2000}-12-12T10:10:00`, + lastModified: `${id % 1000 + 2000}-11-11T10:10:00`, canAccessApi: id % 2 === 0, canAccessContent: id % 2 === 0, planName: 'Free', @@ -120,8 +120,8 @@ export function createApp(id: number, suffix = '') { `id${id}`, `name${id}${suffix}`, ['Owner'], - DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10`), - DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10`), + DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10:00`), + DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`), id % 2 === 0, id % 2 === 0, 'Free', diff --git a/src/Squidex/app/shared/services/apps.service.ts b/src/Squidex/app/shared/services/apps.service.ts index 0e1975183..06e9fd41a 100644 --- a/src/Squidex/app/shared/services/apps.service.ts +++ b/src/Squidex/app/shared/services/apps.service.ts @@ -95,8 +95,8 @@ function parseApp(response: any) { response.id, response.name, response.permissions, - DateTime.parseISO(response.created), - DateTime.parseISO(response.lastModified), + DateTime.parseISO_UTC(response.created), + DateTime.parseISO_UTC(response.lastModified), response.canAccessApi, response.canAccessContent, response.planName, diff --git a/src/Squidex/app/shared/services/assets.service.spec.ts b/src/Squidex/app/shared/services/assets.service.spec.ts index f2bb4cfc6..d63540d8a 100644 --- a/src/Squidex/app/shared/services/assets.service.spec.ts +++ b/src/Squidex/app/shared/services/assets.service.spec.ts @@ -305,9 +305,9 @@ describe('AssetsService', () => { function assetResponse(id: number, suffix = '') { return { id: `id${id}`, - created: `${id % 1000 + 2000}-12-12T10:10`, + created: `${id % 1000 + 2000}-12-12T10:10:00`, createdBy: `creator-${id}`, - lastModified: `${id % 1000 + 2000}-11-11T10:10`, + lastModified: `${id % 1000 + 2000}-11-11T10:10:00`, lastModifiedBy: `modifier-${id}`, fileName: `My Name${id}${suffix}.png`, fileHash: `My Hash${id}${suffix}`, @@ -334,10 +334,8 @@ describe('AssetsService', () => { export function createAsset(id: number, tags?: string[], suffix = '') { const result = new AssetDto( `id${id}`, - `creator-${id}`, - `modifier-${id}`, - DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10`), - DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10`), + DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10:00`), `creator-${id}`, + DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`), `modifier-${id}`, `My Name${id}${suffix}.png`, `My Hash${id}${suffix}`, 'png', diff --git a/src/Squidex/app/shared/services/assets.service.ts b/src/Squidex/app/shared/services/assets.service.ts index c74866c18..14ffe966a 100644 --- a/src/Squidex/app/shared/services/assets.service.ts +++ b/src/Squidex/app/shared/services/assets.service.ts @@ -42,10 +42,10 @@ export class AssetDto { constructor( public readonly id: string, - public readonly createdBy: string, - public readonly lastModifiedBy: string, public readonly created: DateTime, + public readonly createdBy: string, public readonly lastModified: DateTime, + public readonly lastModifiedBy: string, public readonly fileName: string, public readonly fileHash: string, public readonly fileType: string, @@ -252,10 +252,8 @@ function parseAsset(response: any) { return withLinks( new AssetDto( response.id, - response.createdBy, - response.lastModifiedBy, - DateTime.parseISO_UTC(response.created), - DateTime.parseISO_UTC(response.lastModified), + DateTime.parseISO_UTC(response.created), response.createdBy, + DateTime.parseISO_UTC(response.lastModified), response.lastModifiedBy, response.fileName, response.fileHash, response.fileType, diff --git a/src/Squidex/app/shared/services/schemas.service.spec.ts b/src/Squidex/app/shared/services/schemas.service.spec.ts index 21e69270d..94d094e2f 100644 --- a/src/Squidex/app/shared/services/schemas.service.spec.ts +++ b/src/Squidex/app/shared/services/schemas.service.spec.ts @@ -13,20 +13,20 @@ import { ApiUrlConfig, createProperties, DateTime, - FieldDto, NestedFieldDto, + Resource, RootFieldDto, SchemaDetailsDto, SchemaDto, SchemaPropertiesDto, + SchemasDto, SchemasService, - Version, - Versioned + Version } from '@app/shared/internal'; -import { SchemaCreatedDto } from './schemas.service'; describe('SchemasService', () => { const version = new Version('1'); + const versionNew = new Version('2'); beforeEach(() => { TestBed.configureTestingModule({ @@ -54,7 +54,7 @@ describe('SchemasService', () => { it('should make get request to get schemas', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - let schemas: SchemaDto[]; + let schemas: SchemasDto; schemasService.getSchemas('my-app').subscribe(result => { schemas = result; @@ -65,54 +65,20 @@ describe('SchemasService', () => { expect(req.request.method).toEqual('GET'); expect(req.request.headers.get('If-Match')).toBeNull(); - req.flush([ - { - id: 'id1', - name: 'name1', - category: 'category1', - properties: { - label: 'label1', - hints: 'hints1' - }, - isSingleton: true, - isPublished: true, - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - version: 11, - data: {} - }, - { - id: 'id2', - name: 'name2', - category: 'category2', - properties: { - label: 'label2', - hints: 'hints2' - }, - isSingleton: true, - isPublished: true, - created: '2016-10-12T10:10', - createdBy: 'Created2', - lastModified: '2017-10-12T10:10', - lastModifiedBy: 'LastModifiedBy2', - version: 22, - data: {} - } - ]); - - expect(schemas!).toEqual( - [ - new SchemaDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, true, - DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1', - DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1', - new Version('11')), - new SchemaDto('id2', 'name2', 'category2', new SchemaPropertiesDto('label2', 'hints2'), true, true, - DateTime.parseISO_UTC('2016-10-12T10:10'), 'Created2', - DateTime.parseISO_UTC('2017-10-12T10:10'), 'LastModifiedBy2', - new Version('22')) - ]); + req.flush({ + items: [ + schemaResponse(12), + schemaResponse(13) + ] + }); + + expect(schemas!).toEqual({ + items: [ + createSchema(12), + createSchema(13) + ], + _links: {} + }); })); it('should make get request to get schema', @@ -129,200 +95,13 @@ describe('SchemasService', () => { expect(req.request.method).toEqual('GET'); expect(req.request.headers.get('If-Match')).toBeNull(); - req.flush({ - id: 'id1', - name: 'name1', - category: 'category1', - isSingleton: true, - isPublished: true, - created: '2016-12-12T10:10', - createdBy: 'Created1', - lastModified: '2017-12-12T10:10', - lastModifiedBy: 'LastModifiedBy1', - properties: { - label: 'label1', - hints: 'hints1' - }, - previewUrls: { - 'Default': 'url' - }, - fields: [ - { - fieldId: 11, - name: 'field11', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Array' - }, - nested: [ - { - fieldId: 101, - name: 'field101', - isLocked: true, - isHidden: true, - isDisabled: true, - properties: { - fieldType: 'String' - } - }, - { - fieldId: 102, - name: 'field102', - isLocked: true, - isHidden: true, - isDisabled: true, - properties: { - fieldType: 'Number' - } - } - ] - }, - { - fieldId: 12, - name: 'field12', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Assets' - } - }, - { - fieldId: 13, - name: 'field13', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Boolean' - } - }, - { - fieldId: 14, - name: 'field14', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'DateTime' - } - }, - { - fieldId: 15, - name: 'field15', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Geolocation' - } - }, - { - fieldId: 16, - name: 'field16', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Json' - } - }, - { - fieldId: 17, - name: 'field17', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Number' - } - }, - { - fieldId: 18, - name: 'field18', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'References' - } - }, - { - fieldId: 19, - name: 'field19', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'String' - } - }, - { - fieldId: 20, - name: 'field20', - isLocked: true, - isHidden: true, - isDisabled: true, - partitioning: 'language', - properties: { - fieldType: 'Tags' - } - } - ], - scripts: { - query: '', - create: '', - change: '', - delete: '', - update: '' - } - }, { + req.flush(schemaDetailsResponse(12), { headers: { etag: '2' } }); - expect(schema!).toEqual( - new SchemaDetailsDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, true, - DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1', - DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1', - new Version('2'), - [ - new RootFieldDto(11, 'field11', createProperties('Array'), 'language', true, true, true, [ - new NestedFieldDto(101, 'field101', createProperties('String'), 11, true, true, true), - new NestedFieldDto(102, 'field102', createProperties('Number'), 11, true, true, true) - ]), - new RootFieldDto(12, 'field12', createProperties('Assets'), 'language', true, true, true), - new RootFieldDto(13, 'field13', createProperties('Boolean'), 'language', true, true, true), - new RootFieldDto(14, 'field14', createProperties('DateTime'), 'language', true, true, true), - new RootFieldDto(15, 'field15', createProperties('Geolocation'), 'language', true, true, true), - new RootFieldDto(16, 'field16', createProperties('Json'), 'language', true, true, true), - new RootFieldDto(17, 'field17', createProperties('Number'), 'language', true, true, true), - new RootFieldDto(18, 'field18', createProperties('References'), 'language', true, true, true), - new RootFieldDto(19, 'field19', createProperties('String'), 'language', true, true, true), - new RootFieldDto(20, 'field20', createProperties('Tags'), 'language', true, true, true) - ], - { - query: '', - create: '', - change: '', - delete: '', - update: '' - }, - { - 'Default': 'url' - })); + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make post request to create schema', @@ -330,7 +109,7 @@ describe('SchemasService', () => { const dto = { name: 'name' }; - let schema: Versioned; + let schema: SchemaDetailsDto; schemasService.postSchema('my-app', dto).subscribe(result => { schema = result; @@ -341,20 +120,13 @@ describe('SchemasService', () => { expect(req.request.method).toEqual('POST'); expect(req.request.headers.get('If-Match')).toBeNull(); - req.flush({ - id: '1' - }, { + req.flush(schemaDetailsResponse(12), { headers: { - etag: '1' + etag: '2' } }); - expect(schema!).toEqual({ - payload: { - id: '1' - }, - version - }); + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to update schema', @@ -362,14 +134,30 @@ describe('SchemasService', () => { const dto = { label: 'label1' }; - schemasService.putSchema('my-app', 'my-schema', dto, version).subscribe(); + const resource: Resource = { + _links: { + update: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema' } + } + }; + + let schema: SchemaDetailsDto; + + schemasService.putSchema('my-app', resource, dto, version).subscribe(result => { + schema = result; + }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to update schema scripts', @@ -377,14 +165,30 @@ describe('SchemasService', () => { const dto = {}; - schemasService.putScripts('my-app', 'my-schema', dto, version).subscribe(); + const resource: Resource = { + _links: { + updateScripts: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/scripts' } + } + }; + + let schema: SchemaDetailsDto; + + schemasService.putScripts('my-app', resource, dto, version).subscribe(result => { + schema = result; + }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/scripts'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to update category', @@ -392,14 +196,30 @@ describe('SchemasService', () => { const dto = {}; - schemasService.putCategory('my-app', 'my-schema', dto, version).subscribe(); + const resource: Resource = { + _links: { + updateCategory: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/category' } + } + }; + + let schema: SchemaDetailsDto; + + schemasService.putCategory('my-app', resource, dto, version).subscribe(result => { + schema = result; + }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/category'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to update preview urls', @@ -407,14 +227,30 @@ describe('SchemasService', () => { const dto = {}; - schemasService.putPreviewUrls('my-app', 'my-schema', dto, version).subscribe(); + const resource: Resource = { + _links: { + updateUrls: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/preview-urls' } + } + }; + + let schema: SchemaDetailsDto; + + schemasService.putPreviewUrls('my-app', resource, dto, version).subscribe(result => { + schema = result; + }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/preview-urls'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make post request to add field', @@ -422,10 +258,16 @@ describe('SchemasService', () => { const dto = { name: 'name', partitioning: 'invariant', properties: createProperties('Number') }; - let field: FieldDto; + const resource: Resource = { + _links: { + addField: { method: 'POST', href: '/api/apps/my-app/schemas/my-schema/fields' } + } + }; + + let schema: SchemaDetailsDto; - schemasService.postField('my-app', 'my-schema', dto, undefined, version).subscribe(result => { - field = result.payload; + schemasService.postField('my-app', resource, dto, version).subscribe(result => { + schema = result; }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields'); @@ -433,86 +275,102 @@ describe('SchemasService', () => { expect(req.request.method).toEqual('POST'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({ id: 123 }); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); - expect(field!).toEqual(new RootFieldDto(123, dto.name, dto.properties, dto.partitioning)); + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to publish schema', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.publishSchema('my-app', 'my-schema', version).subscribe(); + const resource: Resource = { + _links: { + publish: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/publish' } + } + }; + + let schema: SchemaDetailsDto; + + schemasService.publishSchema('my-app', resource, version).subscribe(result => { + schema = result; + }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/publish'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to unpublish schema', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.unpublishSchema('my-app', 'my-schema', version).subscribe(); + const resource: Resource = { + _links: { + unpublish: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/unpublish' } + } + }; + + let schema: SchemaDetailsDto; + + schemasService.unpublishSchema('my-app', resource, version).subscribe(result => { + schema = result; + }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/unpublish'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); - it('should make post request to add nested field', + it('should make put request to update field', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - const dto = { name: 'name', partitioning: 'invariant', properties: createProperties('Number') }; + const dto = { properties: createProperties('Number') }; + + const resource: Resource = { + _links: { + update: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/fields/1' } + } + }; - let field: FieldDto; + let schema: SchemaDetailsDto; - schemasService.postField('my-app', 'my-schema', dto, 13, version).subscribe(result => { - field = result.payload; + schemasService.putField('my-app', resource, dto, version).subscribe(result => { + schema = result; }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1'); - expect(req.request.method).toEqual('POST'); + expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({ id: 123 }); - - expect(field!).toEqual(new NestedFieldDto(123, dto.name, dto.properties, 13)); - })); - - it('should make put request to update field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - - const dto = { properties: createProperties('Number') }; - - schemasService.putField('my-app', 'my-schema', 1, dto, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); - - it('should make put request to update nested field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - - const dto = { properties: createProperties('Number') }; - - schemasService.putField('my-app', 'my-schema', 1, dto, 13, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); - req.flush({}); + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to update field ordering', @@ -520,191 +378,216 @@ describe('SchemasService', () => { const dto = [1, 2, 3]; - schemasService.putFieldOrdering('my-app', 'my-schema', dto, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/ordering'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); - - it('should make put request to update nested field ordering', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + const resource: Resource = { + _links: { + order: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/fields/ordering' } + } + }; - const dto = [1, 2, 3]; + let schema: SchemaDetailsDto; - schemasService.putFieldOrdering('my-app', 'my-schema', dto, 13, version).subscribe(); + schemasService.putFieldOrdering('my-app', resource, dto, version).subscribe(result => { + schema = result; + }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/ordering'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/ordering'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to lock field', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.lockField('my-app', 'my-schema', 1, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/lock'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); + const resource: Resource = { + _links: { + lock: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/fields/1/lock' } + } + }; - it('should make put request to lock nested field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + let schema: SchemaDetailsDto; - schemasService.lockField('my-app', 'my-schema', 1, 13, version).subscribe(); + schemasService.lockField('my-app', resource, version).subscribe(result => { + schema = result; + }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/lock'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/lock'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to enable field', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.enableField('my-app', 'my-schema', 1, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/enable'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); + const resource: Resource = { + _links: { + enable: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/fields/1/enable' } + } + }; - it('should make put request to enable nested field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + let schema: SchemaDetailsDto; - schemasService.enableField('my-app', 'my-schema', 1, 13, version).subscribe(); + schemasService.enableField('my-app', resource, version).subscribe(result => { + schema = result; + }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/enable'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/enable'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to disable field', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.disableField('my-app', 'my-schema', 1, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/disable'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); + const resource: Resource = { + _links: { + disable: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/fields/1/disable' } + } + }; - it('should make put request to disable nested field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + let schema: SchemaDetailsDto; - schemasService.disableField('my-app', 'my-schema', 1, 13, version).subscribe(); + schemasService.disableField('my-app', resource, version).subscribe(result => { + schema = result; + }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/disable'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/disable'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to show field', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.showField('my-app', 'my-schema', 1, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/show'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); + const resource: Resource = { + _links: { + show: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/fields/1/show' } + } + }; - it('should make put request to show nested field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + let schema: SchemaDetailsDto; - schemasService.showField('my-app', 'my-schema', 1, 13, version).subscribe(); + schemasService.showField('my-app', resource, version).subscribe(result => { + schema = result; + }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/show'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/show'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make put request to hide field', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.hideField('my-app', 'my-schema', 1, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/hide'); - - expect(req.request.method).toEqual('PUT'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); + const resource: Resource = { + _links: { + hide: { method: 'PUT', href: '/api/apps/my-app/schemas/my-schema/fields/1/hide' } + } + }; - it('should make put request to hide nested field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + let schema: SchemaDetailsDto; - schemasService.hideField('my-app', 'my-schema', 1, 13, version).subscribe(); + schemasService.hideField('my-app', resource, version).subscribe(result => { + schema = result; + }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/hide'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/hide'); expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make delete request to delete field', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.deleteField('my-app', 'my-schema', 1, undefined, version).subscribe(); - - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1'); - - expect(req.request.method).toEqual('DELETE'); - expect(req.request.headers.get('If-Match')).toBe(version.value); - - req.flush({}); - })); + const resource: Resource = { + _links: { + delete: { method: 'DELETE', href: '/api/apps/my-app/schemas/my-schema/fields/1' } + } + }; - it('should make delete request to delete nested field', - inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + let schema: SchemaDetailsDto; - schemasService.deleteField('my-app', 'my-schema', 1, 13, version).subscribe(); + schemasService.deleteField('my-app', resource, version).subscribe(result => { + schema = result; + }); - const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1'); + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1'); expect(req.request.method).toEqual('DELETE'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({}); + req.flush(schemaDetailsResponse(12), { + headers: { + etag: '2' + } + }); + + expect(schema!).toEqual(createSchemaDetails(12, versionNew)); })); it('should make delete request to delete schema', inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { - schemasService.deleteSchema('my-app', 'my-schema', version).subscribe(); + const resource: Resource = { + _links: { + delete: { method: 'DELETE', href: '/api/apps/my-app/schemas/my-schema' } + } + }; + + schemasService.deleteSchema('my-app', resource, version).subscribe(); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema'); @@ -713,4 +596,250 @@ describe('SchemasService', () => { req.flush({}); })); -}); \ No newline at end of file + + function schemaResponse(id: number, suffix = '') { + return { + id: `schema-id${id}${suffix}`, + name: `schema-name${id}${suffix}`, + category: `category${id}${suffix}`, + isSingleton: id % 2 === 0, + isPublished: id % 3 === 0, + created: `${id % 1000 + 2000}-12-12T10:10:00`, + createdBy: `creator-${id}`, + lastModified: `${id % 1000 + 2000}-11-11T10:10:00`, + lastModifiedBy: `modifier-${id}`, + properties: { + label: `label${id}${suffix}`, + hints: `hints${id}${suffix}` + }, + version: `${id}`, + _links: { + update: { method: 'PUT', href: `/schemas/${id}` } + } + }; + } + + function schemaDetailsResponse(id: number, suffix = '') { + return { + id: `schema-id${id}`, + name: `schema-name${id}${suffix}`, + category: `category${id}${suffix}`, + isSingleton: id % 2 === 0, + isPublished: id % 3 === 0, + created: `${id % 1000 + 2000}-12-12T10:10:00`, + createdBy: `creator-${id}`, + lastModified: `${id % 1000 + 2000}-11-11T10:10:00`, + lastModifiedBy: `modifier-${id}`, + properties: { + label: `label${id}${suffix}`, + hints: `hints${id}${suffix}` + }, + previewUrls: { + 'Default': 'url' + }, + fields: [ + { + fieldId: 11, + name: 'field11', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'Array' + }, + nested: [ + { + fieldId: 101, + name: 'field101', + isLocked: true, + isHidden: true, + isDisabled: true, + properties: { + fieldType: 'String' + } + }, + { + fieldId: 102, + name: 'field102', + isLocked: true, + isHidden: true, + isDisabled: true, + properties: { + fieldType: 'Number' + } + } + ] + }, + { + fieldId: 12, + name: 'field12', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'Assets' + } + }, + { + fieldId: 13, + name: 'field13', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'Boolean' + } + }, + { + fieldId: 14, + name: 'field14', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'DateTime' + } + }, + { + fieldId: 15, + name: 'field15', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'Geolocation' + } + }, + { + fieldId: 16, + name: 'field16', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'Json' + } + }, + { + fieldId: 17, + name: 'field17', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'Number' + } + }, + { + fieldId: 18, + name: 'field18', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'References' + } + }, + { + fieldId: 19, + name: 'field19', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'String' + } + }, + { + fieldId: 20, + name: 'field20', + isLocked: true, + isHidden: true, + isDisabled: true, + partitioning: 'language', + properties: { + fieldType: 'Tags' + } + } + ], + scripts: { + query: '', + create: '', + change: '', + delete: '', + update: '' + }, + _links: { + update: { method: 'PUT', href: `/schemas/${id}` } + } + }; + } +}); + +export function createSchema(id: number, suffix = '') { + const result = new SchemaDto( + `schema-id${id}`, + `schema-name${id}${suffix}`, + `category${id}${suffix}`, + new SchemaPropertiesDto(`label${id}${suffix}`, `hints${id}${suffix}`), + id % 2 === 0, + id % 3 === 0, + DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10:00`), `creator-${id}`, + DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`), `modifier-${id}`, + new Version(`${id}`)); + + result._links['update'] = { method: 'PUT', href: `/schemas/${id}` }; + + return result; +} + +export function createSchemaDetails(id: number, version: Version, suffix = '') { + const result = new SchemaDetailsDto( + `schema-id${id}`, + `schema-name${id}${suffix}`, + `category${id}${suffix}`, + new SchemaPropertiesDto(`label${id}${suffix}`, `hints${id}${suffix}`), + id % 2 === 0, + id % 3 === 0, + DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10:00`), `creator-${id}`, + DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`), `modifier-${id}`, + version, + [ + new RootFieldDto(11, 'field11', createProperties('Array'), 'language', true, true, true, [ + new NestedFieldDto(101, 'field101', createProperties('String'), 11, true, true, true), + new NestedFieldDto(102, 'field102', createProperties('Number'), 11, true, true, true) + ]), + new RootFieldDto(12, 'field12', createProperties('Assets'), 'language', true, true, true), + new RootFieldDto(13, 'field13', createProperties('Boolean'), 'language', true, true, true), + new RootFieldDto(14, 'field14', createProperties('DateTime'), 'language', true, true, true), + new RootFieldDto(15, 'field15', createProperties('Geolocation'), 'language', true, true, true), + new RootFieldDto(16, 'field16', createProperties('Json'), 'language', true, true, true), + new RootFieldDto(17, 'field17', createProperties('Number'), 'language', true, true, true), + new RootFieldDto(18, 'field18', createProperties('References'), 'language', true, true, true), + new RootFieldDto(19, 'field19', createProperties('String'), 'language', true, true, true), + new RootFieldDto(20, 'field20', createProperties('Tags'), 'language', true, true, true) + ], + { + query: '', + create: '', + change: '', + delete: '', + update: '' + }, + { + 'Default': 'url' + }); + + result._links['update'] = { method: 'PUT', href: `/schemas/${id}` }; + + return result; +} \ No newline at end of file diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index dcefef6bc..4b40c16db 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -15,17 +15,22 @@ import { ApiUrlConfig, DateTime, HTTP, - mapVersioned, - Model, pretifyError, + Resource, + ResourceLinks, StringHelper, Version, - Versioned + Versioned, + withLinks } from '@app/framework'; import { createProperties, FieldPropertiesDto } from './schemas.types'; -export class SchemaDto extends Model { +export type SchemasDto = { items: SchemaDto[] } & Resource; + +export class SchemaDto { + public readonly _links: ResourceLinks = {}; + public get displayName() { return StringHelper.firstNonEmpty(this.properties.label, this.name); } @@ -43,7 +48,6 @@ export class SchemaDto extends Model { public readonly lastModifiedBy: string, public readonly version: Version ) { - super(); } } @@ -66,32 +70,26 @@ export class SchemaDetailsDto extends SchemaDto { ) { super(id, name, category, properties, isSingleton, isPublished, created, createdBy, lastModified, lastModifiedBy, version); - this.onCloned(); - } + if (fields) { + let listFields = this.fields.filter(x => x.properties.isListField && x.properties.isContentField); - protected onCloned() { - if (this.fields) { - let fields = this.fields.filter(x => x.properties.isListField && x.properties.isContentField); - - if (fields.length === 0 && this.fields.length > 0) { - fields = [this.fields[0]]; + if (listFields.length === 0 && this.fields.length > 0) { + listFields = [this.fields[0]]; } - if (fields.length === 0) { - fields = NONE_FIELDS; + if (listFields.length === 0) { + listFields = NONE_FIELDS; } - this.listFields = fields; - this.listFieldsEditable = fields.filter(x => x.isInlineEditable); + this.listFields = listFields; + this.listFieldsEditable = listFields.filter(x => x.isInlineEditable); } } - - public with(value: Partial): SchemaDetailsDto { - return this.clone(value); - } } -export class FieldDto extends Model { +export class FieldDto { + public readonly _links: ResourceLinks = {}; + public get isInlineEditable(): boolean { return !this.isDisabled && this.properties['inlineEditable'] === true; } @@ -112,7 +110,6 @@ export class FieldDto extends Model { public readonly isHidden: boolean = false, public readonly isDisabled: boolean = false ) { - super(); } } @@ -142,10 +139,6 @@ export class RootFieldDto extends FieldDto { ) { super(fieldId, name, properties, isLocked, isHidden, isDisabled); } - - public with(value: Partial): RootFieldDto { - return this.clone(value); - } } const NONE_FIELD = new RootFieldDto(-1, '', createProperties('String'), 'invariant'); @@ -160,10 +153,6 @@ export class NestedFieldDto extends FieldDto { ) { super(fieldId, name, properties, isLocked, isHidden, isDisabled); } - - public with(value: Partial): NestedFieldDto { - return this.clone(value); - } } export class SchemaPropertiesDto { @@ -187,10 +176,6 @@ export interface CreateSchemaDto { readonly isSingleton?: boolean; } -export interface SchemaCreatedDto { - readonly id: string; -} - export interface UpdateSchemaCategoryDto { readonly name?: string; } @@ -213,192 +198,155 @@ export class SchemasService { ) { } - public getSchemas(appName: string): Observable { + public getSchemas(appName: string): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`); return HTTP.getVersioned(this.http, url).pipe( map(({ payload }) => { const body = payload.body; - const items: any[] = body; - - const schemas = items.map(item => { - const properties = new SchemaPropertiesDto(item.properties.label, item.properties.hints); - - return new SchemaDto( - item.id, - item.name, - item.category, properties, - item.isSingleton, - item.isPublished, - DateTime.parseISO_UTC(item.created), item.createdBy, - DateTime.parseISO_UTC(item.lastModified), item.lastModifiedBy, - new Version(item.version.toString())); - }); - - return schemas; + const items: any[] = body.items; + + const schemas = items.map(item => + withLinks( + new SchemaDto( + item.id, + item.name, + item.category, + new SchemaPropertiesDto(item.properties.label, item.properties.hints), + item.isSingleton, + item.isPublished, + DateTime.parseISO_UTC(item.created), item.createdBy, + DateTime.parseISO_UTC(item.lastModified), item.lastModifiedBy, + new Version(item.version.toString())), + item)); + + return withLinks({ items: schemas, _links: {} }, body); }), pretifyError('Failed to load schemas. Please reload.')); } - public getSchema(appName: string, id: string): Observable { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${id}`); + public getSchema(appName: string, name: string): Observable { + const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${name}`); return HTTP.getVersioned(this.http, url).pipe( map(({ version, payload }) => { - const body = payload.body; - - const fields = body.fields.map((item: any) => { - const propertiesDto = - createProperties( - item.properties.fieldType, - item.properties); - - let nested: NestedFieldDto[] | null = null; - - if (item.nested && item.nested.length > 0) { - nested = item.nested.map((nestedItem: any) => { - const nestedPropertiesDto = - createProperties( - nestedItem.properties.fieldType, - nestedItem.properties); - - return new NestedFieldDto( - nestedItem.fieldId, - nestedItem.name, - nestedPropertiesDto, - item.fieldId, - nestedItem.isLocked, - nestedItem.isHidden, - nestedItem.isDisabled); - }); - } - - return new RootFieldDto( - item.fieldId, - item.name, - propertiesDto, - item.partitioning, - item.isLocked, - item.isHidden, - item.isDisabled, - nested || []); - }); - - const properties = new SchemaPropertiesDto(body.properties.label, body.properties.hints); - - return new SchemaDetailsDto( - body.id, - body.name, - body.category, - properties, - body.isSingleton, - body.isPublished, - DateTime.parseISO_UTC(body.created), body.createdBy, - DateTime.parseISO_UTC(body.lastModified), body.lastModifiedBy, - version, - fields, - body.scripts || {}, - body.previewUrls || {}); + return parseSchemaWithDetails(payload.body, version); }), pretifyError('Failed to load schema. Please reload.')); } - public postSchema(appName: string, dto: CreateSchemaDto): Observable> { + public postSchema(appName: string, dto: CreateSchemaDto): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`); return HTTP.postVersioned(this.http, url, dto).pipe( - mapVersioned(({ body }) => body!), + map(({ version, payload }) => { + return parseSchemaWithDetails(payload.body, version); + }), tap(() => { this.analytics.trackEvent('Schema', 'Created', appName); }), pretifyError('Failed to create schema. Please reload.')); } - public deleteSchema(appName: string, schemaName: string, version: Version): Observable> { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); + public putScripts(appName: string, resource: Resource, dto: {}, version: Version): Observable { + const link = resource._links['updateScripts']; - return HTTP.deleteVersioned(this.http, url, version).pipe( - tap(() => { - this.analytics.trackEvent('Schema', 'Deleted', appName); - }), - pretifyError('Failed to delete schema. Please reload.')); - } + const url = this.apiUrl.buildUrl(link.href); - public putScripts(appName: string, schemaName: string, dto: {}, version: Version): Observable> { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/scripts`); - - return HTTP.putVersioned(this.http, url, dto, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'ScriptsConfigured', appName); }), pretifyError('Failed to update schema scripts. Please reload.')); } - public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version: Version): Observable> { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); + public putSchema(appName: string, resource: Resource, dto: UpdateSchemaDto, version: Version): Observable { + const link = resource._links['update']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, dto, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'Updated', appName); }), pretifyError('Failed to update schema. Please reload.')); } - public publishSchema(appName: string, schemaName: string, version: Version): Observable> { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/publish`); + public putCategory(appName: string, resource: Resource, dto: UpdateSchemaCategoryDto, version: Version): Observable { + const link = resource._links['updateCategory']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, {}, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { - this.analytics.trackEvent('Schema', 'Published', appName); + this.analytics.trackEvent('Schema', 'CategoryChanged', appName); }), - pretifyError('Failed to publish schema. Please reload.')); + pretifyError('Failed to change category. Please reload.')); } - public unpublishSchema(appName: string, schemaName: string, version: Version): Observable> { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/unpublish`); + public putPreviewUrls(appName: string, resource: Resource, dto: {}, version: Version): Observable { + const link = resource._links['updateUrls']; - return HTTP.putVersioned(this.http, url, {}, version).pipe( + const url = this.apiUrl.buildUrl(link.href); + + return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { - this.analytics.trackEvent('Schema', 'Unpublished', appName); + this.analytics.trackEvent('Schema', 'PreviewUrlsConfigured', appName); }), - pretifyError('Failed to unpublish schema. Please reload.')); + pretifyError('Failed to configure preview urls. Please reload.')); } - public putCategory(appName: string, schemaName: string, dto: UpdateSchemaCategoryDto, version: Version): Observable> { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/category`); + public publishSchema(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['publish']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, dto, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { - this.analytics.trackEvent('Schema', 'CategoryChanged', appName); + this.analytics.trackEvent('Schema', 'Published', appName); }), - pretifyError('Failed to change category. Please reload.')); + pretifyError('Failed to publish schema. Please reload.')); } - public putPreviewUrls(appName: string, schemaName: string, dto: {}, version: Version): Observable> { - const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/preview-urls`); + public unpublishSchema(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['unpublish']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, dto, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { - this.analytics.trackEvent('Schema', 'PreviewUrlsConfigured', appName); + this.analytics.trackEvent('Schema', 'Unpublished', appName); }), - pretifyError('Failed to configure preview urls. Please reload.')); + pretifyError('Failed to unpublish schema. Please reload.')); } - public postField(appName: string, schemaName: string, dto: AddFieldDto, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, ''); + public postField(appName: string, resource: Resource, dto: AddFieldDto, version: Version): Observable { + const link = resource._links['addField']; - return HTTP.postVersioned(this.http, url, dto, version).pipe( - mapVersioned(({ body }) => { - if (parentId) { - const field = new NestedFieldDto(body.id, dto.name, dto.properties, parentId); + const url = this.apiUrl.buildUrl(link.href); - return field; - } else { - const field = new RootFieldDto(body.id, dto.name, dto.properties, dto.partitioning); - - return field; - } + return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); }), tap(() => { this.analytics.trackEvent('Schema', 'FieldCreated', appName); @@ -406,92 +354,192 @@ export class SchemasService { pretifyError('Failed to add field. Please reload.')); } - public putFieldOrdering(appName: string, schemaName: string, dto: number[], parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, '/ordering'); + public putFieldOrdering(appName: string, resource: Resource, dto: number[], version: Version): Observable { + const link = resource._links['order']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, { fieldIds: dto }, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldsReordered', appName); }), pretifyError('Failed to reorder fields. Please reload.')); } - public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}`); + public putField(appName: string, resource: Resource, dto: UpdateFieldDto, version: Version): Observable { + const link = resource._links['update']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, dto, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldUpdated', appName); }), pretifyError('Failed to update field. Please reload.')); } - public lockField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/lock`); + public lockField(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['lock']; - return HTTP.putVersioned(this.http, url, {}, version).pipe( + const url = this.apiUrl.buildUrl(link.href); + + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldLocked', appName); }), pretifyError('Failed to lock field. Please reload.')); } - public enableField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/enable`); + public enableField(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['enable']; - return HTTP.putVersioned(this.http, url, {}, version).pipe( + const url = this.apiUrl.buildUrl(link.href); + + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldEnabled', appName); }), pretifyError('Failed to enable field. Please reload.')); } - public disableField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/disable`); + public disableField(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['disable']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, {}, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldDisabled', appName); }), pretifyError('Failed to disable field. Please reload.')); } - public showField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/show`); + public showField(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['show']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, {}, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldShown', appName); }), pretifyError('Failed to show field. Please reload.')); } - public hideField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/hide`); + public hideField(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['hide']; + + const url = this.apiUrl.buildUrl(link.href); - return HTTP.putVersioned(this.http, url, {}, version).pipe( + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldHidden', appName); }), pretifyError('Failed to hide field. Please reload.')); } - public deleteField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable> { - const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}`); + public deleteField(appName: string, resource: Resource, version: Version): Observable { + const link = resource._links['delete']; - return HTTP.deleteVersioned(this.http, url, version).pipe( + const url = this.apiUrl.buildUrl(link.href); + + return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( + map(({ version: newVersion, payload }) => { + return parseSchemaWithDetails(payload.body, newVersion); + }), tap(() => { this.analytics.trackEvent('Schema', 'FieldDeleted', appName); }), pretifyError('Failed to delete field. Please reload.')); } - private buildUrl(appName: string, schemaName: string, parentId: number | undefined, suffix: string) { - const url = - parentId ? - this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${parentId}/nested${suffix}`) : - this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields${suffix}`); + public deleteSchema(appName: string, resource: Resource, version: Version): Observable> { + const link = resource._links['delete']; - return url; + const url = this.apiUrl.buildUrl(link.href); + + return HTTP.requestVersioned(this.http, link.method, url, version).pipe( + tap(() => { + this.analytics.trackEvent('Schema', 'Deleted', appName); + }), + pretifyError('Failed to delete schema. Please reload.')); } +} + +function parseSchemaWithDetails(response: any, version: Version) { + const fields = response.fields.map((item: any) => { + const propertiesDto = + createProperties( + item.properties.fieldType, + item.properties); + + let nested: NestedFieldDto[] | null = null; + + if (item.nested && item.nested.length > 0) { + nested = item.nested.map((nestedItem: any) => { + const nestedPropertiesDto = + createProperties( + nestedItem.properties.fieldType, + nestedItem.properties); + + return new NestedFieldDto( + nestedItem.fieldId, + nestedItem.name, + nestedPropertiesDto, + item.fieldId, + nestedItem.isLocked, + nestedItem.isHidden, + nestedItem.isDisabled); + }); + } + + return new RootFieldDto( + item.fieldId, + item.name, + propertiesDto, + item.partitioning, + item.isLocked, + item.isHidden, + item.isDisabled, + nested || []); + }); + + const properties = new SchemaPropertiesDto(response.properties.label, response.properties.hints); + + return withLinks( + new SchemaDetailsDto( + response.id, + response.name, + response.category, + properties, + response.isSingleton, + response.isPublished, + DateTime.parseISO_UTC(response.created), response.createdBy, + DateTime.parseISO_UTC(response.lastModified), response.lastModifiedBy, + version, + fields, + response.scripts || {}, + response.previewUrls || {}), + response); } \ No newline at end of file diff --git a/src/Squidex/app/shared/state/schemas.state.spec.ts b/src/Squidex/app/shared/state/schemas.state.spec.ts index 5b084eab0..61cbcc3d1 100644 --- a/src/Squidex/app/shared/state/schemas.state.spec.ts +++ b/src/Squidex/app/shared/state/schemas.state.spec.ts @@ -5,56 +5,44 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ + import { of, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { SchemasState } from './schemas.state'; import { - createProperties, DialogService, - NestedFieldDto, - RootFieldDto, SchemaDetailsDto, - SchemaDto, - SchemaPropertiesDto, SchemasService, UpdateSchemaCategoryDto, versioned } from '@app/shared/internal'; +import { createSchema, createSchemaDetails } from '../services/schemas.service.spec'; + import { TestValues } from './_test-helpers'; describe('SchemasState', () => { const { app, appsState, - authService, - creation, - creator, - modified, - modifier, newVersion, version } = TestValues; - const oldSchemas = [ - new SchemaDto('id1', 'name1', 'category1', {}, false, false, creation, creator, creation, creator, version), - new SchemaDto('id2', 'name2', 'category2', {}, false, true , creation, creator, creation, creator, version) - ]; + const schema1 = createSchema(1); + const schema2 = createSchema(2); - const nested1 = new NestedFieldDto(3, '3', createProperties('Number'), 2); - const nested2 = new NestedFieldDto(4, '4', createProperties('String'), 2, true, true); + const oldSchemas = { + items: [ + schema1, + schema2 + ], + _links: {} + }; - const field1 = new RootFieldDto(1, '1', createProperties('String'), 'invariant'); - const field2 = new RootFieldDto(2, '2', createProperties('Array'), 'invariant', true, true, true, [nested1, nested2]); - - const schema = - new SchemaDetailsDto('id2', 'name2', 'category2', {}, false, true, - creation, creator, - creation, creator, - version, - [field1, field2]); + const schema = createSchemaDetails(1, version); let dialogs: IMock; let schemasService: IMock; @@ -64,7 +52,7 @@ describe('SchemasState', () => { dialogs = Mock.ofType(); schemasService = Mock.ofType(); - schemasState = new SchemasState(appsState.object, authService.object, dialogs.object, schemasService.object); + schemasState = new SchemasState(appsState.object, dialogs.object, schemasService.object); }); afterEach(() => { @@ -78,7 +66,7 @@ describe('SchemasState', () => { schemasState.load().subscribe(); - expect(schemasState.snapshot.schemas.values).toEqual(oldSchemas); + expect(schemasState.snapshot.schemas.values).toEqual(oldSchemas.items); expect(schemasState.snapshot.isLoaded).toBeTruthy(); expect(schemasState.snapshot.categories).toEqual({ 'category1': false, 'category2': false, '': true }); @@ -92,7 +80,7 @@ describe('SchemasState', () => { schemasState.addCategory('category3'); schemasState.load(true).subscribe(); - expect(schemasState.snapshot.schemas.values).toEqual(oldSchemas); + expect(schemasState.snapshot.schemas.values).toEqual(oldSchemas.items); expect(schemasState.snapshot.isLoaded).toBeTruthy(); expect(schemasState.snapshot.categories).toEqual({ 'category1': false, 'category2': false, 'category3': true, '': true }); @@ -132,28 +120,28 @@ describe('SchemasState', () => { }); it('should return schema on select and reload when already loaded', () => { - schemasService.setup(x => x.getSchema(app, schema.name)) + schemasService.setup(x => x.getSchema(app, schema1.name)) .returns(() => of(schema)).verifiable(Times.exactly(2)); - schemasState.select('name2').subscribe(); - schemasState.select('name2').subscribe(); + schemasState.select(schema1.name).subscribe(); + schemasState.select(schema1.name).subscribe(); expect().nothing(); }); it('should return schema on select and load when not loaded', () => { - schemasService.setup(x => x.getSchema(app, schema.name)) + schemasService.setup(x => x.getSchema(app, schema1.name)) .returns(() => of(schema)).verifiable(); let selectedSchema: SchemaDetailsDto; - schemasState.select('name2').subscribe(x => { + schemasState.select(schema1.name).subscribe(x => { selectedSchema = x!; }); expect(selectedSchema!).toBe(schema); expect(schemasState.snapshot.selectedSchema).toBe(schema); - expect(schemasState.snapshot.selectedSchema).toBe(schemasState.snapshot.schemas.at(1)); + expect(schemasState.snapshot.selectedSchema).toBe(schemasState.snapshot.schemas.at(0)); }); it('should return null on select when loading failed', () => { @@ -181,378 +169,316 @@ describe('SchemasState', () => { expect(schemasState.snapshot.selectedSchema).toBeNull(); }); - it('should mark published and update user info when published', () => { - schemasService.setup(x => x.publishSchema(app, oldSchemas[0].name, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + it('should update schema when schema published', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.publishSchema(app, schema1, version)) + .returns(() => of(updated)).verifiable(); - schemasState.publish(oldSchemas[0], modified).subscribe(); + schemasState.publish(schema1).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(0); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.isPublished).toBeTruthy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); }); - it('should unmark published and update user info when unpublished', () => { - schemasService.setup(x => x.unpublishSchema(app, oldSchemas[1].name, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + it('should update schema when schema unpublished', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - schemasState.unpublish(oldSchemas[1], modified).subscribe(); + schemasService.setup(x => x.unpublishSchema(app, schema1, version)) + .returns(() => of(updated)).verifiable(); - const schema_1 = schemasState.snapshot.schemas.at(1); + schemasState.unpublish(schema1).subscribe(); - expect(schema_1.isPublished).toBeFalsy(); - expectToBeModified(schema_1); + const schema1New = schemasState.snapshot.schemas.at(0); + + expect(schema1New).toEqual(updated); }); - it('should change category and update user info when category changed', () => { + it('should update schema when schema category changed', () => { const category = 'my-new-category'; - schemasService.setup(x => x.putCategory(app, oldSchemas[0].name, It.is(i => i.name === category), version)) - .returns(() => of(versioned(newVersion))).verifiable(); + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.putCategory(app, schema1, It.is(i => i.name === category), version)) + .returns(() => of(updated)).verifiable(); - schemasState.changeCategory(oldSchemas[0], category, modified).subscribe(); + schemasState.changeCategory(schema1, category).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(0); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.category).toEqual(category); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); }); describe('with selection', () => { beforeEach(() => { - schemasService.setup(x => x.getSchema(app, schema.name)) + schemasService.setup(x => x.getSchema(app, schema1.name)) .returns(() => of(schema)).verifiable(); - schemasState.select(schema.name).subscribe(); + schemasState.select(schema1.name).subscribe(); }); - it('should nmark published and update user info when published selected schema', () => { - schemasService.setup(x => x.publishSchema(app, schema.name, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + it('should update schema and selected schema when schema published', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.publishSchema(app, schema1, version)) + .returns(() => of(updated)).verifiable(); - schemasState.publish(schema, modified).subscribe(); + schemasState.publish(schema1).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.isPublished).toBeTruthy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should change category and update user info when category of selected schema changed', () => { + it('should update schema and selected schema when schema category changed', () => { const category = 'my-new-category'; - schemasService.setup(x => x.putCategory(app, oldSchemas[0].name, It.is(i => i.name === category), version)) - .returns(() => of(versioned(newVersion))).verifiable(); + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.putCategory(app, schema1, It.is(i => i.name === category), version)) + .returns(() => of(updated)).verifiable(); - schemasState.changeCategory(oldSchemas[0], category, modified).subscribe(); + schemasState.changeCategory(schema1, category).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(0); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.category).toEqual(category); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should update properties and update user info when updated', () => { + it('should update schema and selected schema when schema updated', () => { const request = { label: 'name2_label', hints: 'name2_hints' }; - schemasService.setup(x => x.putSchema(app, schema.name, It.isAny(), version)) - .returns(() => of(versioned(newVersion))).verifiable(); + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.putSchema(app, schema1, It.isAny(), version)) + .returns(() => of(updated)).verifiable(); - schemasState.update(schema, request, modified).subscribe(); + schemasState.update(schema1, request).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.properties.label).toEqual(request.label); - expect(schema_1.properties.hints).toEqual(request.hints); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should update script properties and update user info when scripts configured', () => { + it('should update schema and selected schema when scripts configured', () => { const request = { query: '' }; - schemasService.setup(x => x.putScripts(app, schema.name, It.isAny(), version)) - .returns(() => of(versioned(newVersion))).verifiable(); + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.putScripts(app, schema1, It.isAny(), version)) + .returns(() => of(updated)).verifiable(); - schemasState.configureScripts(schema, request, modified).subscribe(); + schemasState.configureScripts(schema1, request).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.scripts['query']).toEqual(''); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should update script properties and update user info when preview urls configured', () => { + it('should update schema and selected schema when preview urls configured', () => { const request = { web: 'url' }; - schemasService.setup(x => x.putPreviewUrls(app, schema.name, It.isAny(), version)) - .returns(() => of(versioned(newVersion))).verifiable(); + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.putPreviewUrls(app, schema1, It.isAny(), version)) + .returns(() => of(updated)).verifiable(); - schemasState.configurePreviewUrls(schema, request, modified).subscribe(); + schemasState.configurePreviewUrls(schema1, request).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.previewUrls).toEqual(request); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); it('should add schema to snapshot when created', () => { const request = { name: 'newName' }; - const result = new SchemaDetailsDto('id4', 'newName', '', new SchemaPropertiesDto(), false, false, modified, modifier, modified, modifier, version); + const updated = createSchemaDetails(3, newVersion, '-new'); schemasService.setup(x => x.postSchema(app, request)) - .returns(() => of(versioned(version, { id: 'id4' }))).verifiable(); + .returns(() => of(updated)).verifiable(); - schemasState.create(request, modified).subscribe(); + schemasState.create(request).subscribe(); expect(schemasState.snapshot.schemas.values.length).toBe(3); - expect(schemasState.snapshot.schemas.at(2)).toEqual(result); + expect(schemasState.snapshot.schemas.at(2)).toEqual(updated); }); it('should remove schema from snapshot when deleted', () => { - schemasService.setup(x => x.deleteSchema(app, schema.name, version)) + schemasService.setup(x => x.deleteSchema(app, schema1, version)) .returns(() => of(versioned(newVersion))).verifiable(); - schemasState.delete(schema).subscribe(); + schemasState.delete(schema1).subscribe(); expect(schemasState.snapshot.schemas.values.length).toBe(1); expect(schemasState.snapshot.selectedSchema).toBeNull(); }); - it('should add field and update user info when field added', () => { - const request = { ...field1 }; + it('should update schema and selected schema when field added', () => { + const request = { ...schema.fields[0] }; - const newField = new RootFieldDto(3, '3', createProperties('String'), 'invariant'); + const updated = createSchemaDetails(1, newVersion, '-new'); - schemasService.setup(x => x.postField(app, schema.name, It.isAny(), undefined, version)) - .returns(() => of(versioned(newVersion, newField))).verifiable(); + schemasService.setup(x => x.postField(app, schema1, It.isAny(), version)) + .returns(() => of(updated)).verifiable(); - schemasState.addField(schema, request, undefined, modified).subscribe(); + schemasState.addField(schema1, request).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.fields).toEqual([field1, field2, newField]); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should add field and update user info when nested field added', () => { - const request = { ...field1 }; + it('should update schema and selected schema when nested field added', () => { + const request = { ...schema.fields[0] }; - const newField = new NestedFieldDto(3, '3', createProperties('String'), 2); + const updated = createSchemaDetails(1, newVersion, '-new'); - schemasService.setup(x => x.postField(app, schema.name, It.isAny(), 2, version)) - .returns(() => of(versioned(newVersion, newField))).verifiable(); + schemasService.setup(x => x.postField(app, schema.fields[0], It.isAny(), version)) + .returns(() => of(updated)).verifiable(); - schemasState.addField(schema, request, field2, modified).subscribe(); + schemasState.addField(schema1, request, schema.fields[0]).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.fields[1].nested).toEqual([nested1, nested2, newField]); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should remove field and update user info when field removed', () => { - schemasService.setup(x => x.deleteField(app, schema.name, field1.fieldId, undefined, version)) - .returns(() => of(versioned(newVersion))).verifiable(); - - schemasState.deleteField(schema, field1, modified).subscribe(); - - const schema_1 = schemasState.snapshot.schemas.at(1); - - expect(schema_1.fields).toEqual([field2]); - expectToBeModified(schema_1); - }); + it('should update schema and selected schema when field removed', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - it('should remove field and update user info when nested field removed', () => { - schemasService.setup(x => x.deleteField(app, schema.name, nested1.fieldId, 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + schemasService.setup(x => x.deleteField(app, schema.fields[0], version)) + .returns(() => of(updated)).verifiable(); - schemasState.deleteField(schema, nested1, modified).subscribe(); + schemasState.deleteField(schema1, schema.fields[0]).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.fields[1].nested).toEqual([nested2]); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should sort fields and update user info when fields sorted', () => { - schemasService.setup(x => x.putFieldOrdering(app, schema.name, [field2.fieldId, field1.fieldId], undefined, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + it('should update schema and selected schema when fields sorted', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - schemasState.sortFields(schema, [field2, field1], undefined, modified).subscribe(); + schemasService.setup(x => x.putFieldOrdering(app, schema1, [schema.fields[1].fieldId, schema.fields[2].fieldId], version)) + .returns(() => of(updated)).verifiable(); - const schema_1 = schemasState.snapshot.schemas.at(1); + schemasState.sortFields(schema1, [schema.fields[1], schema.fields[2]]).subscribe(); - expect(schema_1.fields).toEqual([field2, field1]); - expectToBeModified(schema_1); - }); + const schema1New = schemasState.snapshot.schemas.at(0); - it('should sort fields and update user info when nested fields sorted', () => { - schemasService.setup(x => x.putFieldOrdering(app, schema.name, [nested2.fieldId, nested1.fieldId], 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); - - schemasState.sortFields(schema, [nested2, nested1], field2, modified).subscribe(); - - const schema_1 = schemasState.snapshot.schemas.at(1); - - expect(schema_1.fields[1].nested).toEqual([nested2, nested1]); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should update field properties and update user info when field updated', () => { - const request = { properties: createProperties('String') }; + it('should update schema and selected schema when nested fields sorted', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - schemasService.setup(x => x.putField(app, schema.name, field1.fieldId, request, undefined, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + schemasService.setup(x => x.putFieldOrdering(app, schema.fields[0], [schema.fields[1].fieldId, schema.fields[2].fieldId], version)) + .returns(() => of(updated)).verifiable(); - schemasState.updateField(schema, field1, request, modified).subscribe(); + schemasState.sortFields(schema1, [schema.fields[1], schema.fields[2]], schema.fields[0]).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.fields[0].properties).toBe(request.properties); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should update field properties and update user info when nested field updated', () => { - const request = { properties: createProperties('String') }; + it('should update schema and selected schema when field updated', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - schemasService.setup(x => x.putField(app, schema.name, nested1.fieldId, request, 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + const request = { ...schema.fields[0] }; - schemasState.updateField(schema, nested1, request, modified).subscribe(); + schemasService.setup(x => x.putField(app, schema.fields[0], request, version)) + .returns(() => of(updated)).verifiable(); - const schema_1 = schemasState.snapshot.schemas.at(1); + schemasState.updateField(schema1, schema.fields[0], request).subscribe(); - expect(schema_1.fields[1].nested[0].properties).toBe(request.properties); - expectToBeModified(schema_1); - }); + const schema1New = schemasState.snapshot.schemas.at(0); - it('should mark field hidden and update user info when field hidden', () => { - schemasService.setup(x => x.hideField(app, schema.name, field1.fieldId, undefined, version)) - .returns(() => of(versioned(newVersion))).verifiable(); - - schemasState.hideField(schema, field1, modified).subscribe(); - - const schema_1 = schemasState.snapshot.schemas.at(1); - - expect(schema_1.fields[0].isHidden).toBeTruthy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should mark field hidden and update user info when nested field hidden', () => { - schemasService.setup(x => x.hideField(app, schema.name, nested1.fieldId, 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); - - schemasState.hideField(schema, nested1, modified).subscribe(); + it('should update schema and selected schema when field hidden', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - const schema_1 = schemasState.snapshot.schemas.at(1); - - expect(schema_1.fields[1].nested[0].isHidden).toBeTruthy(); - expectToBeModified(schema_1); - }); + schemasService.setup(x => x.hideField(app, schema.fields[0], version)) + .returns(() => of(updated)).verifiable(); - it('should mark field disabled and update user info when field disabled', () => { - schemasService.setup(x => x.disableField(app, schema.name, field1.fieldId, undefined, version)) - .returns(() => of(versioned(newVersion))); + schemasState.hideField(schema1, schema.fields[0]).subscribe(); - schemasState.disableField(schema, field1, modified).subscribe(); + const schema1New = schemasState.snapshot.schemas.at(0); - const schema_1 = schemasState.snapshot.schemas.at(1); - - expect(schema_1.fields[0].isDisabled).toBeTruthy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should mark field disabled and update user info when nested disabled', () => { - schemasService.setup(x => x.disableField(app, schema.name, nested1.fieldId, 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); - - schemasState.disableField(schema, nested1, modified).subscribe(); - - const schema_1 = schemasState.snapshot.schemas.at(1); + it('should update schema and selected schema when field disabled', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - expect(schema_1.fields[1].nested[0].isDisabled).toBeTruthy(); - expectToBeModified(schema_1); - }); - - it('should mark field locked and update user info when field locked', () => { - schemasService.setup(x => x.lockField(app, schema.name, field1.fieldId, undefined, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + schemasService.setup(x => x.disableField(app, schema.fields[0], version)) + .returns(() => of(updated)); - schemasState.lockField(schema, field1, modified).subscribe(); + schemasState.disableField(schema1, schema.fields[0]).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.fields[0].isLocked).toBeTruthy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should mark field locked and update user info when nested field locked', () => { - schemasService.setup(x => x.lockField(app, schema.name, nested1.fieldId, 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); - - schemasState.lockField(schema, nested1, modified).subscribe(); - - const schema_1 = schemasState.snapshot.schemas.at(1); - - expect(schema_1.fields[1].nested[0].isLocked).toBeTruthy(); - expectToBeModified(schema_1); - }); + it('should update schema and selected schema when field locked', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - it('should unmark field hidden and update user info when field shown', () => { - schemasService.setup(x => x.showField(app, schema.name, field2.fieldId, undefined, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + schemasService.setup(x => x.lockField(app, schema.fields[0], version)) + .returns(() => of(updated)).verifiable(); - schemasState.showField(schema, field2, modified).subscribe(); + schemasState.lockField(schema1, schema.fields[0]).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.fields[1].isHidden).toBeFalsy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should unmark field hidden and update user info when nested field shown', () => { - schemasService.setup(x => x.showField(app, schema.name, nested2.fieldId, 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + it('should update schema and selected schema when field shown', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); - schemasState.showField(schema, nested2, modified).subscribe(); + schemasService.setup(x => x.showField(app, schema.fields[0], version)) + .returns(() => of(updated)).verifiable(); - const schema_1 = schemasState.snapshot.schemas.at(1); + schemasState.showField(schema1, schema.fields[0]).subscribe(); - expect(schema_1.fields[1].nested[1].isHidden).toBeFalsy(); - expectToBeModified(schema_1); - }); + const schema1New = schemasState.snapshot.schemas.at(0); - it('should unmark field disabled and update user info when field enabled', () => { - schemasService.setup(x => x.enableField(app, schema.name, field2.fieldId, undefined, version)) - .returns(() => of(versioned(newVersion))).verifiable(); - - schemasState.enableField(schema, field2, modified).subscribe(); - - const schema_1 = schemasState.snapshot.schemas.at(1); - - expect(schema_1.fields[1].isDisabled).toBeFalsy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); - it('should unmark field disabled and update user info when nested field enabled', () => { - schemasService.setup(x => x.enableField(app, schema.name, nested2.fieldId, 2, version)) - .returns(() => of(versioned(newVersion))).verifiable(); + it('should update schema and selected schema when field enabled', () => { + const updated = createSchemaDetails(1, newVersion, '-new'); + + schemasService.setup(x => x.enableField(app, schema.fields[0], version)) + .returns(() => of(updated)).verifiable(); - schemasState.enableField(schema, nested2, modified).subscribe(); + schemasState.enableField(schema1, schema.fields[0]).subscribe(); - const schema_1 = schemasState.snapshot.schemas.at(1); + const schema1New = schemasState.snapshot.schemas.at(0); - expect(schema_1.fields[1].nested[1].isDisabled).toBeFalsy(); - expectToBeModified(schema_1); + expect(schema1New).toEqual(updated); + expect(schemasState.snapshot.selectedSchema).toEqual(updated); }); }); - - function expectToBeModified(schema_1: SchemaDto) { - expect(schema_1.lastModified).toEqual(modified); - expect(schema_1.lastModifiedBy).toEqual(modifier); - expect(schema_1.version).toEqual(newVersion); - } }); }); \ No newline at end of file diff --git a/src/Squidex/app/shared/state/schemas.state.ts b/src/Squidex/app/shared/state/schemas.state.ts index bece9616f..49714d64b 100644 --- a/src/Squidex/app/shared/state/schemas.state.ts +++ b/src/Squidex/app/shared/state/schemas.state.ts @@ -10,19 +10,15 @@ import { Observable, of } from 'rxjs'; import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators'; import { - DateTime, DialogService, ImmutableArray, - mapVersioned, + ResourceLinks, shareMapSubscribed, shareSubscribed, State, - Types, - Version, - Versioned + Types } from '@app/framework'; -import { AuthService } from './../services/auth.service'; import { AppsState } from './apps.state'; import { @@ -31,17 +27,13 @@ import { FieldDto, NestedFieldDto, RootFieldDto, - SchemaCreatedDto, SchemaDetailsDto, SchemaDto, - SchemaPropertiesDto, SchemasService, UpdateFieldDto, UpdateSchemaDto } from './../services/schemas.service'; -import { FieldPropertiesDto } from './../services/schemas.types'; - type AnyFieldDto = NestedFieldDto | RootFieldDto; interface Snapshot { @@ -56,6 +48,9 @@ interface Snapshot { // The selected schema. selectedSchema?: SchemaDetailsDto | null; + + // The links. + links: ResourceLinks; } export type SchemasList = ImmutableArray; @@ -90,13 +85,16 @@ export class SchemasState extends State { this.changes.pipe(map(x => !!x.isLoaded), distinctUntilChanged()); + public links = + this.changes.pipe(map(x => x.links), + distinctUntilChanged()); + constructor( private readonly appsState: AppsState, - private readonly authState: AuthService, private readonly dialogs: DialogService, private readonly schemasService: SchemasService ) { - super({ schemas: ImmutableArray.empty(), categories: buildCategories({}) }); + super({ schemas: ImmutableArray.empty(), categories: buildCategories({}), links: {} }); } public select(idOrName: string | null): Observable { @@ -130,19 +128,18 @@ export class SchemasState extends State { } return this.next(s => { - const schemas = ImmutableArray.of(payload).sortByStringAsc(x => x.displayName); + const schemas = ImmutableArray.of(payload.items).sortByStringAsc(x => x.displayName); const categories = buildCategories(s.categories, schemas); - return { ...s, schemas, isLoaded: true, categories }; + return { ...s, schemas, isLoaded: true, categories, links: payload._links }; }); }), shareSubscribed(this.dialogs)); } - public create(request: CreateSchemaDto, now?: DateTime): Observable { + public create(request: CreateSchemaDto): Observable { return this.schemasService.postSchema(this.appName, request).pipe( - map(payload => createSchema(request, payload, this.user, now)), tap(created => { this.next(s => { const schemas = s.schemas.push(created).sortByStringAsc(x => x.displayName); @@ -154,7 +151,7 @@ export class SchemasState extends State { } public delete(schema: SchemaDto): Observable { - return this.schemasService.deleteSchema(this.appName, schema.name, schema.version).pipe( + return this.schemasService.deleteSchema(this.appName, schema, schema.version).pipe( tap(() => { this.next(s => { const schemas = s.schemas.filter(x => x.id !== schema.id); @@ -182,189 +179,137 @@ export class SchemasState extends State { }); } - public publish(schema: SchemaDto, now?: DateTime): Observable { - return this.schemasService.publishSchema(this.appName, schema.name, schema.version).pipe( - map(({ version }) => setPublished(schema, true, this.user, version, now)), + public publish(schema: SchemaDto): Observable { + return this.schemasService.publishSchema(this.appName, schema, schema.version).pipe( tap(updated => { this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - public unpublish(schema: SchemaDto, now?: DateTime): Observable { - return this.schemasService.unpublishSchema(this.appName, schema.name, schema.version).pipe( - map(({ version }) => setPublished(schema, false, this.user, version, now)), + public unpublish(schema: SchemaDto): Observable { + return this.schemasService.unpublishSchema(this.appName, schema, schema.version).pipe( tap(updated => { this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - public changeCategory(schema: SchemaDto, name: string, now?: DateTime): Observable { - return this.schemasService.putCategory(this.appName, schema.name, { name }, schema.version).pipe( - map(({ version }) => changeCategory(schema, name, this.user, version, now)), + public changeCategory(schema: SchemaDto, name: string): Observable { + return this.schemasService.putCategory(this.appName, schema, { name }, schema.version).pipe( tap(updated => { this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - public configurePreviewUrls(schema: SchemaDetailsDto, request: {}, now?: DateTime): Observable { - return this.schemasService.putPreviewUrls(this.appName, schema.name, request, schema.version).pipe( - map(({ version }) => configurePreviewUrls(schema, request, this.user, version, now)), + public configurePreviewUrls(schema: SchemaDto, request: {}): Observable { + return this.schemasService.putPreviewUrls(this.appName, schema, request, schema.version).pipe( tap(updated => { this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - public configureScripts(schema: SchemaDetailsDto, request: {}, now?: DateTime): Observable { - return this.schemasService.putScripts(this.appName, schema.name, request, schema.version).pipe( - map(({ version }) => configureScripts(schema, request, this.user, version, now)), + public configureScripts(schema: SchemaDto, request: {}): Observable { + return this.schemasService.putScripts(this.appName, schema, request, schema.version).pipe( tap(updated => { this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - public update(schema: SchemaDetailsDto, request: UpdateSchemaDto, now?: DateTime): Observable { - return this.schemasService.putSchema(this.appName, schema.name, request, schema.version).pipe( - map(({ version }) => updateProperties(schema, request, this.user, version, now)), + public update(schema: SchemaDto, request: UpdateSchemaDto): Observable { + return this.schemasService.putSchema(this.appName, schema, request, schema.version).pipe( tap(updated => { this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - public addField(schema: SchemaDetailsDto, request: AddFieldDto, parent?: RootFieldDto, now?: DateTime): Observable { - return this.schemasService.postField(this.appName, schema.name, request, pid(parent), schema.version).pipe( - map(({ version, payload }) => { - let updated: SchemaDto; - - if (Types.is(payload, NestedFieldDto)) { - updated = updateField(schema, addNested(parent!, payload), this.user, version, now); - } else { - updated = addField(schema, payload, this.user, version, now); - } - - return { updated, field: payload }; - }), - tap(({ updated }) => { + public addField(schema: SchemaDto, request: AddFieldDto, parent?: RootFieldDto): Observable { + return this.schemasService.postField(this.appName, parent || schema, request, schema.version).pipe( + tap(updated => { this.replaceSchema(updated); }), - shareMapSubscribed(this.dialogs, x => x.field, { silent: true })); + shareMapSubscribed(this.dialogs, x => getField(x, request, parent), { silent: true })); } - public sortFields(schema: SchemaDetailsDto, fields: any[], parent?: RootFieldDto, now?: DateTime): Observable { - return this.schemasService.putFieldOrdering(this.appName, schema.name, fields.map(t => t.fieldId), pid(parent), schema.version).pipe( - map(({ version }) => { - let updated: SchemaDetailsDto; - - if (!parent) { - updated = replaceFields(schema, fields, this.user, version, now); - } else { - updated = updateField(schema, replaceNested(parent, fields), this.user, version, now); - } - - return updated; - }), + public sortFields(schema: SchemaDto, fields: any[], parent?: RootFieldDto): Observable { + return this.schemasService.putFieldOrdering(this.appName, parent || schema, fields.map(t => t.fieldId), schema.version).pipe( tap(updated => { this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - public lockField(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable { - return this.schemasService.lockField(this.appName, schema.name, field.fieldId, pidof(field), schema.version).pipe( - mapVersioned(() => setLocked(field, true)), - tap(({ version, payload }) => { - this.replaceField(schema, payload, version, now); + public lockField(schema: SchemaDto, field: T): Observable { + return this.schemasService.lockField(this.appName, field, schema.version).pipe( + tap(updated => { + this.replaceSchema(updated); }), - shareMapSubscribed(this.dialogs, x => x.payload)); + shareSubscribed(this.dialogs)); } - public enableField(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable { - return this.schemasService.enableField(this.appName, schema.name, field.fieldId, pidof(field), schema.version).pipe( - mapVersioned(() => setDisabled(field, false)), - tap(({ version, payload }) => { - this.replaceField(schema, payload, version, now); + public enableField(schema: SchemaDto, field: T): Observable { + return this.schemasService.enableField(this.appName, field, schema.version).pipe( + tap(updated => { + this.replaceSchema(updated); }), - shareMapSubscribed(this.dialogs, x => x.payload)); + shareSubscribed(this.dialogs)); } - public disableField(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable { - return this.schemasService.disableField(this.appName, schema.name, field.fieldId, pidof(field), schema.version).pipe( - mapVersioned(() => setDisabled(field, true)), - tap(({ version, payload }) => { - this.replaceField(schema, payload, version, now); + public disableField(schema: SchemaDto, field: T): Observable { + return this.schemasService.disableField(this.appName, field, schema.version).pipe( + tap(updated => { + this.replaceSchema(updated); }), - shareMapSubscribed(this.dialogs, x => x.payload)); + shareSubscribed(this.dialogs)); } - public showField(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable { - return this.schemasService.showField(this.appName, schema.name, field.fieldId, pidof(field), schema.version).pipe( - mapVersioned(() => setHidden(field, false)), - tap(({ version, payload }) => { - this.replaceField(schema, payload, version, now); + public showField(schema: SchemaDto, field: T): Observable { + return this.schemasService.showField(this.appName, field, schema.version).pipe( + tap(updated => { + this.replaceSchema(updated); }), - shareMapSubscribed(this.dialogs, x => x.payload)); + shareSubscribed(this.dialogs)); } - public hideField(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable { - return this.schemasService.hideField(this.appName, schema.name, field.fieldId, pidof(field), schema.version).pipe( - mapVersioned(() => setHidden(field, true)), - tap(({ version, payload }) => { - this.replaceField(schema, payload, version, now); + public hideField(schema: SchemaDto, field: T): Observable { + return this.schemasService.hideField(this.appName, field, schema.version).pipe( + tap(updated => { + this.replaceSchema(updated); }), - shareMapSubscribed(this.dialogs, x => x.payload)); + shareSubscribed(this.dialogs)); } - public updateField(schema: SchemaDetailsDto, field: T, request: UpdateFieldDto, now?: DateTime): Observable { - return this.schemasService.putField(this.appName, schema.name, field.fieldId, request, pidof(field), schema.version).pipe( - mapVersioned(() => update(field, request.properties)), - tap(({ version, payload }) => { - this.replaceField(schema, payload, version, now); + public updateField(schema: SchemaDto, field: T, request: UpdateFieldDto): Observable { + return this.schemasService.putField(this.appName, field, request, schema.version).pipe( + tap(updated => { + this.replaceSchema(updated); }), - shareMapSubscribed(this.dialogs, x => x.payload)); + shareSubscribed(this.dialogs)); } - public deleteField(schema: SchemaDetailsDto, field: AnyFieldDto, now?: DateTime): Observable { - return this.schemasService.deleteField(this.appName, schema.name, field.fieldId, pidof(field), schema.version).pipe( - mapVersioned(() => field), - tap(({ version, payload }) => { - this.removeField(schema, payload, version, now); + public deleteField(schema: SchemaDto, field: AnyFieldDto): Observable { + return this.schemasService.deleteField(this.appName, field, schema.version).pipe( + tap(updated => { + this.replaceSchema(updated); }), shareSubscribed(this.dialogs)); } - private replaceField(schema: SchemaDetailsDto, field: T, version: Version, now?: DateTime) { - if (Types.is(field, RootFieldDto)) { - this.replaceSchema(updateField(schema, field, this.user, version, now)); - } else if (Types.is(field, NestedFieldDto)) { - const parent = schema.fields.find(x => x.fieldId === field.parentId); - - if (parent) { - this.replaceSchema(updateField(schema, updatedNested(parent, field), this.user, version, now)); - } - } - } - - private removeField(schema: SchemaDetailsDto, field: AnyFieldDto, version: Version, now?: DateTime) { - if (Types.is(field, RootFieldDto)) { - this.replaceSchema(removeField(schema, field, this.user, version, now)); - } else { - const parent = schema.fields.find(x => x.fieldId === field.parentId); - - if (parent) { - this.replaceSchema(updateField(schema, removeNested(parent, field), this.user, version, now)); - } - } - } - private replaceSchema(schema: SchemaDto) { return this.next(s => { const schemas = s.schemas.replaceBy('id', schema).sortByStringAsc(x => x.displayName); - const selectedSchema = Types.is(schema, SchemaDetailsDto) && s.selectedSchema && s.selectedSchema.id === schema.id ? schema : s.selectedSchema; + + const selectedSchema = + Types.is(schema, SchemaDetailsDto) && + schema && + s.selectedSchema && + s.selectedSchema.id === schema.id ? + schema : + s.selectedSchema; const categories = buildCategories(s.categories, schemas); @@ -375,9 +320,13 @@ export class SchemasState extends State { private get appName() { return this.appsState.appName; } +} - private get user() { - return this.authState.user!.token; +function getField(x: SchemaDetailsDto, request: AddFieldDto, parent?: RootFieldDto): FieldDto { + if (parent) { + return parent.nested.find(f => f.name === request.name)!; + } else { + return x.fields.find(f => f.name === request.name)!; } } @@ -417,123 +366,4 @@ function removeCategory(categories: { [name: string]: boolean }, category: strin delete categories[category]; return categories; -} - -const setPublished = (schema: T, isPublished: boolean, user: string, version: Version, now?: DateTime) => - schema.with({ - isPublished, - lastModified: now || DateTime.now(), - lastModifiedBy: user, - version - }); - -const changeCategory = (schema: T, category: string, user: string, version: Version, now?: DateTime) => - schema.with({ - category, - lastModified: now || DateTime.now(), - lastModifiedBy: user, - version - }); - -const configurePreviewUrls = (schema: SchemaDetailsDto, previewUrls: {}, user: string, version: Version, now?: DateTime) => - schema.with({ - previewUrls, - lastModified: now || DateTime.now(), - lastModifiedBy: user, - version - }); - -const configureScripts = (schema: SchemaDetailsDto, scripts: {}, user: string, version: Version, now?: DateTime) => - schema.with({ - scripts, - lastModified: now || DateTime.now(), - lastModifiedBy: user, - version - }); - -const updateProperties = (schema: SchemaDetailsDto, properties: SchemaPropertiesDto, user: string, version: Version, now?: DateTime) => - schema.with({ - properties, - lastModified: now || DateTime.now(), - lastModifiedBy: user, - version - }); - -const addField = (schema: SchemaDetailsDto, field: RootFieldDto, user: string, version: Version, now?: DateTime) => - schema.with({ - fields: [...schema.fields, field], - lastModified: now || DateTime.now(), - lastModifiedBy: user, - version - }); - -const updateField = (schema: SchemaDetailsDto, field: RootFieldDto, user: string, version: Version, now?: DateTime) => - schema.with({ - fields: schema.fields.map(f => f.fieldId === field.fieldId ? field : f), - lastModified: now || DateTime.now(), - lastModifiedBy: user, - version - }); - -const replaceFields = (schema: SchemaDetailsDto, fields: RootFieldDto[], user: string, version: Version, now?: DateTime) => - schema.with({ - fields, - version, - lastModified: now || DateTime.now(), - lastModifiedBy: user - }); - -const removeField = (schema: SchemaDetailsDto, field: FieldDto, user: string, version: Version, now?: DateTime) => - schema.with({ - fields: schema.fields.filter(f => f.fieldId !== field.fieldId), - version, - lastModified: now || DateTime.now(), - lastModifiedBy: user - }); - -const addNested = (parent: RootFieldDto, nested: NestedFieldDto) => - parent.with({ nested: [...parent.nested, nested] }); - -const updatedNested = (parent: RootFieldDto, nested: NestedFieldDto) => - parent.with({ nested: parent.nested.map(f => f.fieldId === nested.fieldId ? nested : f) }); - -const replaceNested = (parent: RootFieldDto, nested: NestedFieldDto[]) => - parent.with({ nested }); - -const removeNested = (parent: RootFieldDto, nested: NestedFieldDto) => - parent.with({ nested: parent.nested.filter(f => f.fieldId !== nested.fieldId) }); - -const setLocked = (field: T, isLocked: boolean) => - field.with({ isLocked }); - -const setHidden = (field: T, isHidden: boolean) => - field.with({ isHidden }); - -const setDisabled = (field: T, isDisabled: boolean) => - field.with({ isDisabled }); - -const update = (field: T, properties: FieldPropertiesDto) => - field.with({ properties }); - -const pidof = (field: T) => - Types.is(field, NestedFieldDto) ? field.parentId : undefined; - -const pid = (field?: RootFieldDto) => - field ? field.fieldId : undefined; - -function createSchema(request: CreateSchemaDto, { payload, version }: Versioned, user: string, now?: DateTime) { - now = now || DateTime.now(); - - const schema = new SchemaDetailsDto( - payload.id, - request.name, '', - request.properties || new SchemaPropertiesDto(), - request.isSingleton === true, - false, - now, user, - now, user, - version, - request.fields || []); - - return schema; } \ No newline at end of file