Browse Source

Schemas updated.

pull/363/head
Sebastian Stehle 7 years ago
parent
commit
dfe840f05a
  1. 10
      src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs
  2. 164
      src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs
  3. 82
      src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  4. 12
      src/Squidex/app/features/administration/state/users.state.spec.ts
  5. 8
      src/Squidex/app/shared/services/apps.service.spec.ts
  6. 4
      src/Squidex/app/shared/services/apps.service.ts
  7. 10
      src/Squidex/app/shared/services/assets.service.spec.ts
  8. 10
      src/Squidex/app/shared/services/assets.service.ts
  9. 951
      src/Squidex/app/shared/services/schemas.service.spec.ts
  10. 424
      src/Squidex/app/shared/services/schemas.service.ts
  11. 456
      src/Squidex/app/shared/state/schemas.state.spec.ts
  12. 330
      src/Squidex/app/shared/state/schemas.state.ts

10
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<RuleDto> InvokeCommand(string app, RuleCommand command)
private async Task<RuleDto> InvokeCommandAsync(string app, ICommand command)
{
var context = await CommandBus.PublishAsync(command);

164
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
/// </returns>
[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<IActionResult> PostField(string app, string name, [FromBody] AddFieldDto request)
{
var context = await CommandBus.PublishAsync(request.ToCommand());
var command = request.ToCommand();
var result = context.Result<EntityCreatedResult<long>>();
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
/// </returns>
[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<IActionResult> 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<EntityCreatedResult<long>>();
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
/// <param name="name">The name of the schema.</param>
/// <param name="request">The request that contains the field ids.</param>
/// <returns>
/// 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.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/ordering/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -116,20 +118,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="parentId">The parent field id.</param>
/// <param name="request">The request that contains the field ids.</param>
/// <returns>
/// 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.
/// </returns>
[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<IActionResult> 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);
}
/// <summary>
@ -140,20 +145,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="id">The id of the field to update.</param>
/// <param name="request">The field object that needs to be added to the schema.</param>
/// <returns>
/// 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.
/// </returns>
[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<IActionResult> 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);
}
/// <summary>
@ -165,20 +173,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="id">The id of the field to update.</param>
/// <param name="request">The field object that needs to be added to the schema.</param>
/// <returns>
/// 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.
/// </returns>
[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<IActionResult> 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);
}
/// <summary>
@ -188,7 +199,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="id">The id of the field to lock.</param>
/// <returns>
/// 204 => Schema field shown.
/// 200 => Schema field shown.
/// 400 => Schema field already locked.
/// 404 => Schema, field or app not found.
/// </returns>
@ -197,14 +208,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -215,7 +229,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="parentId">The parent field id.</param>
/// <param name="id">The id of the field to lock.</param>
/// <returns>
/// 204 => Schema field hidden.
/// 200 => Schema field hidden.
/// 400 => Schema field already hidden.
/// 404 => Field, schema, or app not found.
/// </returns>
@ -224,14 +238,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -241,7 +258,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="id">The id of the field to hide.</param>
/// <returns>
/// 204 => Schema field hidden.
/// 200 => Schema field hidden.
/// 400 => Schema field already hidden.
/// 404 => Schema, field or app not found.
/// </returns>
@ -250,14 +267,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -268,7 +288,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="parentId">The parent field id.</param>
/// <param name="id">The id of the field to hide.</param>
/// <returns>
/// 204 => Schema field hidden.
/// 200 => Schema field hidden.
/// 400 => Schema field already hidden.
/// 404 => Field, schema, or app not found.
/// </returns>
@ -277,14 +297,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -294,7 +317,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="id">The id of the field to show.</param>
/// <returns>
/// 204 => Schema field shown.
/// 200 => Schema field shown.
/// 400 => Schema field already visible.
/// 404 => Schema, field or app not found.
/// </returns>
@ -303,14 +326,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -321,7 +347,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="parentId">The parent field id.</param>
/// <param name="id">The id of the field to show.</param>
/// <returns>
/// 204 => Schema field shown.
/// 200 => Schema field shown.
/// 400 => Schema field already visible.
/// 404 => Schema, field or app not found.
/// </returns>
@ -335,9 +361,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -347,7 +375,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="id">The id of the field to enable.</param>
/// <returns>
/// 204 => Schema field enabled.
/// 200 => Schema field enabled.
/// 400 => Schema field already enabled.
/// 404 => Schema, field or app not found.
/// </returns>
@ -356,14 +384,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -374,7 +405,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="parentId">The parent field id.</param>
/// <param name="id">The id of the field to enable.</param>
/// <returns>
/// 204 => Schema field enabled.
/// 200 => Schema field enabled.
/// 400 => Schema field already enabled.
/// 404 => Schema, field or app not found.
/// </returns>
@ -383,14 +414,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -400,7 +434,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="id">The id of the field to disable.</param>
/// <returns>
/// 204 => Schema field disabled.
/// 200 => Schema field disabled.
/// 400 => Schema field already disabled.
/// 404 => Schema, field or app not found.
/// </returns>
@ -409,14 +443,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -427,7 +464,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="parentId">The parent field id.</param>
/// <param name="id">The id of the field to disable.</param>
/// <returns>
/// 204 => Schema field disabled.
/// 200 => Schema field disabled.
/// 400 => Schema field already disabled.
/// 404 => Schema, field or app not found.
/// </returns>
@ -436,14 +473,17 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[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<IActionResult> 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);
}
/// <summary>
@ -453,19 +493,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="id">The id of the field to disable.</param>
/// <returns>
/// 204 => Schema field deleted.
/// 200 => Schema field deleted.
/// 400 => Field is locked.
/// 404 => Schema, field or app not found.
/// </returns>
[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<IActionResult> 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);
}
/// <summary>
@ -476,12 +520,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="parentId">The parent field id.</param>
/// <param name="id">The id of the field to disable.</param>
/// <returns>
/// 204 => Schema field deleted.
/// 200 => Schema field deleted.
/// 400 => Field is locked.
/// 404 => Schema, field or app not found.
/// </returns>
[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<IActionResult> DeleteNestedField(string app, string name, long parentId, long id)
@ -490,5 +536,15 @@ namespace Squidex.Areas.Api.Controllers.Schemas
return NoContent();
}
private async Task<SchemaDetailsDto> InvokeCommandAsync(string app, ICommand command)
{
var context = await CommandBus.PublishAsync(command);
var response = context.Result<ISchemaEntity>();
var response = SchemaDetailsDto.FromSchemaWithDetails(response, this, app);
return response;
}
}
}

82
src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -109,7 +109,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns>
[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<IActionResult> PostSchema(string app, [FromBody] CreateSchemaDto request)
{
var command = request.ToCommand();
var context = await CommandBus.PublishAsync(command);
var result = context.Result<EntityCreatedResult<Guid>>();
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
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -154,19 +156,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="request">The schema object that needs to updated.</param>
/// <returns>
/// 204 => Schema updated.
/// 200 => Schema updated.
/// 400 => Schema properties are not valid.
/// 404 => Schema or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/sync")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -176,18 +182,22 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="request">The schema object that needs to updated.</param>
/// <returns>
/// 204 => Schema updated.
/// 200 => Schema updated.
/// 404 => Schema or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/category")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -197,18 +207,22 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="request">The preview urls for the schema.</param>
/// <returns>
/// 204 => Schema updated.
/// 200 => Schema updated.
/// 404 => Schema or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/preview-urls")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -218,19 +232,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="name">The name of the schema.</param>
/// <param name="request">The schema scripts object that needs to updated.</param>
/// <returns>
/// 204 => Schema updated.
/// 200 => Schema updated.
/// 400 => Schema properties are not valid.
/// 404 => Schema or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/scripts/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasScripts)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -239,20 +257,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema to publish.</param>
/// <returns>
/// 204 => Schema has been published.
/// 200 => Schema has been published.
/// 400 => Schema is already published.
/// 404 => Schema or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/publish/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasPublish)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -261,20 +282,23 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema to unpublish.</param>
/// <returns>
/// 204 => Schema has been unpublished.
/// 200 => Schema has been unpublished.
/// 400 => Schema is not published.
/// 404 => Schema or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/unpublish/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 200)]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiPermission(Permissions.AppSchemasPublish)]
[ApiCosts(1)]
public async Task<IActionResult> 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);
}
/// <summary>
@ -296,5 +320,15 @@ namespace Squidex.Areas.Api.Controllers.Schemas
return NoContent();
}
private async Task<SchemaDetailsDto> InvokeCommandAsync(string app, ICommand command)
{
var context = await CommandBus.PublishAsync(command);
var result = context.Result<ISchemaEntity>();
var response = SchemaDetailsDto.FromSchemaWithDetails(result, this, app);
return response;
}
}
}

12
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<DialogService>;
@ -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();
});

8
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',

4
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,

10
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',

10
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,

951
src/Squidex/app/shared/services/schemas.service.spec.ts

File diff suppressed because it is too large

424
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<SchemaDto> {
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<SchemaDto> {
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>): SchemaDetailsDto {
return this.clone(value);
}
}
export class FieldDto extends Model<FieldDto> {
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<FieldDto> {
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>): 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>): 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<SchemaDto[]> {
public getSchemas(appName: string): Observable<SchemasDto> {
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<SchemaDetailsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${id}`);
public getSchema(appName: string, name: string): Observable<SchemaDetailsDto> {
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<Versioned<SchemaCreatedDto>> {
public postSchema(appName: string, dto: CreateSchemaDto): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`);
public putScripts(appName: string, resource: Resource, dto: {}, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
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<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`);
public putSchema(appName: string, resource: Resource, dto: UpdateSchemaDto, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/publish`);
public putCategory(appName: string, resource: Resource, dto: UpdateSchemaCategoryDto, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/unpublish`);
public putPreviewUrls(appName: string, resource: Resource, dto: {}, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/category`);
public publishSchema(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/preview-urls`);
public unpublishSchema(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<RootFieldDto | NestedFieldDto>> {
const url = this.buildUrl(appName, schemaName, parentId, '');
public postField(appName: string, resource: Resource, dto: AddFieldDto, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, '/ordering');
public putFieldOrdering(appName: string, resource: Resource, dto: number[], version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}`);
public putField(appName: string, resource: Resource, dto: UpdateFieldDto, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/lock`);
public lockField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/enable`);
public enableField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/disable`);
public disableField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/show`);
public showField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/hide`);
public hideField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}`);
public deleteField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> {
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<Versioned<any>> {
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);
}

456
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<DialogService>;
let schemasService: IMock<SchemasService>;
@ -64,7 +52,7 @@ describe('SchemasState', () => {
dialogs = Mock.ofType<DialogService>();
schemasService = Mock.ofType<SchemasService>();
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(<SchemaDetailsDto>schemasState.snapshot.schemas.at(1));
expect(schemasState.snapshot.selectedSchema).toBe(<SchemaDetailsDto>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<UpdateSchemaCategoryDto>(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<UpdateSchemaCategoryDto>(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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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<UpdateSchemaCategoryDto>(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<UpdateSchemaCategoryDto>(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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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: '<query-script>' };
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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>schemasState.snapshot.schemas.at(0);
expect(schema_1.scripts['query']).toEqual('<query-script>');
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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(0);
const schema_1 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>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 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
const schema1New = <SchemaDetailsDto>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);
}
});
});

330
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<SchemaDto>;
@ -90,13 +85,16 @@ export class SchemasState extends State<Snapshot> {
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<SchemaDetailsDto | null> {
@ -130,19 +128,18 @@ export class SchemasState extends State<Snapshot> {
}
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<SchemaDetailsDto> {
public create(request: CreateSchemaDto): Observable<SchemaDetailsDto> {
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<Snapshot> {
}
public delete(schema: SchemaDto): Observable<any> {
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<Snapshot> {
});
}
public publish(schema: SchemaDto, now?: DateTime): Observable<SchemaDto> {
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<SchemaDto> {
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<SchemaDto> {
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<SchemaDto> {
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<SchemaDto> {
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<SchemaDto> {
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<SchemaDetailsDto> {
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<SchemaDetailsDto> {
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<SchemaDetailsDto> {
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<SchemaDetailsDto> {
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<SchemaDetailsDto> {
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<SchemaDetailsDto> {
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<FieldDto> {
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<FieldDto> {
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<SchemaDetailsDto> {
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<SchemaDetailsDto> {
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<T extends FieldDto>(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable<T> {
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<T extends FieldDto>(schema: SchemaDto, field: T): Observable<SchemaDetailsDto> {
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<T extends FieldDto>(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable<T> {
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<T extends FieldDto>(schema: SchemaDto, field: T): Observable<SchemaDetailsDto> {
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<T extends FieldDto>(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable<T> {
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<T extends FieldDto>(schema: SchemaDto, field: T): Observable<SchemaDetailsDto> {
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<T extends FieldDto>(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable<T> {
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<T extends FieldDto>(schema: SchemaDto, field: T): Observable<SchemaDetailsDto> {
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<T extends FieldDto>(schema: SchemaDetailsDto, field: T, now?: DateTime): Observable<T> {
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<T extends FieldDto>(schema: SchemaDto, field: T): Observable<SchemaDetailsDto> {
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<T extends FieldDto>(schema: SchemaDetailsDto, field: T, request: UpdateFieldDto, now?: DateTime): Observable<T> {
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<T extends FieldDto>(schema: SchemaDto, field: T, request: UpdateFieldDto): Observable<SchemaDetailsDto> {
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<any> {
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<SchemaDetailsDto> {
return this.schemasService.deleteField(this.appName, field, schema.version).pipe(
tap(updated => {
this.replaceSchema(updated);
}),
shareSubscribed(this.dialogs));
}
private replaceField<T extends FieldDto>(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<Snapshot> {
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 = <T extends SchemaDto>(schema: T, isPublished: boolean, user: string, version: Version, now?: DateTime) =>
<T>schema.with({
isPublished,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const changeCategory = <T extends SchemaDto>(schema: T, category: string, user: string, version: Version, now?: DateTime) =>
<T>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 = <T extends FieldDto>(field: T, isLocked: boolean) =>
<T>field.with({ isLocked });
const setHidden = <T extends FieldDto>(field: T, isHidden: boolean) =>
<T>field.with({ isHidden });
const setDisabled = <T extends FieldDto>(field: T, isDisabled: boolean) =>
<T>field.with({ isDisabled });
const update = <T extends FieldDto>(field: T, properties: FieldPropertiesDto) =>
<T>field.with({ properties });
const pidof = <T extends FieldDto>(field: T) =>
Types.is(field, NestedFieldDto) ? field.parentId : undefined;
const pid = (field?: RootFieldDto) =>
field ? field.fieldId : undefined;
function createSchema(request: CreateSchemaDto, { payload, version }: Versioned<SchemaCreatedDto>, 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;
}
Loading…
Cancel
Save