Browse Source

Refactoring/schema type (#696)

* Introduce schema-type.

* Make tests more stable.

* Metadata provider.

* Fix stackoverflow.
pull/697/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
816e77af3a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      backend/src/Migrations/OldEvents/SchemaCreated.cs
  2. 1
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs
  3. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs
  4. 1
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs
  5. 30
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldBase.cs
  6. 27
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaSurrogate.cs
  7. 154
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs
  8. 5
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs
  9. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs
  10. 15
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaType.cs
  11. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs
  12. 2
      backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs
  13. 3
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs
  14. 7
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/SingletonExtensions.cs
  15. 5
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/WorkflowExtensions.cs
  16. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/EnrichWithSchema.cs
  17. 3
      backend/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs
  18. 4
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigurePreviewUrls.cs
  19. 12
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs
  20. 8
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs
  21. 8
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SynchronizeSchema.cs
  22. 6
      backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs
  23. 4
      backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasSearchSource.cs
  24. 4
      backend/src/Squidex.Domain.Apps.Events/Schemas/SchemaPreviewUrlsConfigured.cs
  25. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  26. 6
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs
  27. 2
      backend/src/Squidex/Areas/Api/Controllers/BulkResultDto.cs
  28. 4
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  29. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs
  30. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/CreateContentDto.cs
  31. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs
  32. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/UpsertContentDto.cs
  33. 3
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs
  34. 20
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureUIFieldsDto.cs
  35. 20
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs
  36. 2
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs
  37. 102
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs
  38. 93
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  39. 17
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaDto.cs
  40. 44
      backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs
  41. 28
      backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  42. 28
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs
  43. 3
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs
  44. 4
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs
  45. 4
      backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs
  46. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultWorkflowsValidatorTests.cs
  47. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs
  48. 7
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/SingletonCommandMiddlewareTests.cs
  49. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/GuardSchemaTests.cs
  50. 10
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs
  51. 7
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs
  52. 14
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemasSearchSourceTests.cs
  53. 1
      backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableListTests.cs
  54. 4
      frontend/app/features/administration/state/event-consumers.state.spec.ts
  55. 6
      frontend/app/features/administration/state/users.state.spec.ts
  56. 4
      frontend/app/features/content/pages/content/content-page.component.ts
  57. 4
      frontend/app/features/content/pages/content/editor/content-editor.component.ts
  58. 4
      frontend/app/features/content/pages/contents/contents-page.component.ts
  59. 4
      frontend/app/features/content/shared/preview-button.component.ts
  60. 4
      frontend/app/features/content/shared/references/content-creator.component.ts
  61. 4
      frontend/app/features/content/shared/references/content-selector-item.component.ts
  62. 4
      frontend/app/features/content/shared/references/content-selector.component.ts
  63. 4
      frontend/app/features/schemas/pages/schema/common/schema-edit-form.component.ts
  64. 4
      frontend/app/features/schemas/pages/schema/export/schema-export-form.component.ts
  65. 4
      frontend/app/features/schemas/pages/schema/fields/field-wizard.component.ts
  66. 4
      frontend/app/features/schemas/pages/schema/fields/field.component.ts
  67. 4
      frontend/app/features/schemas/pages/schema/fields/schema-fields.component.ts
  68. 4
      frontend/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts
  69. 4
      frontend/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts
  70. 4
      frontend/app/features/schemas/pages/schema/schema-page.component.ts
  71. 4
      frontend/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts
  72. 4
      frontend/app/features/schemas/pages/schema/ui/field-list.component.ts
  73. 4
      frontend/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts
  74. 8
      frontend/app/features/schemas/pages/schemas/schema-form.component.html
  75. 2
      frontend/app/features/schemas/pages/schemas/schema-form.component.ts
  76. 6
      frontend/app/shared/guards/schema-must-exist-published.guard.spec.ts
  77. 4
      frontend/app/shared/guards/schema-must-exist.guard.spec.ts
  78. 8
      frontend/app/shared/guards/schema-must-not-be-singleton.guard.spec.ts
  79. 16
      frontend/app/shared/services/apps.service.spec.ts
  80. 18
      frontend/app/shared/services/apps.service.ts
  81. 4
      frontend/app/shared/services/assets.service.spec.ts
  82. 8
      frontend/app/shared/services/assets.service.ts
  83. 8
      frontend/app/shared/services/contents.service.spec.ts
  84. 20
      frontend/app/shared/services/contents.service.ts
  85. 176
      frontend/app/shared/services/schemas.service.spec.ts
  86. 169
      frontend/app/shared/services/schemas.service.ts
  87. 9
      frontend/app/shared/services/schemas.spec.ts
  88. 5
      frontend/app/shared/services/users-provider.service.spec.ts
  89. 14
      frontend/app/shared/state/_test-helpers.ts
  90. 5
      frontend/app/shared/state/apps.state.spec.ts
  91. 4
      frontend/app/shared/state/asset-uploader.state.spec.ts
  92. 4
      frontend/app/shared/state/assets.state.spec.ts
  93. 6
      frontend/app/shared/state/backups.state.spec.ts
  94. 2
      frontend/app/shared/state/clients.state.spec.ts
  95. 4
      frontend/app/shared/state/contents.forms.ts
  96. 109
      frontend/app/shared/state/contents.forms.visitors.spec.ts
  97. 2
      frontend/app/shared/state/contributors.state.spec.ts
  98. 2
      frontend/app/shared/state/languages.state.spec.ts
  99. 2
      frontend/app/shared/state/plans.state.spec.ts
  100. 4
      frontend/app/shared/state/query.ts

2
backend/src/Migrations/OldEvents/SchemaCreated.cs

@ -33,7 +33,7 @@ namespace Migrations.OldEvents
public IEvent Migrate() public IEvent Migrate()
{ {
var schema = new Schema(Name, Properties, Singleton); var schema = new Schema(Name, Properties, Singleton ? SchemaType.Singleton : SchemaType.Default);
if (Publish) if (Publish)
{ {

1
backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs

@ -8,7 +8,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
namespace Squidex.Domain.Apps.Core.Rules namespace Squidex.Domain.Apps.Core.Rules

2
backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs

@ -5,8 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Rules namespace Squidex.Domain.Apps.Core.Rules
{ {
public abstract record RuleTrigger public abstract record RuleTrigger

1
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs

@ -8,7 +8,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {

30
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldBase.cs

@ -5,16 +5,25 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {
public abstract class FieldBase public abstract class FieldBase
{ {
private Dictionary<string, object> metadata;
public long Id { get; } public long Id { get; }
public string Name { get; } public string Name { get; }
public IDictionary<string, object> Metadata
{
get => metadata ??= new Dictionary<string, object>();
}
protected FieldBase(long id, string name) protected FieldBase(long id, string name)
{ {
Guard.NotNullOrEmpty(name, nameof(name)); Guard.NotNullOrEmpty(name, nameof(name));
@ -24,5 +33,26 @@ namespace Squidex.Domain.Apps.Core.Schemas
Name = name; Name = name;
} }
public T? GetMetadata<T>(string key, T? defaultValue = default)
{
var local = metadata;
return local != null && local.TryGetValue(key, out var item) ? (T)item : defaultValue;
}
public T GetMetadata<T>(string key, Func<T> defaultValueFactory)
{
var local = metadata;
return local != null && local.TryGetValue(key, out var item) ? (T)item : defaultValueFactory();
}
public bool HasMetadata(string key)
{
var local = metadata;
return local?.ContainsKey(key) == true;
}
} }
} }

27
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaSurrogate.cs

@ -1,4 +1,4 @@
// ========================================================================== // ==========================================================================
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt) // Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -6,9 +6,9 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Schemas.Json namespace Squidex.Domain.Apps.Core.Schemas.Json
@ -19,10 +19,10 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
public string Category { get; set; } public string Category { get; set; }
public bool IsSingleton { get; set; }
public bool IsPublished { get; set; } public bool IsPublished { get; set; }
public SchemaType Type { get; set; }
public SchemaProperties Properties { get; set; } public SchemaProperties Properties { get; set; }
public SchemaScripts? Scripts { get; set; } public SchemaScripts? Scripts { get; set; }
@ -35,7 +35,18 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
public FieldSurrogate[] Fields { get; set; } public FieldSurrogate[] Fields { get; set; }
public Dictionary<string, string>? PreviewUrls { get; set; } public ImmutableDictionary<string, string>? PreviewUrls { get; set; }
public bool IsSingleton
{
set
{
if (value)
{
Type = SchemaType.Singleton;
}
}
}
public void FromSource(Schema source) public void FromSource(Schema source)
{ {
@ -54,8 +65,6 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
Partitioning = x.Partitioning.Key, Partitioning = x.Partitioning.Key,
Properties = x.RawProperties Properties = x.RawProperties
}).ToArray(); }).ToArray();
PreviewUrls = source.PreviewUrls.ToDictionary(x => x.Key, x => x.Value);
} }
private static FieldSurrogate[]? CreateChildren(IField field) private static FieldSurrogate[]? CreateChildren(IField field)
@ -81,7 +90,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
{ {
var fields = Fields?.Select(f => f.ToField()).ToArray() ?? Array.Empty<RootField>(); var fields = Fields?.Select(f => f.ToField()).ToArray() ?? Array.Empty<RootField>();
var schema = new Schema(Name, fields, Properties, IsPublished, IsSingleton); var schema = new Schema(Name, fields, Properties, IsPublished, Type);
if (!string.IsNullOrWhiteSpace(Category)) if (!string.IsNullOrWhiteSpace(Category))
{ {
@ -116,4 +125,4 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
return schema; return schema;
} }
} }
} }

154
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs

@ -10,113 +10,71 @@ using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Linq; using System.Linq;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {
public sealed class Schema public sealed class Schema
{ {
private static readonly Dictionary<string, string> EmptyPreviewUrls = new Dictionary<string, string>(); public SchemaType Type { get; }
private readonly string name;
private readonly bool isSingleton;
private string category;
private FieldNames fieldsInLists = FieldNames.Empty;
private FieldNames fieldsInReferences = FieldNames.Empty;
private FieldRules fieldRules = FieldRules.Empty;
private FieldCollection<RootField> fields = FieldCollection<RootField>.Empty;
private IReadOnlyDictionary<string, string> previewUrls = EmptyPreviewUrls;
private SchemaScripts scripts = SchemaScripts.Empty;
private SchemaProperties properties;
private bool isPublished;
public string Name
{
get => name;
}
public string Category public string Name { get; }
{
get => category;
}
public bool IsPublished public string Category { get; private set; }
{
get => isPublished;
}
public bool IsSingleton public bool IsPublished { get; private set; }
{
get => isSingleton;
}
public IReadOnlyList<RootField> Fields public FieldCollection<RootField> FieldCollection { get; private set; } = FieldCollection<RootField>.Empty;
{
get => fields.Ordered;
}
public IReadOnlyDictionary<long, RootField> FieldsById public FieldRules FieldRules { get; private set; } = FieldRules.Empty;
{
get => fields.ById;
}
public IReadOnlyDictionary<string, RootField> FieldsByName public FieldNames FieldsInLists { get; private set; } = FieldNames.Empty;
{
get => fields.ByName;
}
public IReadOnlyDictionary<string, string> PreviewUrls public FieldNames FieldsInReferences { get; private set; } = FieldNames.Empty;
{
get => previewUrls;
}
public FieldCollection<RootField> FieldCollection public SchemaScripts Scripts { get; private set; } = SchemaScripts.Empty;
{
get => fields;
}
public FieldRules FieldRules public SchemaProperties Properties { get; private set; } = new SchemaProperties();
{
get => fieldRules;
}
public FieldNames FieldsInLists public ImmutableDictionary<string, string> PreviewUrls { get; private set; } = ImmutableDictionary.Empty<string, string>();
{
get => fieldsInLists;
}
public FieldNames FieldsInReferences public IReadOnlyList<RootField> Fields
{ {
get => fieldsInReferences; get => FieldCollection.Ordered;
} }
public SchemaScripts Scripts public IReadOnlyDictionary<long, RootField> FieldsById
{ {
get => scripts; get => FieldCollection.ById;
} }
public SchemaProperties Properties public IReadOnlyDictionary<string, RootField> FieldsByName
{ {
get => properties; get => FieldCollection.ByName;
} }
public Schema(string name, SchemaProperties? properties = null, bool isSingleton = false) public Schema(string name, SchemaProperties? properties = null, SchemaType type = SchemaType.Default)
{ {
Guard.NotNullOrEmpty(name, nameof(name)); Guard.NotNullOrEmpty(name, nameof(name));
this.name = name; Name = name;
this.properties = properties ?? new SchemaProperties(); if (properties != null)
{
Properties = properties;
}
this.isSingleton = isSingleton; Type = type;
} }
public Schema(string name, RootField[] fields, SchemaProperties properties, bool isPublished, bool isSingleton = false) public Schema(string name, RootField[] fields, SchemaProperties? properties, bool isPublished = false, SchemaType type = SchemaType.Default)
: this(name, properties, isSingleton) : this(name, properties, type)
{ {
Guard.NotNull(fields, nameof(fields)); Guard.NotNull(fields, nameof(fields));
this.fields = new FieldCollection<RootField>(fields); FieldCollection = new FieldCollection<RootField>(fields);
this.isPublished = isPublished; IsPublished = isPublished;
} }
[Pure] [Pure]
@ -124,14 +82,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
newProperties ??= new SchemaProperties(); newProperties ??= new SchemaProperties();
if (properties.Equals(newProperties)) if (Properties.Equals(newProperties))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.properties = newProperties; clone.Properties = newProperties;
}); });
} }
@ -140,14 +98,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
newScripts ??= new SchemaScripts(); newScripts ??= new SchemaScripts();
if (scripts.Equals(newScripts)) if (Scripts.Equals(newScripts))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.scripts = newScripts; clone.Scripts = newScripts;
}); });
} }
@ -156,14 +114,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
names ??= FieldNames.Empty; names ??= FieldNames.Empty;
if (fieldsInLists.SequenceEqual(names)) if (FieldsInLists.SequenceEqual(names))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.fieldsInLists = names; clone.FieldsInLists = names;
}); });
} }
@ -178,14 +136,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
names ??= FieldNames.Empty; names ??= FieldNames.Empty;
if (fieldsInReferences.SequenceEqual(names)) if (FieldsInReferences.SequenceEqual(names))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.fieldsInReferences = names; clone.FieldsInReferences = names;
}); });
} }
@ -200,14 +158,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
rules ??= FieldRules.Empty; rules ??= FieldRules.Empty;
if (fieldRules.Equals(rules)) if (FieldRules.Equals(rules))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.fieldRules = rules; clone.FieldRules = rules;
}); });
} }
@ -220,58 +178,58 @@ namespace Squidex.Domain.Apps.Core.Schemas
[Pure] [Pure]
public Schema Publish() public Schema Publish()
{ {
if (isPublished) if (IsPublished)
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.isPublished = true; clone.IsPublished = true;
}); });
} }
[Pure] [Pure]
public Schema Unpublish() public Schema Unpublish()
{ {
if (!isPublished) if (!IsPublished)
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.isPublished = false; clone.IsPublished = false;
}); });
} }
[Pure] [Pure]
public Schema ChangeCategory(string newCategory) public Schema ChangeCategory(string category)
{ {
if (string.Equals(category, newCategory)) if (string.Equals(Category, category))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.category = newCategory; clone.Category = category;
}); });
} }
[Pure] [Pure]
public Schema SetPreviewUrls(IReadOnlyDictionary<string, string> newPreviewUrls) public Schema SetPreviewUrls(ImmutableDictionary<string, string> previewUrls)
{ {
previewUrls ??= EmptyPreviewUrls; previewUrls ??= ImmutableDictionary.Empty<string, string>();
if (previewUrls.EqualsDictionary(newPreviewUrls)) if (PreviewUrls.Equals(previewUrls))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.previewUrls = newPreviewUrls; clone.PreviewUrls = previewUrls;
}); });
} }
@ -285,9 +243,9 @@ namespace Squidex.Domain.Apps.Core.Schemas
return Clone(clone => return Clone(clone =>
{ {
clone.fields = fields.Remove(fieldId); clone.FieldCollection = FieldCollection.Remove(fieldId);
clone.fieldsInLists = fieldsInLists.Remove(field.Name); clone.FieldsInLists = FieldsInLists.Remove(field.Name);
clone.fieldsInReferences = fieldsInReferences.Remove(field.Name); clone.FieldsInReferences = FieldsInReferences.Remove(field.Name);
}); });
} }
@ -311,16 +269,16 @@ namespace Squidex.Domain.Apps.Core.Schemas
private Schema UpdateFields(Func<FieldCollection<RootField>, FieldCollection<RootField>> updater) private Schema UpdateFields(Func<FieldCollection<RootField>, FieldCollection<RootField>> updater)
{ {
var newFields = updater(fields); var newFields = updater(FieldCollection);
if (ReferenceEquals(newFields, fields)) if (ReferenceEquals(newFields, FieldCollection))
{ {
return this; return this;
} }
return Clone(clone => return Clone(clone =>
{ {
clone.fields = newFields; clone.FieldCollection = newFields;
}); });
} }

5
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs

@ -45,6 +45,11 @@ namespace Squidex.Domain.Apps.Core.Schemas
return field.RawProperties.Label.Or(field.TypeName()); return field.RawProperties.Label.Or(field.TypeName());
} }
public static bool IsSingleton(this Schema schema)
{
return schema.Type == SchemaType.Singleton;
}
public static string TypeName(this Schema schema) public static string TypeName(this Schema schema)
{ {
return schema.Name.ToPascalCase(); return schema.Name.ToPascalCase();

2
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs

@ -11,6 +11,8 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
public sealed record SchemaProperties : NamedElementPropertiesBase public sealed record SchemaProperties : NamedElementPropertiesBase
{ {
public static readonly SchemaProperties Empty = new SchemaProperties();
public ImmutableList<string>? Tags { get; init; } public ImmutableList<string>? Tags { get; init; }
public string? ContentsSidebarUrl { get; init; } public string? ContentsSidebarUrl { get; init; }

15
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaType.cs

@ -0,0 +1,15 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Core.Schemas
{
public enum SchemaType
{
Default,
Singleton
}
}

2
backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs

@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Core.EventSynchronization
if (!source.PreviewUrls.EqualsDictionary(target.PreviewUrls)) if (!source.PreviewUrls.EqualsDictionary(target.PreviewUrls))
{ {
yield return new SchemaPreviewUrlsConfigured { PreviewUrls = target.PreviewUrls.ToDictionary() }; yield return new SchemaPreviewUrlsConfigured { PreviewUrls = target.PreviewUrls };
} }
if (source.IsPublished != target.IsPublished) if (source.IsPublished != target.IsPublished)

2
backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs

@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
public SchemaBuilder Singleton() public SchemaBuilder Singleton()
{ {
command.IsSingleton = true; command.Type = SchemaType.Singleton;
return this; return this;
} }

3
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs

@ -10,6 +10,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards; using Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
@ -95,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
await CreateCore(c, operation); await CreateCore(c, operation);
if (operation.Schema.SchemaDef.IsSingleton) if (operation.Schema.SchemaDef.IsSingleton())
{ {
ChangeStatus(c.AsChange(Status.Published)); ChangeStatus(c.AsChange(Status.Published));
} }

7
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/SingletonExtensions.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Translations;
@ -15,7 +16,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
{ {
public static void MustNotCreateSingleton(this OperationContext context) public static void MustNotCreateSingleton(this OperationContext context)
{ {
if (context.SchemaDef.IsSingleton && context.ContentId != context.Schema.Id) if (context.SchemaDef.IsSingleton() && context.ContentId != context.Schema.Id)
{ {
throw new DomainException(T.Get("contents.singletonNotCreatable")); throw new DomainException(T.Get("contents.singletonNotCreatable"));
} }
@ -23,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
public static void MustNotChangeSingleton(this OperationContext context, Status status) public static void MustNotChangeSingleton(this OperationContext context, Status status)
{ {
if (context.SchemaDef.IsSingleton && (context.Content.NewStatus == null || status != Status.Published)) if (context.SchemaDef.IsSingleton() && (context.Content.NewStatus == null || status != Status.Published))
{ {
throw new DomainException(T.Get("contents.singletonNotChangeable")); throw new DomainException(T.Get("contents.singletonNotChangeable"));
} }
@ -31,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
public static void MustNotDeleteSingleton(this OperationContext context) public static void MustNotDeleteSingleton(this OperationContext context)
{ {
if (context.SchemaDef.IsSingleton) if (context.SchemaDef.IsSingleton())
{ {
throw new DomainException(T.Get("contents.singletonNotDeletable")); throw new DomainException(T.Get("contents.singletonNotDeletable"));
} }

5
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/WorkflowExtensions.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Translations;
@ -23,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
public static async Task CheckTransitionAsync(this OperationContext context, Status status) public static async Task CheckTransitionAsync(this OperationContext context, Status status)
{ {
if (!context.SchemaDef.IsSingleton) if (!context.SchemaDef.IsSingleton())
{ {
var workflow = GetWorkflow(context); var workflow = GetWorkflow(context);
@ -41,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
public static async Task CheckStatusAsync(this OperationContext context, Status status) public static async Task CheckStatusAsync(this OperationContext context, Status status)
{ {
if (!context.SchemaDef.IsSingleton) if (!context.SchemaDef.IsSingleton())
{ {
var workflow = GetWorkflow(context); var workflow = GetWorkflow(context);

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/EnrichWithSchema.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
foreach (var content in group) foreach (var content in group)
{ {
content.IsSingleton = schema.SchemaDef.IsSingleton; content.IsSingleton = schema.SchemaDef.IsSingleton();
content.SchemaName = schemaName; content.SchemaName = schemaName;
content.SchemaDisplayName = schemaDisplayName; content.SchemaDisplayName = schemaDisplayName;

3
backend/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -21,7 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
await next(context); await next(context);
if (context.IsCompleted && context.Command is CreateSchema { IsSingleton: true } createSchema) if (context.IsCompleted && context.Command is CreateSchema { Type: SchemaType.Singleton } createSchema)
{ {
var schemaId = NamedId.Of(createSchema.SchemaId, createSchema.Name); var schemaId = NamedId.Of(createSchema.SchemaId, createSchema.Name);

4
backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigurePreviewUrls.cs

@ -5,12 +5,12 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Entities.Schemas.Commands namespace Squidex.Domain.Apps.Entities.Schemas.Commands
{ {
public sealed class ConfigurePreviewUrls : SchemaUpdateCommand public sealed class ConfigurePreviewUrls : SchemaUpdateCommand
{ {
public Dictionary<string, string> PreviewUrls { get; set; } public ImmutableDictionary<string, string> PreviewUrls { get; set; }
} }
} }

12
backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs

@ -1,14 +1,14 @@
// ========================================================================== // ==========================================================================
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt) // Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using SchemaField = Squidex.Domain.Apps.Entities.Schemas.Commands.UpsertSchemaField; using SchemaField = Squidex.Domain.Apps.Entities.Schemas.Commands.UpsertSchemaField;
@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
public bool IsPublished { get; set; } public bool IsPublished { get; set; }
public bool IsSingleton { get; set; } public SchemaType Type { get; set; }
public SchemaField[]? Fields { get; set; } public SchemaField[]? Fields { get; set; }
@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
public SchemaProperties Properties { get; set; } public SchemaProperties Properties { get; set; }
public Dictionary<string, string>? PreviewUrls { get; set; } public ImmutableDictionary<string, string>? PreviewUrls { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public override DomainId AggregateId public override DomainId AggregateId
@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
{ {
IUpsertCommand self = this; IUpsertCommand self = this;
return self.ToSchema(Name, IsSingleton); return self.ToSchema(Name, Type);
} }
} }
} }

8
backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs

@ -5,10 +5,10 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using SchemaField = Squidex.Domain.Apps.Entities.Schemas.Commands.UpsertSchemaField; using SchemaField = Squidex.Domain.Apps.Entities.Schemas.Commands.UpsertSchemaField;
namespace Squidex.Domain.Apps.Entities.Schemas.Commands namespace Squidex.Domain.Apps.Entities.Schemas.Commands
@ -31,11 +31,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
FieldRuleCommand[]? FieldRules { get; set; } FieldRuleCommand[]? FieldRules { get; set; }
Dictionary<string, string>? PreviewUrls { get; set; } ImmutableDictionary<string, string>? PreviewUrls { get; set; }
Schema ToSchema(string name, bool isSingleton) Schema ToSchema(string name, SchemaType type)
{ {
var schema = new Schema(name, Properties, isSingleton); var schema = new Schema(name, Properties, type);
if (IsPublished) if (IsPublished)
{ {

8
backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SynchronizeSchema.cs

@ -5,8 +5,8 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using SchemaField = Squidex.Domain.Apps.Entities.Schemas.Commands.UpsertSchemaField; using SchemaField = Squidex.Domain.Apps.Entities.Schemas.Commands.UpsertSchemaField;
@ -34,13 +34,13 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
public SchemaProperties Properties { get; set; } public SchemaProperties Properties { get; set; }
public Dictionary<string, string>? PreviewUrls { get; set; } public ImmutableDictionary<string, string>? PreviewUrls { get; set; }
public Schema BuildSchema(string name, bool isSingleton) public Schema BuildSchema(string name, SchemaType type)
{ {
IUpsertCommand self = this; IUpsertCommand self = this;
return self.ToSchema(name, isSingleton); return self.ToSchema(name, type);
} }
} }
} }

6
backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs

@ -1,4 +1,4 @@
// ========================================================================== // ==========================================================================
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt) // Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -251,7 +251,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
}; };
var schemaSource = Snapshot.SchemaDef; var schemaSource = Snapshot.SchemaDef;
var schemaTarget = command.BuildSchema(schemaSource.Name, schemaSource.IsSingleton); var schemaTarget = command.BuildSchema(schemaSource.Name, schemaSource.Type);
var events = schemaSource.Synchronize(schemaTarget, () => Snapshot.SchemaFieldsTotal + 1, options); var events = schemaSource.Synchronize(schemaTarget, () => Snapshot.SchemaFieldsTotal + 1, options);
@ -411,4 +411,4 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
return J.AsTask<ISchemaEntity>(Snapshot); return J.AsTask<ISchemaEntity>(Snapshot);
} }
} }
} }

4
backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasSearchSource.cs

@ -1,4 +1,4 @@
// ========================================================================== // ==========================================================================
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt) // Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
private void AddContentsUrl(SearchResults result, NamedId<DomainId> appId, ISchemaEntity schema, NamedId<DomainId> schemaId, string name) private void AddContentsUrl(SearchResults result, NamedId<DomainId> appId, ISchemaEntity schema, NamedId<DomainId> schemaId, string name)
{ {
if (schema.SchemaDef.IsSingleton) if (schema.SchemaDef.IsSingleton())
{ {
var contentUrl = urlGenerator.ContentUI(appId, schemaId, schemaId.Id); var contentUrl = urlGenerator.ContentUI(appId, schemaId, schemaId.Id);

4
backend/src/Squidex.Domain.Apps.Events/Schemas/SchemaPreviewUrlsConfigured.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Schemas namespace Squidex.Domain.Apps.Events.Schemas
@ -13,6 +13,6 @@ namespace Squidex.Domain.Apps.Events.Schemas
[EventType(nameof(SchemaPreviewUrlsConfigured))] [EventType(nameof(SchemaPreviewUrlsConfigured))]
public sealed class SchemaPreviewUrlsConfigured : SchemaEvent public sealed class SchemaPreviewUrlsConfigured : SchemaEvent
{ {
public Dictionary<string, string> PreviewUrls { get; set; } public ImmutableDictionary<string, string> PreviewUrls { get; set; }
} }
} }

2
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -76,7 +76,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary> /// <summary>
/// Indicates if the user can access the api. /// Indicates if the user can access the api.
/// </summary> /// </summary>
[Obsolete("Usage role properties")] [Obsolete("Use 'roleProperties' field now.")]
public bool CanAccessApi { get; set; } public bool CanAccessApi { get; set; }
/// <summary> /// <summary>

6
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs

@ -133,7 +133,7 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
/// <summary> /// <summary>
/// Determines of the created file is an image. /// Determines of the created file is an image.
/// </summary> /// </summary>
[Obsolete("Use Type instead")] [Obsolete("Use 'type' field now.")]
public bool IsImage public bool IsImage
{ {
get => Type == AssetType.Image; get => Type == AssetType.Image;
@ -142,7 +142,7 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
/// <summary> /// <summary>
/// The width of the image in pixels if the asset is an image. /// The width of the image in pixels if the asset is an image.
/// </summary> /// </summary>
[Obsolete("Use Metdata instead")] [Obsolete("Use 'metdata' field now.")]
public int? PixelWidth public int? PixelWidth
{ {
get => Metadata.GetPixelWidth(); get => Metadata.GetPixelWidth();
@ -151,7 +151,7 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
/// <summary> /// <summary>
/// The height of the image in pixels if the asset is an image. /// The height of the image in pixels if the asset is an image.
/// </summary> /// </summary>
[Obsolete("Use Metdata instead")] [Obsolete("Use 'metdata' field now.")]
public int? PixelHeight public int? PixelHeight
{ {
get => Metadata.GetPixelHeight(); get => Metadata.GetPixelHeight();

2
backend/src/Squidex/Areas/Api/Controllers/BulkResultDto.cs

@ -34,7 +34,7 @@ namespace Squidex.Areas.Api.Controllers
/// <summary> /// <summary>
/// The id of the entity that has been handled successfully or not. /// The id of the entity that has been handled successfully or not.
/// </summary> /// </summary>
[Obsolete("Use Id instead.")] [Obsolete("Use 'id' field now.")]
public DomainId? ContentId => Id; public DomainId? ContentId => Id;
public static BulkResultDto FromBulkResult(BulkUpdateResultItem result, HttpContext httpContext) public static BulkResultDto FromBulkResult(BulkUpdateResultItem result, HttpContext httpContext)

4
backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -1,4 +1,4 @@
// ========================================================================== // ==========================================================================
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt) // Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -386,7 +386,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(BulkResultDto[]), StatusCodes.Status200OK)] [ProducesResponseType(typeof(BulkResultDto[]), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsCreate)] [ApiPermissionOrAnonymous(Permissions.AppContentsCreate)]
[ApiCosts(5)] [ApiCosts(5)]
[Obsolete("Use bulk endpoint")] [Obsolete("Use bulk endpoint now.")]
public async Task<IActionResult> PostContents(string app, string name, [FromBody] ImportContentsDto request) public async Task<IActionResult> PostContents(string app, string name, [FromBody] ImportContentsDto request)
{ {
var command = request.ToCommand(); var command = request.ToCommand();

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs

@ -27,7 +27,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// <summary> /// <summary>
/// True to automatically publish the content. /// True to automatically publish the content.
/// </summary> /// </summary>
[Obsolete("Use Jobs.Status")] [Obsolete("Use 'jobs.status' fields now.")]
public bool Publish { get; set; } public bool Publish { get; set; }
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/CreateContentDto.cs

@ -40,7 +40,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// True to automatically publish the content. /// True to automatically publish the content.
/// </summary> /// </summary>
[FromQuery] [FromQuery]
[Obsolete("Use status query string.")] [Obsolete("Use 'status' query string now.")]
public bool Publish { get; set; } public bool Publish { get; set; }
public CreateContent ToCommand() public CreateContent ToCommand()

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs

@ -28,7 +28,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// <summary> /// <summary>
/// True to automatically publish the content. /// True to automatically publish the content.
/// </summary> /// </summary>
[Obsolete("Use Bulk endpoint")] [Obsolete("Use bulk endpoint now.")]
public bool Publish { get; set; } public bool Publish { get; set; }
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/UpsertContentDto.cs

@ -34,7 +34,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// True to automatically publish the content. /// True to automatically publish the content.
/// </summary> /// </summary>
[FromQuery] [FromQuery]
[Obsolete("Use status query string.")] [Obsolete("Use 'status' query string now.")]
public bool Publish { get; set; } public bool Publish { get; set; }
public UpsertContent ToCommand(DomainId id) public UpsertContent ToCommand(DomainId id)

3
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs

@ -7,6 +7,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
namespace Squidex.Areas.Api.Controllers.Schemas.Models namespace Squidex.Areas.Api.Controllers.Schemas.Models
{ {
@ -16,7 +17,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
{ {
return new ConfigurePreviewUrls return new ConfigurePreviewUrls
{ {
PreviewUrls = new Dictionary<string, string>(this) PreviewUrls = new Dictionary<string, string>(this).ToImmutableDictionary()
}; };
} }
} }

20
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureUIFieldsDto.cs

@ -5,9 +5,9 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models namespace Squidex.Areas.Api.Controllers.Schemas.Models
{ {
@ -16,28 +16,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary> /// <summary>
/// The name of fields that are used in content lists. /// The name of fields that are used in content lists.
/// </summary> /// </summary>
public List<string>? FieldsInLists { get; set; } public FieldNames? FieldsInLists { get; set; }
/// <summary> /// <summary>
/// The name of fields that are used in content references. /// The name of fields that are used in content references.
/// </summary> /// </summary>
public List<string>? FieldsInReferences { get; set; } public FieldNames? FieldsInReferences { get; set; }
public ConfigureUIFields ToCommand() public ConfigureUIFields ToCommand()
{ {
var command = new ConfigureUIFields(); return SimpleMapper.Map(this, new ConfigureUIFields());
if (FieldsInLists != null)
{
command.FieldsInLists = new FieldNames(FieldsInLists);
}
if (FieldsInReferences != null)
{
command.FieldsInReferences = new FieldNames(FieldsInReferences);
}
return command;
} }
} }
} }

20
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs

@ -5,6 +5,8 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
@ -19,10 +21,26 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")] [LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// The type of the schema.
/// </summary>
public SchemaType Type { get; set; }
/// <summary> /// <summary>
/// Set to true to allow a single content item only. /// Set to true to allow a single content item only.
/// </summary> /// </summary>
public bool IsSingleton { get; set; } [Obsolete("Use 'type' field now.")]
public bool IsSingleton
{
get => Type == SchemaType.Singleton;
set
{
if (value)
{
Type = SchemaType.Singleton;
}
}
}
public CreateSchema ToCommand() public CreateSchema ToCommand()
{ {

2
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs

@ -97,7 +97,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
/// <summary> /// <summary>
/// True to resolve first image in the content list. /// True to resolve first image in the content list.
/// </summary> /// </summary>
[Obsolete("Use ResolveFirst now")] [Obsolete("Use 'resolveFirst' field now")]
public bool ResolveImage public bool ResolveImage
{ {
get => ResolveFirst; get => ResolveFirst;

102
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs

@ -1,102 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
public sealed class SchemaDetailsDto : SchemaDto
{
private static readonly Dictionary<string, string> EmptyPreviewUrls = new Dictionary<string, string>();
/// <summary>
/// The scripts.
/// </summary>
[LocalizedRequired]
public SchemaScriptsDto Scripts { get; set; } = new SchemaScriptsDto();
/// <summary>
/// The preview Urls.
/// </summary>
[LocalizedRequired]
public Dictionary<string, string> PreviewUrls { get; set; } = EmptyPreviewUrls;
/// <summary>
/// The name of fields that are used in content lists.
/// </summary>
[LocalizedRequired]
public List<string> FieldsInLists { get; set; }
/// <summary>
/// The name of fields that are used in content references.
/// </summary>
[LocalizedRequired]
public List<string> FieldsInReferences { get; set; }
/// <summary>
/// The field rules.
/// </summary>
public List<FieldRuleDto> FieldRules { get; set; }
/// <summary>
/// The list of fields.
/// </summary>
[LocalizedRequired]
public List<FieldDto> Fields { get; set; }
public static SchemaDetailsDto FromSchemaWithDetails(ISchemaEntity schema, Resources resources)
{
var result = new SchemaDetailsDto();
SimpleMapper.Map(schema, result);
SimpleMapper.Map(schema.SchemaDef, result);
SimpleMapper.Map(schema.SchemaDef.Scripts, result.Scripts);
SimpleMapper.Map(schema.SchemaDef.Properties, result.Properties);
result.FieldsInLists = schema.SchemaDef.FieldsInLists.ToList();
result.FieldsInReferences = schema.SchemaDef.FieldsInReferences.ToList();
result.FieldRules = schema.SchemaDef.FieldRules.Select(FieldRuleDto.FromFieldRule).ToList();
if (schema.SchemaDef.PreviewUrls.Count > 0)
{
result.PreviewUrls = new Dictionary<string, string>(schema.SchemaDef.PreviewUrls);
}
result.Fields = new List<FieldDto>();
foreach (var field in schema.SchemaDef.Fields)
{
result.Fields.Add(FieldDto.FromField(field));
}
result.CreateLinks(resources);
return result;
}
protected override void CreateLinks(Resources resources)
{
base.CreateLinks(resources);
var allowUpdate = resources.CanUpdateSchema(Name);
if (Fields != null)
{
foreach (var nested in Fields)
{
nested.CreateLinks(resources, Name, allowUpdate);
}
}
}
}
}

93
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -5,10 +5,15 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using NodaTime; using NodaTime;
using Squidex.Areas.Api.Controllers.Contents; using Squidex.Areas.Api.Controllers.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web; using Squidex.Web;
@ -22,6 +27,18 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// </summary> /// </summary>
public DomainId Id { get; set; } public DomainId Id { get; set; }
/// <summary>
/// The user that has created the schema.
/// </summary>
[LocalizedRequired]
public RefToken CreatedBy { get; set; }
/// <summary>
/// The user that has updated the schema.
/// </summary>
[LocalizedRequired]
public RefToken LastModifiedBy { get; set; }
/// <summary> /// <summary>
/// The name of the schema. Unique within the app. /// The name of the schema. Unique within the app.
/// </summary> /// </summary>
@ -29,6 +46,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")] [LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// The type of the schema.
/// </summary>
public SchemaType Type { get; set; }
/// <summary> /// <summary>
/// The name of the category. /// The name of the category.
/// </summary> /// </summary>
@ -43,7 +65,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary> /// <summary>
/// Indicates if the schema is a singleton. /// Indicates if the schema is a singleton.
/// </summary> /// </summary>
public bool IsSingleton { get; set; } [Obsolete("Use 'type' field now.")]
public bool IsSingleton
{
get => Type == SchemaType.Singleton;
}
/// <summary> /// <summary>
/// Indicates if the schema is published. /// Indicates if the schema is published.
@ -51,41 +77,74 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
public bool IsPublished { get; set; } public bool IsPublished { get; set; }
/// <summary> /// <summary>
/// The user that has created the schema. /// The date and time when the schema has been created.
/// </summary>
public Instant Created { get; set; }
/// <summary>
/// The date and time when the schema has been modified last.
/// </summary>
public Instant LastModified { get; set; }
/// <summary>
/// The version of the schema.
/// </summary>
public long Version { get; set; }
/// <summary>
/// The scripts.
/// </summary> /// </summary>
[LocalizedRequired] [LocalizedRequired]
public RefToken CreatedBy { get; set; } public SchemaScriptsDto Scripts { get; set; } = new SchemaScriptsDto();
/// <summary> /// <summary>
/// The user that has updated the schema. /// The preview Urls.
/// </summary> /// </summary>
[LocalizedRequired] [LocalizedRequired]
public RefToken LastModifiedBy { get; set; } public ImmutableDictionary<string, string> PreviewUrls { get; set; }
/// <summary> /// <summary>
/// The date and time when the schema has been created. /// The name of fields that are used in content lists.
/// </summary> /// </summary>
public Instant Created { get; set; } [LocalizedRequired]
public FieldNames FieldsInLists { get; set; }
/// <summary> /// <summary>
/// The date and time when the schema has been modified last. /// The name of fields that are used in content references.
/// </summary> /// </summary>
public Instant LastModified { get; set; } [LocalizedRequired]
public FieldNames FieldsInReferences { get; set; }
/// <summary> /// <summary>
/// The version of the schema. /// The field rules.
/// </summary> /// </summary>
public long Version { get; set; } public List<FieldRuleDto> FieldRules { get; set; }
public static SchemaDto FromSchema(ISchemaEntity schema, Resources controller) /// <summary>
/// The list of fields.
/// </summary>
[LocalizedRequired]
public List<FieldDto> Fields { get; set; }
public static SchemaDto FromSchema(ISchemaEntity schema, Resources resources)
{ {
var result = new SchemaDto(); var result = new SchemaDto();
SimpleMapper.Map(schema, result); SimpleMapper.Map(schema, result);
SimpleMapper.Map(schema.SchemaDef, result); SimpleMapper.Map(schema.SchemaDef, result);
SimpleMapper.Map(schema.SchemaDef.Scripts, result.Scripts);
SimpleMapper.Map(schema.SchemaDef.Properties, result.Properties); SimpleMapper.Map(schema.SchemaDef.Properties, result.Properties);
result.CreateLinks(controller); result.FieldRules = schema.SchemaDef.FieldRules.Select(FieldRuleDto.FromFieldRule).ToList();
result.Fields = new List<FieldDto>();
foreach (var field in schema.SchemaDef.Fields)
{
result.Fields.Add(FieldDto.FromField(field));
}
result.CreateLinks(resources);
return result; return result;
} }
@ -144,6 +203,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
{ {
AddDeleteLink("delete", resources.Url<SchemasController>(x => nameof(x.DeleteSchema), values)); AddDeleteLink("delete", resources.Url<SchemasController>(x => nameof(x.DeleteSchema), values));
} }
if (Fields != null)
{
foreach (var nested in Fields)
{
nested.CreateLinks(resources, Name, allowUpdate);
}
}
} }
} }
} }

17
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaDto.cs

@ -8,6 +8,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models namespace Squidex.Areas.Api.Controllers.Schemas.Models
@ -27,12 +28,12 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary> /// <summary>
/// The names of the fields that should be used in references. /// The names of the fields that should be used in references.
/// </summary> /// </summary>
public string[]? FieldsInReferences { get; set; } public FieldNames? FieldsInReferences { get; set; }
/// <summary> /// <summary>
/// The names of the fields that should be shown in lists, including meta fields. /// The names of the fields that should be shown in lists, including meta fields.
/// </summary> /// </summary>
public string[]? FieldsInLists { get; set; } public FieldNames? FieldsInLists { get; set; }
/// <summary> /// <summary>
/// Optional fields. /// Optional fields.
@ -42,7 +43,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary> /// <summary>
/// The optional preview urls. /// The optional preview urls.
/// </summary> /// </summary>
public Dictionary<string, string>? PreviewUrls { get; set; } public ImmutableDictionary<string, string>? PreviewUrls { get; set; }
/// <summary> /// <summary>
/// The category. /// The category.
@ -72,16 +73,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
SimpleMapper.Map(dto.Scripts, command.Scripts); SimpleMapper.Map(dto.Scripts, command.Scripts);
} }
if (dto.FieldsInLists != null)
{
command.FieldsInLists = new FieldNames(dto.FieldsInLists);
}
if (dto.FieldsInReferences != null)
{
command.FieldsInReferences = new FieldNames(dto.FieldsInReferences);
}
if (dto.Fields?.Length > 0) if (dto.Fields?.Length > 0)
{ {
var fields = new List<UpsertSchemaField>(); var fields = new List<UpsertSchemaField>();

44
backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs

@ -42,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPost] [HttpPost]
[Route("apps/{app}/schemas/{name}/fields/")] [Route("apps/{app}/schemas/{name}/fields/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 201)] [ProducesResponseType(typeof(SchemaDto), 201)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostField(string app, string name, [FromBody] AddFieldDto request) public async Task<IActionResult> PostField(string app, string name, [FromBody] AddFieldDto request)
@ -69,7 +69,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPost] [HttpPost]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 201)] [ProducesResponseType(typeof(SchemaDto), 201)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostNestedField(string app, string name, long parentId, [FromBody] AddFieldDto request) public async Task<IActionResult> PostNestedField(string app, string name, long parentId, [FromBody] AddFieldDto request)
@ -94,7 +94,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/ui/")] [Route("apps/{app}/schemas/{name}/fields/ui/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutSchemaUIFields(string app, string name, [FromBody] ConfigureUIFieldsDto request) public async Task<IActionResult> PutSchemaUIFields(string app, string name, [FromBody] ConfigureUIFieldsDto request)
@ -119,7 +119,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/ordering/")] [Route("apps/{app}/schemas/{name}/fields/ordering/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutSchemaFieldOrdering(string app, string name, [FromBody] ReorderFieldsDto request) public async Task<IActionResult> PutSchemaFieldOrdering(string app, string name, [FromBody] ReorderFieldsDto request)
@ -145,7 +145,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/ordering/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/ordering/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutNestedFieldOrdering(string app, string name, long parentId, [FromBody] ReorderFieldsDto request) public async Task<IActionResult> PutNestedFieldOrdering(string app, string name, long parentId, [FromBody] ReorderFieldsDto request)
@ -171,7 +171,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/")] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutField(string app, string name, long id, [FromBody] UpdateFieldDto request) public async Task<IActionResult> PutField(string app, string name, long id, [FromBody] UpdateFieldDto request)
@ -198,7 +198,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutNestedField(string app, string name, long parentId, long id, [FromBody] UpdateFieldDto request) public async Task<IActionResult> PutNestedField(string app, string name, long parentId, long id, [FromBody] UpdateFieldDto request)
@ -226,7 +226,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/lock/")] [Route("apps/{app}/schemas/{name}/fields/{id:long}/lock/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> LockField(string app, string name, long id) public async Task<IActionResult> LockField(string app, string name, long id)
@ -255,7 +255,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/lock/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/lock/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> LockNestedField(string app, string name, long parentId, long id) public async Task<IActionResult> LockNestedField(string app, string name, long parentId, long id)
@ -283,7 +283,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/hide/")] [Route("apps/{app}/schemas/{name}/fields/{id:long}/hide/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> HideField(string app, string name, long id) public async Task<IActionResult> HideField(string app, string name, long id)
@ -312,7 +312,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/hide/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/hide/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> HideNestedField(string app, string name, long parentId, long id) public async Task<IActionResult> HideNestedField(string app, string name, long parentId, long id)
@ -340,7 +340,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/show/")] [Route("apps/{app}/schemas/{name}/fields/{id:long}/show/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> ShowField(string app, string name, long id) public async Task<IActionResult> ShowField(string app, string name, long id)
@ -369,7 +369,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/show/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/show/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> ShowNestedField(string app, string name, long parentId, long id) public async Task<IActionResult> ShowNestedField(string app, string name, long parentId, long id)
@ -397,7 +397,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/enable/")] [Route("apps/{app}/schemas/{name}/fields/{id:long}/enable/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> EnableField(string app, string name, long id) public async Task<IActionResult> EnableField(string app, string name, long id)
@ -426,7 +426,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/enable/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/enable/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> EnableNestedField(string app, string name, long parentId, long id) public async Task<IActionResult> EnableNestedField(string app, string name, long parentId, long id)
@ -454,7 +454,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/disable/")] [Route("apps/{app}/schemas/{name}/fields/{id:long}/disable/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DisableField(string app, string name, long id) public async Task<IActionResult> DisableField(string app, string name, long id)
@ -483,7 +483,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks> /// </remarks>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/disable/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/disable/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DisableNestedField(string app, string name, long parentId, long id) public async Task<IActionResult> DisableNestedField(string app, string name, long parentId, long id)
@ -508,7 +508,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpDelete] [HttpDelete]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/")] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DeleteField(string app, string name, long id) public async Task<IActionResult> DeleteField(string app, string name, long id)
@ -534,7 +534,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpDelete] [HttpDelete]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> DeleteNestedField(string app, string name, long parentId, long id) public async Task<IActionResult> DeleteNestedField(string app, string name, long parentId, long id)
@ -546,14 +546,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas
return Ok(response); return Ok(response);
} }
private async Task<SchemaDetailsDto> InvokeCommandAsync(ICommand command) private async Task<SchemaDto> InvokeCommandAsync(ICommand command)
{ {
var context = await CommandBus.PublishAsync(command); var context = await CommandBus.PublishAsync(command);
var result = context.Result<ISchemaEntity>(); var result = context.Result<ISchemaEntity>();
var response = SchemaDetailsDto.FromSchemaWithDetails(result, Resources); var response = SchemaDto.FromSchema(result, Resources);
return response; return response;
} }
} }
} }

28
backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -77,7 +77,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpGet] [HttpGet]
[Route("apps/{app}/schemas/{name}/")] [Route("apps/{app}/schemas/{name}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasRead)] [ApiPermissionOrAnonymous(Permissions.AppSchemasRead)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> GetSchema(string app, string name) public async Task<IActionResult> GetSchema(string app, string name)
@ -91,7 +91,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
var response = Deferred.Response(() => var response = Deferred.Response(() =>
{ {
return SchemaDetailsDto.FromSchemaWithDetails(schema, Resources); return SchemaDto.FromSchema(schema, Resources);
}); });
Response.Headers[HeaderNames.ETag] = schema.ToEtag(); Response.Headers[HeaderNames.ETag] = schema.ToEtag();
@ -111,7 +111,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPost] [HttpPost]
[Route("apps/{app}/schemas/")] [Route("apps/{app}/schemas/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 201)] [ProducesResponseType(typeof(SchemaDto), 201)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasCreate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasCreate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostSchema(string app, [FromBody] CreateSchemaDto request) public async Task<IActionResult> PostSchema(string app, [FromBody] CreateSchemaDto request)
@ -136,7 +136,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/")] [Route("apps/{app}/schemas/{name}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutSchema(string app, string name, [FromBody] UpdateSchemaDto request) public async Task<IActionResult> PutSchema(string app, string name, [FromBody] UpdateSchemaDto request)
@ -161,7 +161,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/sync")] [Route("apps/{app}/schemas/{name}/sync")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutSchemaSync(string app, string name, [FromBody] SynchronizeSchemaDto request) public async Task<IActionResult> PutSchemaSync(string app, string name, [FromBody] SynchronizeSchemaDto request)
@ -186,7 +186,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/category")] [Route("apps/{app}/schemas/{name}/category")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutCategory(string app, string name, [FromBody] ChangeCategoryDto request) public async Task<IActionResult> PutCategory(string app, string name, [FromBody] ChangeCategoryDto request)
@ -211,7 +211,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/preview-urls")] [Route("apps/{app}/schemas/{name}/preview-urls")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutPreviewUrls(string app, string name, [FromBody] ConfigurePreviewUrlsDto request) public async Task<IActionResult> PutPreviewUrls(string app, string name, [FromBody] ConfigurePreviewUrlsDto request)
@ -236,7 +236,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/scripts/")] [Route("apps/{app}/schemas/{name}/scripts/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasScripts)] [ApiPermissionOrAnonymous(Permissions.AppSchemasScripts)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutScripts(string app, string name, [FromBody] SchemaScriptsDto request) public async Task<IActionResult> PutScripts(string app, string name, [FromBody] SchemaScriptsDto request)
@ -261,7 +261,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/rules/")] [Route("apps/{app}/schemas/{name}/rules/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutRules(string app, string name, [FromBody] ConfigureFieldRulesDto request) public async Task<IActionResult> PutRules(string app, string name, [FromBody] ConfigureFieldRulesDto request)
@ -284,7 +284,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/publish/")] [Route("apps/{app}/schemas/{name}/publish/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasPublish)] [ApiPermissionOrAnonymous(Permissions.AppSchemasPublish)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PublishSchema(string app, string name) public async Task<IActionResult> PublishSchema(string app, string name)
@ -307,7 +307,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/unpublish/")] [Route("apps/{app}/schemas/{name}/unpublish/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasPublish)] [ApiPermissionOrAnonymous(Permissions.AppSchemasPublish)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> UnpublishSchema(string app, string name) public async Task<IActionResult> UnpublishSchema(string app, string name)
@ -375,14 +375,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
} }
private async Task<SchemaDetailsDto> InvokeCommandAsync(ICommand command) private async Task<SchemaDto> InvokeCommandAsync(ICommand command)
{ {
var context = await CommandBus.PublishAsync(command); var context = await CommandBus.PublishAsync(command);
var result = context.Result<ISchemaEntity>(); var result = context.Result<ISchemaEntity>();
var response = SchemaDetailsDto.FromSchemaWithDetails(result, Resources); var response = SchemaDto.FromSchema(result, Resources);
return response; return response;
} }
} }
} }

28
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs

@ -11,6 +11,7 @@ using System.Linq;
using FluentAssertions; using FluentAssertions;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
#pragma warning disable SA1310 // Field names must not contain underscore #pragma warning disable SA1310 // Field names must not contain underscore
@ -437,11 +438,11 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
var urls1 = new Dictionary<string, string> var urls1 = new Dictionary<string, string>
{ {
["web"] = "Url" ["web"] = "Url"
}; }.ToImmutableDictionary();
var urls2 = new Dictionary<string, string> var urls2 = new Dictionary<string, string>
{ {
["web"] = "Url" ["web"] = "Url"
}; }.ToImmutableDictionary();
var schema_1 = schema_0.SetPreviewUrls(urls1); var schema_1 = schema_0.SetPreviewUrls(urls1);
var schema_2 = schema_1.SetPreviewUrls(urls2); var schema_2 = schema_1.SetPreviewUrls(urls2);
@ -457,7 +458,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
public void Should_serialize_and_deserialize_schema() public void Should_serialize_and_deserialize_schema()
{ {
var schemaSource = var schemaSource =
TestUtils.MixedSchema(true) TestUtils.MixedSchema(SchemaType.Singleton)
.ChangeCategory("Category") .ChangeCategory("Category")
.SetFieldRules(FieldRule.Hide("2")) .SetFieldRules(FieldRule.Hide("2"))
.SetFieldsInLists("field2") .SetFieldsInLists("field2")
@ -465,7 +466,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
.SetPreviewUrls(new Dictionary<string, string> .SetPreviewUrls(new Dictionary<string, string>
{ {
["web"] = "Url" ["web"] = "Url"
}) }.ToImmutableDictionary())
.SetScripts(new SchemaScripts .SetScripts(new SchemaScripts
{ {
Create = "<create-script>" Create = "<create-script>"
@ -476,6 +477,25 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
schemaTarget.Should().BeEquivalentTo(schemaSource); schemaTarget.Should().BeEquivalentTo(schemaSource);
} }
[Fact]
public void Should_deserialize_obsolete_isSingleton_property()
{
var schemaSource = new
{
name = "my-schema",
isPublished = true,
isSingleton = true
};
var expected =
new Schema("my-schema", type: SchemaType.Singleton)
.Publish();
var schemaTarget = schemaSource.SerializeAndDeserialize<Schema>();
schemaTarget.Should().BeEquivalentTo(expected);
}
private static RootField<NumberFieldProperties> CreateField(int id) private static RootField<NumberFieldProperties> CreateField(int id)
{ {
return Fields.Number(id, $"my-field-{id}", Partitioning.Invariant); return Fields.Number(id, $"my-field-{id}", Partitioning.Invariant);

3
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs

@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Core.EventSynchronization;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
@ -88,7 +89,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var previewUrls = new Dictionary<string, string> var previewUrls = new Dictionary<string, string>
{ {
["web"] = "Url" ["web"] = "Url"
}; }.ToImmutableDictionary();
var sourceSchema = var sourceSchema =
new Schema("source"); new Schema("source");

4
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs

@ -38,7 +38,9 @@ namespace Squidex.Domain.Apps.Core.Operations.GenerateEdmSchema
return (new EdmComplexType("Squidex", string.Join(".", names)), true); return (new EdmComplexType("Squidex", string.Join(".", names)), true);
}); });
var edmModel = TestUtils.MixedSchema().BuildEdmType(true, languagesConfig.ToResolver(), typeFactory); var edmModel =
TestUtils.MixedSchema()
.BuildEdmType(true, languagesConfig.ToResolver(), typeFactory);
Assert.NotNull(edmModel); Assert.NotNull(edmModel);
} }

4
backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs

@ -81,9 +81,9 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
return new NewtonsoftJsonSerializer(serializerSettings); return new NewtonsoftJsonSerializer(serializerSettings);
} }
public static Schema MixedSchema(bool isSingleton = false) public static Schema MixedSchema(SchemaType type = SchemaType.Default)
{ {
var schema = new Schema("user", isSingleton: isSingleton) var schema = new Schema("user", type: type)
.Publish() .Publish()
.AddArray(101, "root-array", Partitioning.Language, f => f .AddArray(101, "root-array", Partitioning.Language, f => f
.AddAssets(201, "nested-assets") .AddAssets(201, "nested-assets")

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultWorkflowsValidatorTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs

@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
Mocks.Schema(appId, schemaId, new Schema(schemaId.Name)); Mocks.Schema(appId, schemaId, new Schema(schemaId.Name));
singletonSchema = singletonSchema =
Mocks.Schema(appId, schemaId, new Schema(schemaId.Name, isSingleton: true)); Mocks.Schema(appId, schemaId, new Schema(schemaId.Name, type: SchemaType.Singleton));
} }
[Fact] [Fact]

7
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/SingletonCommandMiddlewareTests.cs

@ -8,6 +8,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
@ -23,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact] [Fact]
public async Task Should_create_content_if_singleton_schema_is_created() public async Task Should_create_content_if_singleton_schema_is_created()
{ {
var command = new CreateSchema { IsSingleton = true, Name = "my-schema" }; var command = new CreateSchema { Type = SchemaType.Singleton, Name = "my-schema" };
var context = var context =
new CommandContext(command, commandBus) new CommandContext(command, commandBus)
@ -38,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact] [Fact]
public async Task Should_not_create_content_if_non_singleton_schema_is_created() public async Task Should_not_create_content_if_non_singleton_schema_is_created()
{ {
var command = new CreateSchema { IsSingleton = false }; var command = new CreateSchema();
var context = var context =
new CommandContext(command, commandBus) new CommandContext(command, commandBus)
@ -53,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact] [Fact]
public async Task Should_not_create_content_if_singleton_schema_not_created() public async Task Should_not_create_content_if_singleton_schema_not_created()
{ {
var command = new CreateSchema { IsSingleton = true }; var command = new CreateSchema { Type = SchemaType.Singleton };
var context = var context =
new CommandContext(command, commandBus); new CommandContext(command, commandBus);

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/GuardSchemaTests.cs

@ -5,13 +5,13 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Xunit; using Xunit;
@ -657,7 +657,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards
[Fact] [Fact]
public void CanConfigurePreviewUrls_should_not_throw_exception_if_valid() public void CanConfigurePreviewUrls_should_not_throw_exception_if_valid()
{ {
var command = new ConfigurePreviewUrls { PreviewUrls = new Dictionary<string, string>() }; var command = new ConfigurePreviewUrls { PreviewUrls = ImmutableDictionary.Empty<string, string>() };
GuardSchema.CanConfigurePreviewUrls(command); GuardSchema.CanConfigurePreviewUrls(command);
} }

10
backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs

@ -15,6 +15,7 @@ using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Log; using Squidex.Log;
using Xunit; using Xunit;
@ -55,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
{ {
var properties = new SchemaProperties(); var properties = new SchemaProperties();
var command = new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties, IsSingleton = true }; var command = new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties, Type = SchemaType.Singleton };
var result = await PublishAsync(command); var result = await PublishAsync(command);
@ -64,12 +65,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
Assert.Equal(AppId, sut.Snapshot.AppId.Id); Assert.Equal(AppId, sut.Snapshot.AppId.Id);
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name); Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name);
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name); Assert.Equal(SchemaType.Singleton, sut.Snapshot.SchemaDef.Type);
Assert.True(sut.Snapshot.SchemaDef.IsSingleton);
LastEvents LastEvents
.ShouldHaveSameEvents( .ShouldHaveSameEvents(
CreateEvent(new SchemaCreated { Schema = new Schema(command.Name, command.Properties, command.IsSingleton) }) CreateEvent(new SchemaCreated { Schema = new Schema(command.Name, command.Properties, SchemaType.Singleton) })
); );
} }
@ -291,7 +291,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
PreviewUrls = new Dictionary<string, string> PreviewUrls = new Dictionary<string, string>
{ {
["Web"] = "web-url" ["Web"] = "web-url"
} }.ToImmutableDictionary()
}; };
await ExecuteCreateAsync(); await ExecuteCreateAsync();

7
backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs

@ -10,6 +10,7 @@ using FluentAssertions;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Entities.Schemas namespace Squidex.Domain.Apps.Entities.Schemas
@ -47,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
PreviewUrls = new Dictionary<string, string> PreviewUrls = new Dictionary<string, string>
{ {
["mobile"] = "http://mobile" ["mobile"] = "http://mobile"
}, }.ToImmutableDictionary(),
Category = "myCategory" Category = "myCategory"
}; };
@ -69,10 +70,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas
.SetPreviewUrls(new Dictionary<string, string> .SetPreviewUrls(new Dictionary<string, string>
{ {
["mobile"] = "http://mobile" ["mobile"] = "http://mobile"
}) }.ToImmutableDictionary())
.Publish(); .Publish();
var actual = command.BuildSchema("my-schema", false); var actual = command.BuildSchema("my-schema", SchemaType.Default);
actual.Should().BeEquivalentTo(expected); actual.Should().BeEquivalentTo(expected);
} }

14
backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemasSearchSourceTests.cs

@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
var ctx = ContextWithPermission(); var ctx = ContextWithPermission();
var schema1 = CreateSchema("schemaA1", false); var schema1 = CreateSchema("schemaA1");
A.CallTo(() => appProvider.GetSchemasAsync(appId.Id)) A.CallTo(() => appProvider.GetSchemasAsync(appId.Id))
.Returns(new List<ISchemaEntity> { schema1 }); .Returns(new List<ISchemaEntity> { schema1 });
@ -61,9 +61,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas
var ctx = ContextWithPermission(permission.Id); var ctx = ContextWithPermission(permission.Id);
var schema1 = CreateSchema("schemaA1", false); var schema1 = CreateSchema("schemaA1");
var schema2 = CreateSchema("schemaA2", false); var schema2 = CreateSchema("schemaA2");
var schema3 = CreateSchema("schemaB2", false); var schema3 = CreateSchema("schemaB2");
A.CallTo(() => appProvider.GetSchemasAsync(appId.Id)) A.CallTo(() => appProvider.GetSchemasAsync(appId.Id))
.Returns(new List<ISchemaEntity> { schema1, schema2, schema3 }); .Returns(new List<ISchemaEntity> { schema1, schema2, schema3 });
@ -93,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
var ctx = ContextWithPermission(permission.Id); var ctx = ContextWithPermission(permission.Id);
var schema1 = CreateSchema("schemaA1", true); var schema1 = CreateSchema("schemaA1", SchemaType.Singleton);
A.CallTo(() => appProvider.GetSchemasAsync(appId.Id)) A.CallTo(() => appProvider.GetSchemasAsync(appId.Id))
.Returns(new List<ISchemaEntity> { schema1 }); .Returns(new List<ISchemaEntity> { schema1 });
@ -112,9 +112,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas
.Add("schemaA1 Content", SearchResultType.Content, "schemaA1-content-url", "schemaA1")); .Add("schemaA1 Content", SearchResultType.Content, "schemaA1-content-url", "schemaA1"));
} }
private ISchemaEntity CreateSchema(string name, bool isSingleton) private ISchemaEntity CreateSchema(string name, SchemaType type = SchemaType.Default)
{ {
return Mocks.Schema(appId, NamedId.Of(DomainId.NewGuid(), name), new Schema(name, null, isSingleton)); return Mocks.Schema(appId, NamedId.Of(DomainId.NewGuid(), name), new Schema(name, type: type));
} }
private Context ContextWithPermission(string? permission = null) private Context ContextWithPermission(string? permission = null)

1
backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableListTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Xunit; using Xunit;

4
frontend/app/features/administration/state/event-consumers.state.spec.ts

@ -48,7 +48,7 @@ describe('EventConsumersState', () => {
it('should reset loading state if loading failed', () => { it('should reset loading state if loading failed', () => {
eventConsumersService.setup(x => x.getEventConsumers()) eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
eventConsumersState.load().pipe(onErrorResumeNext()).subscribe(); eventConsumersState.load().pipe(onErrorResumeNext()).subscribe();
@ -68,7 +68,7 @@ describe('EventConsumersState', () => {
it('should show notification on load error if silent is false', () => { it('should show notification on load error if silent is false', () => {
eventConsumersService.setup(x => x.getEventConsumers()) eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => throwError({})).verifiable(); .returns(() => throwError('Service Error')).verifiable();
eventConsumersState.load(true, false).pipe(onErrorResumeNext()).subscribe(); eventConsumersState.load(true, false).pipe(onErrorResumeNext()).subscribe();

6
frontend/app/features/administration/state/users.state.spec.ts

@ -53,7 +53,7 @@ describe('UsersState', () => {
it('should reset loading state if loading failed', () => { it('should reset loading state if loading failed', () => {
usersService.setup(x => x.getUsers(10, 0, undefined)) usersService.setup(x => x.getUsers(10, 0, undefined))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
usersState.load().pipe(onErrorResumeNext()).subscribe(); usersState.load().pipe(onErrorResumeNext()).subscribe();
@ -136,11 +136,11 @@ describe('UsersState', () => {
it('should return null on select if user is not found', () => { it('should return null on select if user is not found', () => {
usersService.setup(x => x.getUser('unknown')) usersService.setup(x => x.getUser('unknown'))
.returns(() => throwError({})).verifiable(); .returns(() => throwError('Service Error')).verifiable();
let userSelected: UserDto; let userSelected: UserDto;
usersState.select('unknown').subscribe(x => { usersState.select('unknown').pipe(onErrorResumeNext()).subscribe(x => {
userSelected = x!; userSelected = x!;
}).unsubscribe(); }).unsubscribe();

4
frontend/app/features/content/pages/content/content-page.component.ts

@ -9,7 +9,7 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, ContentDto, ContentsState, defined, DialogService, EditContentForm, fadeAnimation, LanguagesState, ModalModel, ResourceOwner, SchemaDetailsDto, SchemasState, TempService, Version } from '@app/shared'; import { ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, ContentDto, ContentsState, defined, DialogService, EditContentForm, fadeAnimation, LanguagesState, ModalModel, ResourceOwner, SchemaDto, SchemasState, TempService, Version } from '@app/shared';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { filter, map, tap } from 'rxjs/operators'; import { filter, map, tap } from 'rxjs/operators';
import { ContentReferencesComponent } from './references/content-references.component'; import { ContentReferencesComponent } from './references/content-references.component';
@ -29,7 +29,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
@ViewChild(ContentReferencesComponent) @ViewChild(ContentReferencesComponent)
public references: ContentReferencesComponent; public references: ContentReferencesComponent;
public schema: SchemaDetailsDto; public schema: SchemaDto;
public formContext: any; public formContext: any;

4
frontend/app/features/content/pages/content/editor/content-editor.component.ts

@ -6,7 +6,7 @@
*/ */
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { AppLanguageDto, EditContentForm, FieldForm, FieldSection, RootFieldDto, SchemaDetailsDto, Version } from '@app/shared'; import { AppLanguageDto, EditContentForm, FieldForm, FieldSection, RootFieldDto, SchemaDto, Version } from '@app/shared';
@Component({ @Component({
selector: 'sqx-content-editor', selector: 'sqx-content-editor',
@ -30,7 +30,7 @@ export class ContentEditorComponent {
public contentFormCompare?: EditContentForm | null; public contentFormCompare?: EditContentForm | null;
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
@Input() @Input()
public formContext: any; public formContext: any;

4
frontend/app/features/content/pages/contents/contents-page.component.ts

@ -9,7 +9,7 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { AppLanguageDto, AppsState, ContentDto, ContentsState, ContributorsState, defined, fadeAnimation, LanguagesState, ModalModel, Queries, Query, queryModelFromSchema, QuerySynchronizer, ResourceOwner, Router2State, SchemaDetailsDto, SchemasState, switchSafe, TableFields, TempService, UIState } from '@app/shared'; import { AppLanguageDto, AppsState, ContentDto, ContentsState, ContributorsState, defined, fadeAnimation, LanguagesState, ModalModel, Queries, Query, queryModelFromSchema, QuerySynchronizer, ResourceOwner, Router2State, SchemaDto, SchemasState, switchSafe, TableFields, TempService, UIState } from '@app/shared';
import { combineLatest } from 'rxjs'; import { combineLatest } from 'rxjs';
import { distinctUntilChanged, map, switchMap, take, tap } from 'rxjs/operators'; import { distinctUntilChanged, map, switchMap, take, tap } from 'rxjs/operators';
import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component'; import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component';
@ -29,7 +29,7 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit {
@ViewChild('dueTimeSelector', { static: false }) @ViewChild('dueTimeSelector', { static: false })
public dueTimeSelector: DueTimeSelectorComponent; public dueTimeSelector: DueTimeSelectorComponent;
public schema: SchemaDetailsDto; public schema: SchemaDto;
public tableView: TableFields; public tableView: TableFields;
public tableViewModal = new ModalModel(); public tableViewModal = new ModalModel();

4
frontend/app/features/content/shared/preview-button.component.ts

@ -6,7 +6,7 @@
*/ */
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { ContentDto, fadeAnimation, interpolate, LocalStoreService, ModalModel, SchemaDetailsDto, Settings, StatefulComponent } from '@app/shared'; import { ContentDto, fadeAnimation, interpolate, LocalStoreService, ModalModel, SchemaDto, Settings, StatefulComponent } from '@app/shared';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
@ -35,7 +35,7 @@ export class PreviewButtonComponent extends StatefulComponent<State> implements
public content: ContentDto; public content: ContentDto;
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public dropdown = new ModalModel(); public dropdown = new ModalModel();

4
frontend/app/features/content/shared/references/content-creator.component.ts

@ -6,7 +6,7 @@
*/ */
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { AppLanguageDto, ComponentContentsState, ContentDto, EditContentForm, ResourceOwner, SchemaDetailsDto, SchemaDto, SchemasState, Types } from '@app/shared'; import { AppLanguageDto, ComponentContentsState, ContentDto, EditContentForm, ResourceOwner, SchemaDto, SchemasState, Types } from '@app/shared';
@Component({ @Component({
selector: 'sqx-content-creator', selector: 'sqx-content-creator',
@ -38,7 +38,7 @@ export class ContentCreatorComponent extends ResourceOwner implements OnInit {
@Input() @Input()
public formContext: any; public formContext: any;
public schema: SchemaDetailsDto; public schema: SchemaDto;
public schemas: ReadonlyArray<SchemaDto> = []; public schemas: ReadonlyArray<SchemaDto> = [];
public contentForm: EditContentForm; public contentForm: EditContentForm;

4
frontend/app/features/content/shared/references/content-selector-item.component.ts

@ -8,7 +8,7 @@
/* tslint:disable: component-selector */ /* tslint:disable: component-selector */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { ContentDto, LanguageDto, SchemaDetailsDto } from '@app/shared'; import { ContentDto, LanguageDto, SchemaDto } from '@app/shared';
@Component({ @Component({
selector: '[sqxContentSelectorItem]', selector: '[sqxContentSelectorItem]',
@ -30,7 +30,7 @@ export class ContentSelectorItemComponent {
public language: LanguageDto; public language: LanguageDto;
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
@Input('sqxContentSelectorItem') @Input('sqxContentSelectorItem')
public content: ContentDto; public content: ContentDto;

4
frontend/app/features/content/shared/references/content-selector.component.ts

@ -6,7 +6,7 @@
*/ */
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ApiUrlConfig, AppsState, ComponentContentsState, ContentDto, LanguageDto, Query, QueryModel, queryModelFromSchema, ResourceOwner, SchemaDetailsDto, SchemaDto, SchemasState, Types } from '@app/shared'; import { ApiUrlConfig, AppsState, ComponentContentsState, ContentDto, LanguageDto, Query, QueryModel, queryModelFromSchema, ResourceOwner, SchemaDto, SchemasState, Types } from '@app/shared';
@Component({ @Component({
selector: 'sqx-content-selector', selector: 'sqx-content-selector',
@ -35,7 +35,7 @@ export class ContentSelectorComponent extends ResourceOwner implements OnInit {
@Input() @Input()
public alreadySelected: ReadonlyArray<ContentDto>; public alreadySelected: ReadonlyArray<ContentDto>;
public schema: SchemaDetailsDto; public schema: SchemaDto;
public schemas: ReadonlyArray<SchemaDto> = []; public schemas: ReadonlyArray<SchemaDto> = [];
public queryModel: QueryModel; public queryModel: QueryModel;

4
frontend/app/features/schemas/pages/schema/common/schema-edit-form.component.ts

@ -7,7 +7,7 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { EditSchemaForm, SchemaDetailsDto, SchemasState } from '@app/shared'; import { EditSchemaForm, SchemaDto, SchemasState } from '@app/shared';
@Component({ @Component({
selector: 'sqx-schema-edit-form', selector: 'sqx-schema-edit-form',
@ -16,7 +16,7 @@ import { EditSchemaForm, SchemaDetailsDto, SchemasState } from '@app/shared';
}) })
export class SchemaEditFormComponent implements OnChanges { export class SchemaEditFormComponent implements OnChanges {
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public fieldForm = new EditSchemaForm(this.formBuilder); public fieldForm = new EditSchemaForm(this.formBuilder);

4
frontend/app/features/schemas/pages/schema/export/schema-export-form.component.ts

@ -7,7 +7,7 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { SchemaDetailsDto, SchemasState, SynchronizeSchemaForm } from '@app/shared'; import { SchemaDto, SchemasState, SynchronizeSchemaForm } from '@app/shared';
@Component({ @Component({
selector: 'sqx-schema-export-form', selector: 'sqx-schema-export-form',
@ -16,7 +16,7 @@ import { SchemaDetailsDto, SchemasState, SynchronizeSchemaForm } from '@app/shar
}) })
export class SchemaExportFormComponent implements OnChanges { export class SchemaExportFormComponent implements OnChanges {
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public synchronizeForm = new SynchronizeSchemaForm(this.formBuilder); public synchronizeForm = new SynchronizeSchemaForm(this.formBuilder);

4
frontend/app/features/schemas/pages/schema/fields/field-wizard.component.ts

@ -7,7 +7,7 @@
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { AddFieldForm, AppSettingsDto, createProperties, EditFieldForm, FieldDto, fieldTypes, LanguagesState, RootFieldDto, SchemaDetailsDto, SchemasState, Types } from '@app/shared'; import { AddFieldForm, AppSettingsDto, createProperties, EditFieldForm, FieldDto, fieldTypes, LanguagesState, RootFieldDto, SchemaDto, SchemasState, Types } from '@app/shared';
const DEFAULT_FIELD = { name: '', partitioning: 'invariant', properties: createProperties('String') }; const DEFAULT_FIELD = { name: '', partitioning: 'invariant', properties: createProperties('String') };
@ -21,7 +21,7 @@ export class FieldWizardComponent implements OnInit {
public nameInput: ElementRef<HTMLElement>; public nameInput: ElementRef<HTMLElement>;
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
@Input() @Input()
public settings: AppSettingsDto; public settings: AppSettingsDto;

4
frontend/app/features/schemas/pages/schema/fields/field.component.ts

@ -8,7 +8,7 @@
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { AppSettingsDto, createProperties, DialogModel, EditFieldForm, fadeAnimation, LanguageDto, ModalModel, NestedFieldDto, RootFieldDto, SchemaDetailsDto, SchemasState, sorted } from '@app/shared'; import { AppSettingsDto, createProperties, DialogModel, EditFieldForm, fadeAnimation, LanguageDto, ModalModel, NestedFieldDto, RootFieldDto, SchemaDto, SchemasState, sorted } from '@app/shared';
@Component({ @Component({
selector: 'sqx-field', selector: 'sqx-field',
@ -23,7 +23,7 @@ export class FieldComponent implements OnChanges {
public field: NestedFieldDto | RootFieldDto; public field: NestedFieldDto | RootFieldDto;
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
@Input() @Input()
public parent: RootFieldDto; public parent: RootFieldDto;

4
frontend/app/features/schemas/pages/schema/fields/schema-fields.component.ts

@ -7,7 +7,7 @@
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, SchemaDetailsDto, SchemasState, sorted } from '@app/shared'; import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, SchemaDto, SchemasState, sorted } from '@app/shared';
@Component({ @Component({
selector: 'sqx-schema-fields', selector: 'sqx-schema-fields',
@ -18,7 +18,7 @@ export class SchemaFieldsComponent implements OnInit {
public fieldTypes = fieldTypes; public fieldTypes = fieldTypes;
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public addFieldDialog = new DialogModel(); public addFieldDialog = new DialogModel();

4
frontend/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts

@ -7,7 +7,7 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { ConfigurePreviewUrlsForm, SchemaDetailsDto, SchemasState } from '@app/shared'; import { ConfigurePreviewUrlsForm, SchemaDto, SchemasState } from '@app/shared';
@Component({ @Component({
selector: 'sqx-schema-preview-urls-form', selector: 'sqx-schema-preview-urls-form',
@ -16,7 +16,7 @@ import { ConfigurePreviewUrlsForm, SchemaDetailsDto, SchemasState } from '@app/s
}) })
export class SchemaPreviewUrlsFormComponent implements OnChanges { export class SchemaPreviewUrlsFormComponent implements OnChanges {
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public editForm = new ConfigurePreviewUrlsForm(this.formBuilder); public editForm = new ConfigurePreviewUrlsForm(this.formBuilder);

4
frontend/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts

@ -7,7 +7,7 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { ConfigureFieldRulesForm, FIELD_RULE_ACTIONS, SchemaDetailsDto, SchemasState } from '@app/shared'; import { ConfigureFieldRulesForm, FIELD_RULE_ACTIONS, SchemaDto, SchemasState } from '@app/shared';
@Component({ @Component({
selector: 'sqx-schema-field-rules-form', selector: 'sqx-schema-field-rules-form',
@ -16,7 +16,7 @@ import { ConfigureFieldRulesForm, FIELD_RULE_ACTIONS, SchemaDetailsDto, SchemasS
}) })
export class SchemaFieldRulesFormComponent implements OnChanges { export class SchemaFieldRulesFormComponent implements OnChanges {
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public editForm = new ConfigureFieldRulesForm(this.formBuilder); public editForm = new ConfigureFieldRulesForm(this.formBuilder);

4
frontend/app/features/schemas/pages/schema/schema-page.component.ts

@ -7,7 +7,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { defined, fadeAnimation, MessageBus, ModalModel, ResourceOwner, SchemaDetailsDto, SchemasState } from '@app/shared'; import { defined, fadeAnimation, MessageBus, ModalModel, ResourceOwner, SchemaDto, SchemasState } from '@app/shared';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { SchemaCloning } from './../messages'; import { SchemaCloning } from './../messages';
@ -22,7 +22,7 @@ import { SchemaCloning } from './../messages';
export class SchemaPageComponent extends ResourceOwner implements OnInit { export class SchemaPageComponent extends ResourceOwner implements OnInit {
public readonly exact = { exact: true }; public readonly exact = { exact: true };
public schema: SchemaDetailsDto; public schema: SchemaDto;
public schemaTab = this.route.queryParams.pipe(map(x => x['tab'] || 'fields')); public schemaTab = this.route.queryParams.pipe(map(x => x['tab'] || 'fields'));
public editOptionsDropdown = new ModalModel(); public editOptionsDropdown = new ModalModel();

4
frontend/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts

@ -7,7 +7,7 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { AppsState, EditScriptsForm, SchemaCompletions, SchemaDetailsDto, SchemasService, SchemasState } from '@app/shared'; import { AppsState, EditScriptsForm, SchemaCompletions, SchemaDto, SchemasService, SchemasState } from '@app/shared';
import { EMPTY, Observable } from 'rxjs'; import { EMPTY, Observable } from 'rxjs';
@Component({ @Component({
@ -17,7 +17,7 @@ import { EMPTY, Observable } from 'rxjs';
}) })
export class SchemaScriptsFormComponent implements OnChanges { export class SchemaScriptsFormComponent implements OnChanges {
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public schemaScript = 'query'; public schemaScript = 'query';
public schemaCompletions: Observable<SchemaCompletions> = EMPTY; public schemaCompletions: Observable<SchemaCompletions> = EMPTY;

4
frontend/app/features/schemas/pages/schema/ui/field-list.component.ts

@ -9,7 +9,7 @@
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { MetaFields, SchemaDetailsDto } from '@app/shared'; import { MetaFields, SchemaDto } from '@app/shared';
const META_FIELD_NAMES = Object.values(MetaFields); const META_FIELD_NAMES = Object.values(MetaFields);
@ -24,7 +24,7 @@ export class FieldListComponent implements OnChanges {
public emptyText = ''; public emptyText = '';
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
@Input() @Input()
public fieldNames: ReadonlyArray<string>; public fieldNames: ReadonlyArray<string>;

4
frontend/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts

@ -6,7 +6,7 @@
*/ */
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { SchemaDetailsDto, SchemasState } from '@app/shared'; import { SchemaDto, SchemasState } from '@app/shared';
type State = { fieldsInLists: ReadonlyArray<string>, fieldsInReferences: ReadonlyArray<string> }; type State = { fieldsInLists: ReadonlyArray<string>, fieldsInReferences: ReadonlyArray<string> };
@ -17,7 +17,7 @@ type State = { fieldsInLists: ReadonlyArray<string>, fieldsInReferences: Readonl
}) })
export class SchemaUIFormComponent implements OnChanges { export class SchemaUIFormComponent implements OnChanges {
@Input() @Input()
public schema: SchemaDetailsDto; public schema: SchemaDto;
public selectedTab = 0; public selectedTab = 0;

8
frontend/app/features/schemas/pages/schemas/schema-form.component.html

@ -29,11 +29,11 @@
<div class="row"> <div class="row">
<div class="col-6 type"> <div class="col-6 type">
<label> <label>
<input type="radio" class="radio-input" name="isSingleton" formControlName="isSingleton" [value]="false"> <input type="radio" class="radio-input" name="type" formControlName="type" [value]="'Default'">
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-auto"> <div class="col-auto">
<div class="btn-radio" [class.active]="createForm.form.controls['isSingleton'].value !== true"> <div class="btn-radio" [class.active]="createForm.form.controls['type'].value === 'Default'">
<i class="icon-multiple-content"></i> <i class="icon-multiple-content"></i>
</div> </div>
</div> </div>
@ -47,11 +47,11 @@
</div> </div>
<div class="col-6 type"> <div class="col-6 type">
<label> <label>
<input type="radio" class="radio-input" name="isSingleton" formControlName="isSingleton" [value]="true"> <input type="radio" class="radio-input" name="type" formControlName="type" [value]="'Singleton'">
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-auto"> <div class="col-auto">
<div class="btn-radio" [class.active]="createForm.form.controls['isSingleton'].value === true"> <div class="btn-radio" [class.active]="createForm.form.controls['type'].value === 'Singleton'">
<i class="icon-single-content"></i> <i class="icon-single-content"></i>
</div> </div>
</div> </div>

2
frontend/app/features/schemas/pages/schemas/schema-form.component.ts

@ -37,7 +37,7 @@ export class SchemaFormComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
this.createForm.load({ ...this.import, name: '' }); this.createForm.load({ type: 'Default', ...this.import, name: '' });
this.showImport = !!this.import; this.showImport = !!this.import;
} }

6
frontend/app/shared/guards/schema-must-exist-published.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { SchemaDetailsDto, SchemasState } from '@app/shared/internal'; import { SchemaDto, SchemasState } from '@app/shared/internal';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { IMock, It, Mock, Times } from 'typemoq'; import { IMock, It, Mock, Times } from 'typemoq';
import { SchemaMustExistPublishedGuard } from './schema-must-exist-published.guard'; import { SchemaMustExistPublishedGuard } from './schema-must-exist-published.guard';
@ -30,7 +30,7 @@ describe('SchemaMustExistPublishedGuard', () => {
it('should load schema and return true if found', () => { it('should load schema and return true if found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => of(<SchemaDetailsDto>{ isPublished: true })); .returns(() => of(<SchemaDto>{ isPublished: true }));
let result: boolean; let result: boolean;
@ -45,7 +45,7 @@ describe('SchemaMustExistPublishedGuard', () => {
it('should load schema and return false if not found', () => { it('should load schema and return false if not found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => of(<SchemaDetailsDto>{ isPublished: false })); .returns(() => of(<SchemaDto>{ isPublished: false }));
let result: boolean; let result: boolean;

4
frontend/app/shared/guards/schema-must-exist.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { SchemaDetailsDto, SchemasState } from '@app/shared/internal'; import { SchemaDto, SchemasState } from '@app/shared/internal';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
import { SchemaMustExistGuard } from './schema-must-exist.guard'; import { SchemaMustExistGuard } from './schema-must-exist.guard';
@ -30,7 +30,7 @@ describe('SchemaMustExistGuard', () => {
it('should load schema and return true if found', () => { it('should load schema and return true if found', () => {
schemasState.setup(x => x.select('123')) schemasState.setup(x => x.select('123'))
.returns(() => of(<SchemaDetailsDto>{})); .returns(() => of(<SchemaDto>{}));
let result: boolean; let result: boolean;

8
frontend/app/shared/guards/schema-must-not-be-singleton.guard.spec.ts

@ -6,7 +6,7 @@
*/ */
import { Router, RouterStateSnapshot, UrlSegment } from '@angular/router'; import { Router, RouterStateSnapshot, UrlSegment } from '@angular/router';
import { SchemaDetailsDto, SchemasState } from '@app/shared/internal'; import { SchemaDto, SchemasState } from '@app/shared/internal';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { IMock, It, Mock, Times } from 'typemoq'; import { IMock, It, Mock, Times } from 'typemoq';
import { SchemaMustNotBeSingletonGuard } from './schema-must-not-be-singleton.guard'; import { SchemaMustNotBeSingletonGuard } from './schema-must-not-be-singleton.guard';
@ -37,7 +37,7 @@ describe('SchemaMustNotBeSingletonGuard', () => {
const state: RouterStateSnapshot = <any>{ url: 'schemas/name/' }; const state: RouterStateSnapshot = <any>{ url: 'schemas/name/' };
schemasState.setup(x => x.selectedSchema) schemasState.setup(x => x.selectedSchema)
.returns(() => of(<SchemaDetailsDto>{ id: '123', isSingleton: false })); .returns(() => of(<SchemaDto>{ id: '123', isSingleton: false }));
let result: boolean; let result: boolean;
@ -54,7 +54,7 @@ describe('SchemaMustNotBeSingletonGuard', () => {
const state: RouterStateSnapshot = <any>{ url: 'schemas/name/' }; const state: RouterStateSnapshot = <any>{ url: 'schemas/name/' };
schemasState.setup(x => x.selectedSchema) schemasState.setup(x => x.selectedSchema)
.returns(() => of(<SchemaDetailsDto>{ id: '123', isSingleton: true })); .returns(() => of(<SchemaDto>{ id: '123', isSingleton: true }));
let result: boolean; let result: boolean;
@ -71,7 +71,7 @@ describe('SchemaMustNotBeSingletonGuard', () => {
const state: RouterStateSnapshot = <any>{ url: 'schemas/name/new/' }; const state: RouterStateSnapshot = <any>{ url: 'schemas/name/new/' };
schemasState.setup(x => x.selectedSchema) schemasState.setup(x => x.selectedSchema)
.returns(() => of(<SchemaDetailsDto>{ id: '123', isSingleton: true })); .returns(() => of(<SchemaDto>{ id: '123', isSingleton: true }));
let result: boolean; let result: boolean;

16
frontend/app/shared/services/apps.service.spec.ts

@ -283,18 +283,20 @@ describe('AppsService', () => {
return { return {
id: `id${id}`, id: `id${id}`,
created: `${id % 1000 + 2000}-12-12T10:10:00Z`,
createdBy: `creator${id}`,
lastModified: `${id % 1000 + 2000}-11-11T10:10:00Z`,
lastModifiedBy: `modifier${id}`,
version: key,
name: `app-name${key}`, name: `app-name${key}`,
label: `app-label${key}`, label: `app-label${key}`,
description: `app-description${key}`, description: `app-description${key}`,
permissions: ['Owner'], permissions: ['Owner'],
created: `${id % 1000 + 2000}-12-12T10:10:00Z`,
lastModified: `${id % 1000 + 2000}-11-11T10:10:00Z`,
canAccessApi: id % 2 === 0, canAccessApi: id % 2 === 0,
canAccessContent: id % 2 === 0, canAccessContent: id % 2 === 0,
planName: 'Free', planName: 'Free',
planUpgrade: 'Basic', planUpgrade: 'Basic',
roleProperties: createProperties(id), roleProperties: createProperties(id),
version: key,
_links: { _links: {
update: { method: 'PUT', href: `apps/${id}` } update: { method: 'PUT', href: `apps/${id}` }
} }
@ -333,17 +335,17 @@ export function createApp(id: number, suffix = '') {
return new AppDto(links, return new AppDto(links,
`id${id}`, `id${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`,
new Version(key),
`app-name${key}`, `app-name${key}`,
`app-label${key}`, `app-label${key}`,
`app-description${key}`, `app-description${key}`,
['Owner'], ['Owner'],
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`),
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`),
id % 2 === 0, id % 2 === 0,
id % 2 === 0, id % 2 === 0,
'Free', 'Basic', 'Free', 'Basic',
createProperties(id), createProperties(id));
new Version(key));
} }
export function createAppSettings(id: number, suffix = '') { export function createAppSettings(id: number, suffix = '') {

18
frontend/app/shared/services/apps.service.ts

@ -36,18 +36,20 @@ export class AppDto {
constructor(links: ResourceLinks, constructor(links: ResourceLinks,
public readonly id: string, public readonly id: string,
public readonly created: DateTime,
public readonly createdBy: string,
public readonly lastModified: DateTime,
public readonly lastModifiedBy: string,
public readonly version: Version,
public readonly name: string, public readonly name: string,
public readonly label: string | undefined, public readonly label: string | undefined,
public readonly description: string | undefined, public readonly description: string | undefined,
public readonly permissions: ReadonlyArray<string>, public readonly permissions: ReadonlyArray<string>,
public readonly created: DateTime,
public readonly lastModified: DateTime,
public readonly canAccessApi: boolean, public readonly canAccessApi: boolean,
public readonly canAccessContent: boolean, public readonly canAccessContent: boolean,
public readonly planName: string | undefined, public readonly planName: string | undefined,
public readonly planUpgrade: string | undefined, public readonly planUpgrade: string | undefined,
public readonly roleProperties: {}, public readonly roleProperties: {}
public readonly version: Version
) { ) {
this._links = links; this._links = links;
@ -283,18 +285,18 @@ export class AppsService {
function parseApp(response: any) { function parseApp(response: any) {
return new AppDto(response._links, return new AppDto(response._links,
response.id, response.id,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()),
response.name, response.name,
response.label, response.label,
response.description, response.description,
response.permissions, response.permissions,
DateTime.parseISO(response.created),
DateTime.parseISO(response.lastModified),
response.canAccessApi, response.canAccessApi,
response.canAccessContent, response.canAccessContent,
response.planName, response.planName,
response.planUpgrade, response.planUpgrade,
response.roleProperties, response.roleProperties);
new Version(response.version.toString()));
} }
function parseAppSettings(response: any) { function parseAppSettings(response: any) {

4
frontend/app/shared/services/assets.service.spec.ts

@ -528,6 +528,7 @@ export function createAsset(id: number, tags?: ReadonlyArray<string>, suffix = '
`id${id}`, `id${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`, DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`, DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`,
new Version(key),
`My Name${key}.png`, `My Name${key}.png`,
`My Hash${key}`, `My Hash${key}`,
'png', 'png',
@ -546,8 +547,7 @@ export function createAsset(id: number, tags?: ReadonlyArray<string>, suffix = '
tags || [ tags || [
'tag1', 'tag1',
'tag2' 'tag2'
], ]);
new Version(key));
} }
export function createAssetFolder(id: number, suffix = '', parentId?: string) { export function createAssetFolder(id: number, suffix = '', parentId?: string) {

8
frontend/app/shared/services/assets.service.ts

@ -59,6 +59,7 @@ export class AssetDto {
public readonly createdBy: string, public readonly createdBy: string,
public readonly lastModified: DateTime, public readonly lastModified: DateTime,
public readonly lastModifiedBy: string, public readonly lastModifiedBy: string,
public readonly version: Version,
public readonly fileName: string, public readonly fileName: string,
public readonly fileHash: string, public readonly fileHash: string,
public readonly fileType: string, public readonly fileType: string,
@ -71,8 +72,7 @@ export class AssetDto {
public readonly metadataText: string, public readonly metadataText: string,
public readonly metadata: any, public readonly metadata: any,
public readonly slug: string, public readonly slug: string,
public readonly tags: ReadonlyArray<string>, public readonly tags: ReadonlyArray<string>
public readonly version: Version
) { ) {
this.canPreview = this.canPreview =
(this.mimeType !== MIME_TIFF && this.type === 'Image') || (this.mimeType !== MIME_TIFF && this.type === 'Image') ||
@ -417,6 +417,7 @@ function parseAsset(response: any) {
response.id, response.id,
DateTime.parseISO(response.created), response.createdBy, DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy, DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()),
response.fileName, response.fileName,
response.fileHash, response.fileHash,
response.fileType, response.fileType,
@ -429,8 +430,7 @@ function parseAsset(response: any) {
response.metadataText, response.metadataText,
response.metadata, response.metadata,
response.slug, response.slug,
response.tags || [], response.tags || []);
new Version(response.version.toString()));
} }
function parseAssetFolder(response: any) { function parseAssetFolder(response: any) {

8
frontend/app/shared/services/contents.service.spec.ts

@ -438,17 +438,17 @@ export function createContent(id: number, suffix = '') {
return new ContentDto(links, return new ContentDto(links,
`id${id}`, `id${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`,
new Version(key),
`Status${key}`, `Status${key}`,
'black', 'black',
`NewStatus${key}`, `NewStatus${key}`,
'black', 'black',
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`,
new ScheduleDto('Draft', `Scheduler${id}`, 'red', DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`)), new ScheduleDto('Draft', `Scheduler${id}`, 'red', DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`)),
{}, {},
'my-schema', 'my-schema',
'MySchema', 'MySchema',
{}, {},
[], []);
new Version(key));
} }

20
frontend/app/shared/services/contents.service.ts

@ -60,21 +60,21 @@ export class ContentDto {
constructor(links: ResourceLinks, constructor(links: ResourceLinks,
public readonly id: string, public readonly id: string,
public readonly status: string,
public readonly statusColor: string,
public readonly newStatus: string | undefined,
public readonly newStatusColor: string | undefined,
public readonly created: DateTime, public readonly created: DateTime,
public readonly createdBy: string, public readonly createdBy: string,
public readonly lastModified: DateTime, public readonly lastModified: DateTime,
public readonly lastModifiedBy: string, public readonly lastModifiedBy: string,
public readonly version: Version,
public readonly status: string,
public readonly statusColor: string,
public readonly newStatus: string | undefined,
public readonly newStatusColor: string | undefined,
public readonly scheduleJob: ScheduleDto | null, public readonly scheduleJob: ScheduleDto | null,
public readonly data: ContentData, public readonly data: ContentData,
public readonly schemaName: string, public readonly schemaName: string,
public readonly schemaDisplayName: string, public readonly schemaDisplayName: string,
public readonly referenceData: ContentReferences, public readonly referenceData: ContentReferences,
public readonly referenceFields: ReadonlyArray<RootFieldDto>, public readonly referenceFields: ReadonlyArray<RootFieldDto>
public readonly version: Version
) { ) {
this._links = links; this._links = links;
@ -389,19 +389,19 @@ function buildQuery(q?: ContentQueryDto) {
function parseContent(response: any) { function parseContent(response: any) {
return new ContentDto(response._links, return new ContentDto(response._links,
response.id, response.id,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()),
response.status, response.status,
response.statusColor, response.statusColor,
response.newStatus, response.newStatus,
response.newStatusColor, response.newStatusColor,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
parseScheduleJob(response.scheduleJob), parseScheduleJob(response.scheduleJob),
response.data, response.data,
response.schemaName, response.schemaName,
response.schemaDisplayName, response.schemaDisplayName,
response.referenceData, response.referenceData,
response.referenceFields.map(parseField), response.referenceFields.map(parseField));
new Version(response.version.toString()));
} }
function parseScheduleJob(response: any) { function parseScheduleJob(response: any) {

176
frontend/app/shared/services/schemas.service.spec.ts

@ -7,7 +7,7 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing'; import { inject, TestBed } from '@angular/core/testing';
import { AnalyticsService, ApiUrlConfig, createProperties, DateTime, FieldRule, NestedFieldDto, Resource, ResourceLinks, RootFieldDto, SchemaDetailsDto, SchemaDto, SchemaPropertiesDto, SchemasDto, SchemasService, Version } from '@app/shared/internal'; import { AnalyticsService, ApiUrlConfig, createProperties, DateTime, FieldRule, NestedFieldDto, Resource, ResourceLinks, RootFieldDto, SchemaDto, SchemaPropertiesDto, SchemasDto, SchemasService, Version } from '@app/shared/internal';
describe('SchemasService', () => { describe('SchemasService', () => {
const version = new Version('1'); const version = new Version('1');
@ -74,7 +74,7 @@ describe('SchemasService', () => {
it('should make get request to get schema', it('should make get request to get schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.getSchema('my-app', 'my-schema').subscribe(result => { schemasService.getSchema('my-app', 'my-schema').subscribe(result => {
schema = result; schema = result;
@ -85,9 +85,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('GET'); expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull(); expect(req.request.headers.get('If-Match')).toBeNull();
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make post request to create schema', it('should make post request to create schema',
@ -95,7 +95,7 @@ describe('SchemasService', () => {
const dto = { name: 'name' }; const dto = { name: 'name' };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.postSchema('my-app', dto).subscribe(result => { schemasService.postSchema('my-app', dto).subscribe(result => {
schema = result; schema = result;
@ -106,9 +106,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('POST'); expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBeNull(); expect(req.request.headers.get('If-Match')).toBeNull();
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update schema', it('should make put request to update schema',
@ -122,7 +122,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putSchema('my-app', resource, dto, version).subscribe(result => { schemasService.putSchema('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -133,9 +133,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update schema scripts', it('should make put request to update schema scripts',
@ -149,7 +149,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putScripts('my-app', resource, dto, version).subscribe(result => { schemasService.putScripts('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -160,9 +160,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update field rules', it('should make put request to update field rules',
@ -176,7 +176,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putFieldRules('my-app', resource, dto, version).subscribe(result => { schemasService.putFieldRules('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -187,9 +187,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to synchronize schema', it('should make put request to synchronize schema',
@ -203,7 +203,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putSchemaSync('my-app', resource, dto, version).subscribe(result => { schemasService.putSchemaSync('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -214,9 +214,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update category', it('should make put request to update category',
@ -230,7 +230,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putCategory('my-app', resource, dto, version).subscribe(result => { schemasService.putCategory('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -241,9 +241,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update preview urls', it('should make put request to update preview urls',
@ -257,7 +257,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putPreviewUrls('my-app', resource, dto, version).subscribe(result => { schemasService.putPreviewUrls('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -268,9 +268,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make post request to add field', it('should make post request to add field',
@ -284,7 +284,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.postField('my-app', resource, dto, version).subscribe(result => { schemasService.postField('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -295,9 +295,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('POST'); expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to publish schema', it('should make put request to publish schema',
@ -309,7 +309,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.publishSchema('my-app', resource, version).subscribe(result => { schemasService.publishSchema('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -320,9 +320,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to unpublish schema', it('should make put request to unpublish schema',
@ -334,7 +334,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.unpublishSchema('my-app', resource, version).subscribe(result => { schemasService.unpublishSchema('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -345,9 +345,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update field', it('should make put request to update field',
@ -361,7 +361,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putField('my-app', resource, dto, version).subscribe(result => { schemasService.putField('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -372,9 +372,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update ui fields', it('should make put request to update ui fields',
@ -388,7 +388,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putUIFields('my-app', resource, dto, version).subscribe(result => { schemasService.putUIFields('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -399,9 +399,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to update field ordering', it('should make put request to update field ordering',
@ -415,7 +415,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.putFieldOrdering('my-app', resource, dto, version).subscribe(result => { schemasService.putFieldOrdering('my-app', resource, dto, version).subscribe(result => {
schema = result; schema = result;
@ -426,9 +426,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to lock field', it('should make put request to lock field',
@ -440,7 +440,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.lockField('my-app', resource, version).subscribe(result => { schemasService.lockField('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -451,9 +451,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to enable field', it('should make put request to enable field',
@ -465,7 +465,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.enableField('my-app', resource, version).subscribe(result => { schemasService.enableField('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -476,9 +476,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to disable field', it('should make put request to disable field',
@ -490,7 +490,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.disableField('my-app', resource, version).subscribe(result => { schemasService.disableField('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -501,9 +501,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to show field', it('should make put request to show field',
@ -515,7 +515,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.showField('my-app', resource, version).subscribe(result => { schemasService.showField('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -526,9 +526,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make put request to hide field', it('should make put request to hide field',
@ -540,7 +540,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.hideField('my-app', resource, version).subscribe(result => { schemasService.hideField('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -551,9 +551,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make delete request to delete field', it('should make delete request to delete field',
@ -565,7 +565,7 @@ describe('SchemasService', () => {
} }
}; };
let schema: SchemaDetailsDto; let schema: SchemaDto;
schemasService.deleteField('my-app', resource, version).subscribe(result => { schemasService.deleteField('my-app', resource, version).subscribe(result => {
schema = result; schema = result;
@ -576,9 +576,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('DELETE'); expect(req.request.method).toEqual('DELETE');
expect(req.request.headers.get('If-Match')).toBe(version.value); expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush(schemaDetailsResponse(12)); req.flush(schemaResponse(12));
expect(schema!).toEqual(createSchemaDetails(12)); expect(schema!).toEqual(createSchema(12));
})); }));
it('should make delete request to delete schema', it('should make delete request to delete schema',
@ -621,36 +621,15 @@ describe('SchemasService', () => {
return { return {
id: `id${id}`, id: `id${id}`,
name: `schema-name${key}`,
category: `schema-category${key}`,
isSingleton: id % 2 === 0,
isPublished: id % 3 === 0,
created: `${id % 1000 + 2000}-12-12T10:10:00Z`, created: `${id % 1000 + 2000}-12-12T10:10:00Z`,
createdBy: `creator${id}`, createdBy: `creator${id}`,
lastModified: `${id % 1000 + 2000}-11-11T10:10:00Z`, lastModified: `${id % 1000 + 2000}-11-11T10:10:00Z`,
lastModifiedBy: `modifier${id}`, lastModifiedBy: `modifier${id}`,
properties: schemaPropertiesResponse(id, suffix),
version: key, version: key,
_links: {
update: { method: 'PUT', href: `/schemas/${id}` }
}
};
}
function schemaDetailsResponse(id: number, suffix = '') {
const key = `${id}${suffix}`;
return {
id: `id${id}`,
name: `schema-name${key}`, name: `schema-name${key}`,
category: `schema-category${key}`, category: `schema-category${key}`,
isSingleton: id % 2 === 0, type: id % 2 === 0 ? 'Default' : 'Singleton',
isPublished: id % 3 === 0, isPublished: id % 3 === 0,
created: `${id % 1000 + 2000}-12-12T10:10:00Z`,
createdBy: `creator${id}`,
lastModified: `${id % 1000 + 2000}-11-11T10:10:00Z`,
lastModifiedBy: `modifier${id}`,
version: key,
properties: schemaPropertiesResponse(id, suffix), properties: schemaPropertiesResponse(id, suffix),
previewUrls: { previewUrls: {
Default: 'url' Default: 'url'
@ -846,33 +825,14 @@ export function createSchema(id: number, suffix = '') {
return new SchemaDto(links, return new SchemaDto(links,
`id${id}`, `id${id}`,
`schema-name${key}`,
`schema-category${key}`,
createSchemaProperties(id, suffix),
id % 2 === 0,
id % 3 === 0,
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`, DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`,
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`, DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`,
new Version(key)); new Version(key),
}
export function createSchemaDetails(id: number, suffix = '') {
const links: ResourceLinks = {
update: { method: 'PUT', href: `/schemas/${id}` }
};
const key = `${id}${suffix}`;
return new SchemaDetailsDto(links,
`id${id}`,
`schema-name${key}`, `schema-name${key}`,
`schema-category${key}`, `schema-category${key}`,
createSchemaProperties(id, suffix), id % 2 === 0 ? 'Default' : 'Singleton',
id % 2 === 0,
id % 3 === 0, id % 3 === 0,
DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`, createSchemaProperties(id, suffix),
DateTime.parseISO(`${id % 1000 + 2000}-11-11T10:10:00Z`), `modifier${id}`,
new Version(key),
[ [
new RootFieldDto({}, 11, 'field11', createProperties('Array'), 'language', true, true, true, [ new RootFieldDto({}, 11, 'field11', createProperties('Array'), 'language', true, true, true, [
new NestedFieldDto({}, 101, 'field101', createProperties('String'), 11, true, true, true), new NestedFieldDto({}, 101, 'field101', createProperties('String'), 11, true, true, true),
@ -893,14 +853,14 @@ export function createSchemaDetails(id: number, suffix = '') {
[{ [{
field: 'field1', action: 'Hide', condition: 'a === 2' field: 'field1', action: 'Hide', condition: 'a === 2'
}], }],
{
Default: 'url'
},
{ {
query: '<script-query>', query: '<script-query>',
create: '<script-create>', create: '<script-create>',
change: '<script-change>', change: '<script-change>',
delete: '<script-delete>', delete: '<script-delete>',
update: '<script-update>' update: '<script-update>'
},
{
Default: 'url'
}); });
} }

169
frontend/app/shared/services/schemas.service.ts

@ -28,6 +28,8 @@ export const MetaFields = {
version: 'meta.version' version: 'meta.version'
}; };
export type SchemaType = 'Default' | 'Singleton';
export class SchemaDto { export class SchemaDto {
public readonly _links: ResourceLinks; public readonly _links: ResourceLinks;
@ -50,18 +52,32 @@ export class SchemaDto {
public readonly displayName: string; public readonly displayName: string;
public readonly contentFields: ReadonlyArray<RootFieldDto>;
public readonly defaultListFields: ReadonlyArray<TableField>;
public readonly defaultReferenceFields: ReadonlyArray<TableField>;
public readonly isSingleton: boolean;
public readonly isDefault: boolean;
constructor(links: ResourceLinks, constructor(links: ResourceLinks,
public readonly id: string, public readonly id: string,
public readonly name: string,
public readonly category: string,
public readonly properties: SchemaPropertiesDto,
public readonly isSingleton: boolean,
public readonly isPublished: boolean,
public readonly created: DateTime, public readonly created: DateTime,
public readonly createdBy: string, public readonly createdBy: string,
public readonly lastModified: DateTime, public readonly lastModified: DateTime,
public readonly lastModifiedBy: string, public readonly lastModifiedBy: string,
public readonly version: Version public readonly version: Version,
public readonly name: string,
public readonly category: string,
public readonly type: SchemaType,
public readonly isPublished: boolean,
public readonly properties: SchemaPropertiesDto,
public readonly fields: ReadonlyArray<RootFieldDto> = [],
public readonly fieldsInLists: Tags = [],
public readonly fieldsInReferences: Tags = [],
public readonly fieldRules: ReadonlyArray<FieldRule> = [],
public readonly previewUrls = {},
public readonly scripts = {}
) { ) {
this._links = links; this._links = links;
@ -83,32 +99,9 @@ export class SchemaDto {
this.canUpdateRules = hasAnyLink(links, 'update/rules'); this.canUpdateRules = hasAnyLink(links, 'update/rules');
this.displayName = StringHelper.firstNonEmpty(this.properties.label, this.name); this.displayName = StringHelper.firstNonEmpty(this.properties.label, this.name);
}
}
export class SchemaDetailsDto extends SchemaDto {
public readonly contentFields: ReadonlyArray<RootFieldDto>;
public readonly defaultListFields: ReadonlyArray<TableField>; this.isDefault = type === 'Default';
public readonly defaultReferenceFields: ReadonlyArray<TableField>; this.isSingleton = type === 'Singleton';
constructor(links: ResourceLinks, id: string, name: string, category: string,
properties: SchemaPropertiesDto,
isSingleton: boolean,
isPublished: boolean,
created: DateTime,
createdBy: string,
lastModified: DateTime,
lastModifiedBy: string,
version: Version,
public readonly fields: ReadonlyArray<RootFieldDto> = [],
public readonly fieldsInLists: Tags = [],
public readonly fieldsInReferences: Tags = [],
public readonly fieldRules: ReadonlyArray<FieldRule> = [],
public readonly scripts = {},
public readonly previewUrls = {}
) {
super(links, id, name, category, properties, isSingleton, isPublished, created, createdBy, lastModified, lastModifiedBy, version);
if (fields) { if (fields) {
this.contentFields = fields.filter(x => x.properties.isContentField); this.contentFields = fields.filter(x => x.properties.isContentField);
@ -193,7 +186,7 @@ export class SchemaDetailsDto extends SchemaDto {
return copy; return copy;
}), }),
isPublished: this.isPublished type: this.type
}; };
return result; return result;
@ -337,7 +330,7 @@ export type UpdateUIFields =
Readonly<{ fieldsInLists?: Tags; fieldsInReferences?: Tags; }>; Readonly<{ fieldsInLists?: Tags; fieldsInReferences?: Tags; }>;
export type CreateSchemaDto = export type CreateSchemaDto =
Readonly<{ name: string; fields?: ReadonlyArray<RootFieldDto>; category?: string; isSingleton?: boolean; isPublished?: boolean; properties?: SchemaPropertiesDto; }>; Readonly<{ name: string; fields?: ReadonlyArray<RootFieldDto>; category?: string; type?: string; isPublished?: boolean; properties?: SchemaPropertiesDto; }>;
export type UpdateSchemaCategoryDto = export type UpdateSchemaCategoryDto =
Readonly<{ name?: string; }>; Readonly<{ name?: string; }>;
@ -370,22 +363,22 @@ export class SchemasService {
pretifyError('i18n:schemas.loadFailed')); pretifyError('i18n:schemas.loadFailed'));
} }
public getSchema(appName: string, name: string): Observable<SchemaDetailsDto> { public getSchema(appName: string, name: string): Observable<SchemaDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${name}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${name}`);
return HTTP.getVersioned(this.http, url).pipe( return HTTP.getVersioned(this.http, url).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
pretifyError('i18n:schemas.loadSchemaFailed')); pretifyError('i18n:schemas.loadSchemaFailed'));
} }
public postSchema(appName: string, dto: CreateSchemaDto): Observable<SchemaDetailsDto> { public postSchema(appName: string, dto: CreateSchemaDto): Observable<SchemaDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`);
return HTTP.postVersioned(this.http, url, dto).pipe( return HTTP.postVersioned(this.http, url, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'Created', appName); this.analytics.trackEvent('Schema', 'Created', appName);
@ -393,14 +386,14 @@ export class SchemasService {
pretifyError('i18n:schemas.createFailed')); pretifyError('i18n:schemas.createFailed'));
} }
public putScripts(appName: string, resource: Resource, dto: {}, version: Version): Observable<SchemaDetailsDto> { public putScripts(appName: string, resource: Resource, dto: {}, version: Version): Observable<SchemaDto> {
const link = resource._links['update/scripts']; const link = resource._links['update/scripts'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'ScriptsConfigured', appName); this.analytics.trackEvent('Schema', 'ScriptsConfigured', appName);
@ -408,14 +401,14 @@ export class SchemasService {
pretifyError('i18n:schemas.updateScriptsFailed')); pretifyError('i18n:schemas.updateScriptsFailed'));
} }
public putFieldRules(appName: string, resource: Resource, dto: ReadonlyArray<FieldRule>, version: Version): Observable<SchemaDetailsDto> { public putFieldRules(appName: string, resource: Resource, dto: ReadonlyArray<FieldRule>, version: Version): Observable<SchemaDto> {
const link = resource._links['update/rules']; const link = resource._links['update/rules'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, { fieldRules: dto }).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, { fieldRules: dto }).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'RulesConfigured', appName); this.analytics.trackEvent('Schema', 'RulesConfigured', appName);
@ -423,14 +416,14 @@ export class SchemasService {
pretifyError('i18n:schemas.updateRulesFailed')); pretifyError('i18n:schemas.updateRulesFailed'));
} }
public putSchemaSync(appName: string, resource: Resource, dto: SynchronizeSchemaDto, version: Version): Observable<SchemaDetailsDto> { public putSchemaSync(appName: string, resource: Resource, dto: SynchronizeSchemaDto, version: Version): Observable<SchemaDto> {
const link = resource._links['update/sync']; const link = resource._links['update/sync'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'Updated', appName); this.analytics.trackEvent('Schema', 'Updated', appName);
@ -438,14 +431,14 @@ export class SchemasService {
pretifyError('i18n:schemas.synchronizeFailed')); pretifyError('i18n:schemas.synchronizeFailed'));
} }
public putSchema(appName: string, resource: Resource, dto: UpdateSchemaDto, version: Version): Observable<SchemaDetailsDto> { public putSchema(appName: string, resource: Resource, dto: UpdateSchemaDto, version: Version): Observable<SchemaDto> {
const link = resource._links['update']; const link = resource._links['update'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'Updated', appName); this.analytics.trackEvent('Schema', 'Updated', appName);
@ -453,14 +446,14 @@ export class SchemasService {
pretifyError('i18n:schemas.updateFailed')); pretifyError('i18n:schemas.updateFailed'));
} }
public putCategory(appName: string, resource: Resource, dto: UpdateSchemaCategoryDto, version: Version): Observable<SchemaDetailsDto> { public putCategory(appName: string, resource: Resource, dto: UpdateSchemaCategoryDto, version: Version): Observable<SchemaDto> {
const link = resource._links['update/category']; const link = resource._links['update/category'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'CategoryChanged', appName); this.analytics.trackEvent('Schema', 'CategoryChanged', appName);
@ -468,14 +461,14 @@ export class SchemasService {
pretifyError('i18n:schemas.changeCategoryFailed')); pretifyError('i18n:schemas.changeCategoryFailed'));
} }
public putPreviewUrls(appName: string, resource: Resource, dto: {}, version: Version): Observable<SchemaDetailsDto> { public putPreviewUrls(appName: string, resource: Resource, dto: {}, version: Version): Observable<SchemaDto> {
const link = resource._links['update/urls']; const link = resource._links['update/urls'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'PreviewUrlsConfigured', appName); this.analytics.trackEvent('Schema', 'PreviewUrlsConfigured', appName);
@ -483,14 +476,14 @@ export class SchemasService {
pretifyError('i18n:schemas.updatePreviewUrlsFailed')); pretifyError('i18n:schemas.updatePreviewUrlsFailed'));
} }
public publishSchema(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public publishSchema(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['publish']; const link = resource._links['publish'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'Published', appName); this.analytics.trackEvent('Schema', 'Published', appName);
@ -498,14 +491,14 @@ export class SchemasService {
pretifyError('i18n:schemas.publishFailed')); pretifyError('i18n:schemas.publishFailed'));
} }
public unpublishSchema(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public unpublishSchema(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['unpublish']; const link = resource._links['unpublish'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'Unpublished', appName); this.analytics.trackEvent('Schema', 'Unpublished', appName);
@ -513,14 +506,14 @@ export class SchemasService {
pretifyError('i18n:schemas.unpublishFailed')); pretifyError('i18n:schemas.unpublishFailed'));
} }
public postField(appName: string, resource: Resource, dto: AddFieldDto, version: Version): Observable<SchemaDetailsDto> { public postField(appName: string, resource: Resource, dto: AddFieldDto, version: Version): Observable<SchemaDto> {
const link = resource._links['fields/add']; const link = resource._links['fields/add'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldCreated', appName); this.analytics.trackEvent('Schema', 'FieldCreated', appName);
@ -528,14 +521,14 @@ export class SchemasService {
pretifyError('i18n:schemas.addFieldFailed')); pretifyError('i18n:schemas.addFieldFailed'));
} }
public putUIFields(appName: string, resource: Resource, dto: UpdateUIFields, version: Version): Observable<SchemaDetailsDto> { public putUIFields(appName: string, resource: Resource, dto: UpdateUIFields, version: Version): Observable<SchemaDto> {
const link = resource._links['fields/ui']; const link = resource._links['fields/ui'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'UIFieldsConfigured', appName); this.analytics.trackEvent('Schema', 'UIFieldsConfigured', appName);
@ -543,14 +536,14 @@ export class SchemasService {
pretifyError('i18n:schemas.updateUIFieldsFailed')); pretifyError('i18n:schemas.updateUIFieldsFailed'));
} }
public putFieldOrdering(appName: string, resource: Resource, dto: ReadonlyArray<number>, version: Version): Observable<SchemaDetailsDto> { public putFieldOrdering(appName: string, resource: Resource, dto: ReadonlyArray<number>, version: Version): Observable<SchemaDto> {
const link = resource._links['fields/order']; const link = resource._links['fields/order'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, { fieldIds: dto }).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, { fieldIds: dto }).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldsReordered', appName); this.analytics.trackEvent('Schema', 'FieldsReordered', appName);
@ -558,14 +551,14 @@ export class SchemasService {
pretifyError('i18n:schemas.reorderFieldsFailed')); pretifyError('i18n:schemas.reorderFieldsFailed'));
} }
public putField(appName: string, resource: Resource, dto: UpdateFieldDto, version: Version): Observable<SchemaDetailsDto> { public putField(appName: string, resource: Resource, dto: UpdateFieldDto, version: Version): Observable<SchemaDto> {
const link = resource._links['update']; const link = resource._links['update'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldUpdated', appName); this.analytics.trackEvent('Schema', 'FieldUpdated', appName);
@ -573,14 +566,14 @@ export class SchemasService {
pretifyError('i18n:schemas.updateFieldFailed')); pretifyError('i18n:schemas.updateFieldFailed'));
} }
public lockField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public lockField(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['lock']; const link = resource._links['lock'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldLocked', appName); this.analytics.trackEvent('Schema', 'FieldLocked', appName);
@ -588,14 +581,14 @@ export class SchemasService {
pretifyError('i18n:schemas.lockFieldFailed')); pretifyError('i18n:schemas.lockFieldFailed'));
} }
public enableField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public enableField(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['enable']; const link = resource._links['enable'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldEnabled', appName); this.analytics.trackEvent('Schema', 'FieldEnabled', appName);
@ -603,14 +596,14 @@ export class SchemasService {
pretifyError('i18n:schemas.enableFieldFailed')); pretifyError('i18n:schemas.enableFieldFailed'));
} }
public disableField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public disableField(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['disable']; const link = resource._links['disable'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldDisabled', appName); this.analytics.trackEvent('Schema', 'FieldDisabled', appName);
@ -618,14 +611,14 @@ export class SchemasService {
pretifyError('i18n:schemas.disableFieldFailed')); pretifyError('i18n:schemas.disableFieldFailed'));
} }
public showField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public showField(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['show']; const link = resource._links['show'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldShown', appName); this.analytics.trackEvent('Schema', 'FieldShown', appName);
@ -633,14 +626,14 @@ export class SchemasService {
pretifyError('i18n:schemas.showFieldFailed')); pretifyError('i18n:schemas.showFieldFailed'));
} }
public hideField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public hideField(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['hide']; const link = resource._links['hide'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldHidden', appName); this.analytics.trackEvent('Schema', 'FieldHidden', appName);
@ -648,14 +641,14 @@ export class SchemasService {
pretifyError('i18n:schemas.hideFieldFailed')); pretifyError('i18n:schemas.hideFieldFailed'));
} }
public deleteField(appName: string, resource: Resource, version: Version): Observable<SchemaDetailsDto> { public deleteField(appName: string, resource: Resource, version: Version): Observable<SchemaDto> {
const link = resource._links['delete']; const link = resource._links['delete'];
const url = this.apiUrl.buildUrl(link.href); const url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe( return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => { map(({ payload }) => {
return parseSchemaWithDetails(payload.body); return parseSchema(payload.body);
}), }),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'FieldDeleted', appName); this.analytics.trackEvent('Schema', 'FieldDeleted', appName);
@ -685,42 +678,32 @@ export class SchemasService {
function parseSchemas(response: any) { function parseSchemas(response: any) {
const raw: any[] = response.items; const raw: any[] = response.items;
const items = raw.map(item => const items = raw.map(parseSchema);
new SchemaDto(item._links,
item.id,
item.name,
item.category,
parseProperties(item.properties),
item.isSingleton,
item.isPublished,
DateTime.parseISO(item.created), item.createdBy,
DateTime.parseISO(item.lastModified), item.lastModifiedBy,
new Version(item.version.toString())));
const _links = response._links; const _links = response._links;
return { items, _links, canCreate: hasAnyLink(_links, 'create') }; return { items, _links, canCreate: hasAnyLink(_links, 'create') };
} }
function parseSchemaWithDetails(response: any) { function parseSchema(response: any) {
const fields = response.fields.map(parseField); const fields = response.fields.map(parseField);
return new SchemaDetailsDto(response._links, return new SchemaDto(response._links,
response.id, response.id,
response.name,
response.category,
parseProperties(response.properties),
response.isSingleton,
response.isPublished,
DateTime.parseISO(response.created), response.createdBy, DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy, DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()), new Version(response.version.toString()),
response.name,
response.category,
response.type,
response.isPublished,
parseProperties(response.properties),
fields, fields,
response.fieldsInLists, response.fieldsInLists,
response.fieldsInReferences, response.fieldsInReferences,
response.fieldRules, response.fieldRules,
response.scripts || {}, response.previewUrls || {},
response.previewUrls || {}); response.scripts || {});
} }
function parseProperties(response: any) { function parseProperties(response: any) {

9
frontend/app/shared/services/schemas.spec.ts

@ -12,9 +12,10 @@ import { TestValues } from './../state/_test-helpers';
const { const {
createField, createField,
createSchema} = TestValues; createSchema
} = TestValues;
describe('SchemaDetailsDto', () => { describe('SchemaDto', () => {
const field1 = createField({ properties: createProperties('Array'), id: 1 }); const field1 = createField({ properties: createProperties('Array'), id: 1 });
const field2 = createField({ properties: createProperties('Array'), id: 2 }); const field2 = createField({ properties: createProperties('Array'), id: 2 });
const field3 = createField({ properties: createProperties('Array'), id: 3 }); const field3 = createField({ properties: createProperties('Array'), id: 3 });
@ -28,13 +29,13 @@ describe('SchemaDetailsDto', () => {
it('should return name as display name if label is undefined', () => { it('should return name as display name if label is undefined', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto(undefined) }); const schema = createSchema({ properties: new SchemaPropertiesDto(undefined) });
expect(schema.displayName).toBe('schema1'); expect(schema.displayName).toBe('schema-name1');
}); });
it('should return name as display name label is empty', () => { it('should return name as display name label is empty', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto('') }); const schema = createSchema({ properties: new SchemaPropertiesDto('') });
expect(schema.displayName).toBe('schema1'); expect(schema.displayName).toBe('schema-name1');
}); });
it('should return configured fields as list fields if fields are declared', () => { it('should return configured fields as list fields if fields are declared', () => {

5
frontend/app/shared/services/users-provider.service.spec.ts

@ -7,6 +7,7 @@
import { AuthService, Profile, UserDto, UsersProviderService, UsersService } from '@app/shared/internal'; import { AuthService, Profile, UserDto, UsersProviderService, UsersService } from '@app/shared/internal';
import { of, throwError } from 'rxjs'; import { of, throwError } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators';
import { IMock, Mock, Times } from 'typemoq'; import { IMock, Mock, Times } from 'typemoq';
describe('UsersProviderService', () => { describe('UsersProviderService', () => {
@ -81,11 +82,11 @@ describe('UsersProviderService', () => {
.returns(() => new Profile(<any>{ profile: { sub: '123'}})); .returns(() => new Profile(<any>{ profile: { sub: '123'}}));
usersService.setup(x => x.getUser('123')) usersService.setup(x => x.getUser('123'))
.returns(() => throwError('NOT FOUND')).verifiable(Times.once()); .returns(() => throwError('Service Error')).verifiable(Times.once());
let resultingUser: UserDto; let resultingUser: UserDto;
usersProviderService.getUser('123').subscribe(result => { usersProviderService.getUser('123').pipe(onErrorResumeNext()).subscribe(result => {
resultingUser = result; resultingUser = result;
}).unsubscribe(); }).unsubscribe();

14
frontend/app/shared/state/_test-helpers.ts

@ -7,7 +7,7 @@
import { of } from 'rxjs'; import { of } from 'rxjs';
import { Mock } from 'typemoq'; import { Mock } from 'typemoq';
import { AppsState, AuthService, DateTime, FieldPropertiesDto, FieldRule, NestedFieldDto, RootFieldDto, SchemaDetailsDto, SchemaPropertiesDto, Version } from './../'; import { AppsState, AuthService, DateTime, FieldPropertiesDto, FieldRule, NestedFieldDto, RootFieldDto, SchemaDto, SchemaPropertiesDto, Version } from './../';
const app = 'my-app'; const app = 'my-app';
const creation = DateTime.today().addDays(-2); const creation = DateTime.today().addDays(-2);
@ -42,16 +42,18 @@ type SchemaValues = {
function createSchema({ properties, id, fields, fieldsInLists, fieldsInReferences, fieldRules }: SchemaValues = {}) { function createSchema({ properties, id, fields, fieldsInLists, fieldsInReferences, fieldRules }: SchemaValues = {}) {
id = id || 1; id = id || 1;
return new SchemaDetailsDto({}, return new SchemaDto({},
`schema${1}`, `schema${id}`,
`schema${1}`,
'category',
properties || new SchemaPropertiesDto(), false, true,
creation, creation,
creator, creator,
modified, modified,
modifier, modifier,
new Version('1'), new Version('1'),
`schema-name${id}`,
`schema-category${id}`,
'Default',
true,
properties || new SchemaPropertiesDto(),
fields, fields,
fieldsInLists || [], fieldsInLists || [],
fieldsInReferences || [], fieldsInReferences || [],

5
frontend/app/shared/state/apps.state.spec.ts

@ -7,6 +7,7 @@
import { AppDto, AppsService, AppsState, DialogService } from '@app/shared/internal'; import { AppDto, AppsService, AppsState, DialogService } from '@app/shared/internal';
import { of, throwError } from 'rxjs'; import { of, throwError } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators';
import { IMock, It, Mock, Times } from 'typemoq'; import { IMock, It, Mock, Times } from 'typemoq';
import { createApp, createAppSettings } from './../services/apps.service.spec'; import { createApp, createAppSettings } from './../services/apps.service.spec';
@ -107,9 +108,9 @@ describe('AppsState', () => {
let appSelected: AppDto; let appSelected: AppDto;
appsService.setup(x => x.getApp('unknown')) appsService.setup(x => x.getApp('unknown'))
.returns(() => throwError(new Error('404'))); .returns(() => throwError('Service Error'));
appsState.select('unknown').subscribe(x => { appsState.select('unknown').pipe(onErrorResumeNext()).subscribe(x => {
appSelected = x!; appSelected = x!;
}); });

4
frontend/app/shared/state/asset-uploader.state.spec.ts

@ -83,7 +83,7 @@ describe('AssetUploaderState', () => {
const file: File = <any>{ name: 'my-file' }; const file: File = <any>{ name: 'my-file' };
assetsService.setup(x => x.postAssetFile(app, file, undefined)) assetsService.setup(x => x.postAssetFile(app, file, undefined))
.returns(() => throwError('Error')).verifiable(); .returns(() => throwError('Service Error')).verifiable();
assetUploader.uploadFile(file).pipe(onErrorResumeNext()).subscribe(); assetUploader.uploadFile(file).pipe(onErrorResumeNext()).subscribe();
@ -148,7 +148,7 @@ describe('AssetUploaderState', () => {
const file: File = <any>{ name: 'my-file' }; const file: File = <any>{ name: 'my-file' };
assetsService.setup(x => x.putAssetFile(app, asset, file, asset.version)) assetsService.setup(x => x.putAssetFile(app, asset, file, asset.version))
.returns(() => throwError('Error')).verifiable(); .returns(() => throwError('Service Error')).verifiable();
assetUploader.uploadAsset(asset, file).pipe(onErrorResumeNext()).subscribe(); assetUploader.uploadAsset(asset, file).pipe(onErrorResumeNext()).subscribe();

4
frontend/app/shared/state/assets.state.spec.ts

@ -279,7 +279,7 @@ describe('AssetsState', () => {
const request = { parentId: 'newParent' }; const request = { parentId: 'newParent' };
assetsService.setup(x => x.putAssetItemParent(app, asset1, It.isValue(request), asset1.version)) assetsService.setup(x => x.putAssetItemParent(app, asset1, It.isValue(request), asset1.version))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
assetsState.moveAsset(asset1, request.parentId).pipe(onErrorResumeNext()).subscribe(); assetsState.moveAsset(asset1, request.parentId).pipe(onErrorResumeNext()).subscribe();
@ -318,7 +318,7 @@ describe('AssetsState', () => {
const request = { parentId: 'newParent' }; const request = { parentId: 'newParent' };
assetsService.setup(x => x.putAssetItemParent(app, assetFolder1, It.isValue(request), assetFolder1.version)) assetsService.setup(x => x.putAssetItemParent(app, assetFolder1, It.isValue(request), assetFolder1.version))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
assetsState.moveAssetFolder(assetFolder1, request.parentId).pipe(onErrorResumeNext()).subscribe(); assetsState.moveAssetFolder(assetFolder1, request.parentId).pipe(onErrorResumeNext()).subscribe();

6
frontend/app/shared/state/backups.state.spec.ts

@ -52,7 +52,7 @@ describe('BackupsState', () => {
it('should reset loading state if loading failed', () => { it('should reset loading state if loading failed', () => {
backupsService.setup(x => x.getBackups(app)) backupsService.setup(x => x.getBackups(app))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
backupsState.load().pipe(onErrorResumeNext()).subscribe(); backupsState.load().pipe(onErrorResumeNext()).subscribe();
@ -72,7 +72,7 @@ describe('BackupsState', () => {
it('should show notification on load error if silent is false', () => { it('should show notification on load error if silent is false', () => {
backupsService.setup(x => x.getBackups(app)) backupsService.setup(x => x.getBackups(app))
.returns(() => throwError({})); .returns(() => throwError('Service Error'));
backupsState.load(true, false).pipe(onErrorResumeNext()).subscribe(); backupsState.load(true, false).pipe(onErrorResumeNext()).subscribe();
@ -83,7 +83,7 @@ describe('BackupsState', () => {
it('should not show notification on load error if silent is true', () => { it('should not show notification on load error if silent is true', () => {
backupsService.setup(x => x.getBackups(app)) backupsService.setup(x => x.getBackups(app))
.returns(() => throwError({})); .returns(() => throwError('Service Error'));
backupsState.load(true, true).pipe(onErrorResumeNext()).subscribe(); backupsState.load(true, true).pipe(onErrorResumeNext()).subscribe();

2
frontend/app/shared/state/clients.state.spec.ts

@ -54,7 +54,7 @@ describe('ClientsState', () => {
it('should reset loading state if loading failed', () => { it('should reset loading state if loading failed', () => {
clientsService.setup(x => x.getClients(app)) clientsService.setup(x => x.getClients(app))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
clientsState.load().pipe(onErrorResumeNext()).subscribe(); clientsState.load().pipe(onErrorResumeNext()).subscribe();

4
frontend/app/shared/state/contents.forms.ts

@ -13,7 +13,7 @@ import { BehaviorSubject, Observable } from 'rxjs';
import { debounceTime, onErrorResumeNext } from 'rxjs/operators'; import { debounceTime, onErrorResumeNext } from 'rxjs/operators';
import { AppLanguageDto } from './../services/app-languages.service'; import { AppLanguageDto } from './../services/app-languages.service';
import { LanguageDto } from './../services/languages.service'; import { LanguageDto } from './../services/languages.service';
import { NestedFieldDto, RootFieldDto, SchemaDetailsDto, TableField } from './../services/schemas.service'; import { NestedFieldDto, RootFieldDto, SchemaDto, TableField } from './../services/schemas.service';
import { fieldInvariant } from './../services/schemas.types'; import { fieldInvariant } from './../services/schemas.types';
import { AbstractContentForm, AbstractContentFormState, CompiledRule, FieldSection, PartitionConfig } from './contents.forms-helpers'; import { AbstractContentForm, AbstractContentFormState, CompiledRule, FieldSection, PartitionConfig } from './contents.forms-helpers';
import { FieldDefaultValue, FieldsValidators } from './contents.forms.visitors'; import { FieldDefaultValue, FieldsValidators } from './contents.forms.visitors';
@ -91,7 +91,7 @@ export class EditContentForm extends Form<FormGroup, any> {
return this.valueChange$.value; return this.valueChange$.value;
} }
constructor(languages: ReadonlyArray<AppLanguageDto>, schema: SchemaDetailsDto, constructor(languages: ReadonlyArray<AppLanguageDto>, schema: SchemaDto,
private context: any, debounce = 100 private context: any, debounce = 100
) { ) {
super(new FormGroup({})); super(new FormGroup({}));

109
frontend/app/shared/state/contents.forms.visitors.spec.ts

@ -8,120 +8,15 @@
// tslint:disable: max-line-length // tslint:disable: max-line-length
import { DateHelper } from '@app/framework'; import { DateHelper } from '@app/framework';
import { createProperties, DateTime, FieldDefaultValue, FieldFormatter, FieldsValidators, HtmlValue, MetaFields, SchemaPropertiesDto } from '@app/shared/internal'; import { createProperties, DateTime, FieldDefaultValue, FieldFormatter, FieldsValidators, HtmlValue } from '@app/shared/internal';
import { TestValues } from './_test-helpers'; import { TestValues } from './_test-helpers';
const { const {
createField, createField
createSchema
} = TestValues; } = TestValues;
const now = DateTime.parseISO('2017-10-12T16:30:10Z'); const now = DateTime.parseISO('2017-10-12T16:30:10Z');
describe('SchemaDetailsDto', () => {
const field1 = createField({ properties: createProperties('Array'), id: 1 });
const field2 = createField({ properties: createProperties('Array'), id: 2 });
const field3 = createField({ properties: createProperties('Array'), id: 3 });
it('should return label as display name', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto('Label') });
expect(schema.displayName).toBe('Label');
});
it('should return name as display name if label is undefined', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto(undefined) });
expect(schema.displayName).toBe('schema1');
});
it('should return name as display name label is empty', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto('') });
expect(schema.displayName).toBe('schema1');
});
it('should return configured fields as list fields if fields are declared', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto(''), fields: [field1, field2, field3], fieldsInLists: ['field1', 'field3'] });
expect(schema.defaultListFields).toEqual([field1, field3]);
});
it('should return first fields as list fields if no field is declared', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto(''), fields: [field1, field2, field3] });
expect(schema.defaultListFields).toEqual([MetaFields.lastModifiedByAvatar, field1, MetaFields.statusColor, MetaFields.lastModified]);
});
it('should return preset with empty content field as list fields if fields is empty', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto() });
expect(schema.defaultListFields).toEqual([MetaFields.lastModifiedByAvatar, '', MetaFields.statusColor, MetaFields.lastModified]);
});
it('should return configured fields as references fields if fields are declared', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto(''), fields: [field1, field2, field3], fieldsInReferences: ['field1', 'field3'] });
expect(schema.defaultReferenceFields).toEqual([field1, field3]);
});
it('should return first field as reference fields if no field is declared', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto(''), fields: [field1, field2, field3] });
expect(schema.defaultReferenceFields).toEqual([field1]);
});
it('should return noop field as reference field if list is empty', () => {
const schema = createSchema({ properties: new SchemaPropertiesDto() });
expect(schema.defaultReferenceFields).toEqual(['']);
});
});
describe('FieldDto', () => {
it('should return label as display name', () => {
const field = createField({ properties: createProperties('Array', { label: 'Label' }) });
expect(field.displayName).toBe('Label');
});
it('should return name as display name if label is null', () => {
const field = createField({ properties: createProperties('Assets') });
expect(field.displayName).toBe('field1');
});
it('should return name as display name label is empty', () => {
const field = createField({ properties: createProperties('Assets', { label: '' }) });
expect(field.displayName).toBe('field1');
});
it('should return placeholder as display placeholder', () => {
const field = createField({ properties: createProperties('Assets', { placeholder: 'Placeholder' }) });
expect(field.displayPlaceholder).toBe('Placeholder');
});
it('should return empty as display placeholder if placeholder is null', () => {
const field = createField({ properties: createProperties('Assets') });
expect(field.displayPlaceholder).toBe('');
});
it('should return localizable if partitioning is language', () => {
const field = createField({ properties: createProperties('Assets'), partitioning: 'language' });
expect(field.isLocalizable).toBeTruthy();
});
it('should not return localizable if partitioning is invariant', () => {
const field = createField({ properties: createProperties('Assets'), partitioning: 'invariant' });
expect(field.isLocalizable).toBeFalsy();
});
});
describe('ArrayField', () => { describe('ArrayField', () => {
const field = createField({ properties: createProperties('Array', { isRequired: true, minItems: 1, maxItems: 5 }) }); const field = createField({ properties: createProperties('Array', { isRequired: true, minItems: 1, maxItems: 5 }) });

2
frontend/app/shared/state/contributors.state.spec.ts

@ -63,7 +63,7 @@ describe('ContributorsState', () => {
it('should reset loading state if loading failed', () => { it('should reset loading state if loading failed', () => {
contributorsService.setup(x => x.getContributors(app)) contributorsService.setup(x => x.getContributors(app))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
contributorsState.load().pipe(onErrorResumeNext()).subscribe(); contributorsState.load().pipe(onErrorResumeNext()).subscribe();

2
frontend/app/shared/state/languages.state.spec.ts

@ -77,7 +77,7 @@ describe('LanguagesState', () => {
it('should reset loading state if loading failed', () => { it('should reset loading state if loading failed', () => {
languagesService.setup(x => x.getLanguages(app)) languagesService.setup(x => x.getLanguages(app))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
languagesState.load().pipe(onErrorResumeNext()).subscribe(); languagesState.load().pipe(onErrorResumeNext()).subscribe();

2
frontend/app/shared/state/plans.state.spec.ts

@ -86,7 +86,7 @@ describe('PlansState', () => {
it('should reset loading state if loading failed', () => { it('should reset loading state if loading failed', () => {
plansService.setup(x => x.getPlans(app)) plansService.setup(x => x.getPlans(app))
.returns(() => throwError('error')); .returns(() => throwError('Service Error'));
plansState.load().pipe(onErrorResumeNext()).subscribe(); plansState.load().pipe(onErrorResumeNext()).subscribe();

4
frontend/app/shared/state/query.ts

@ -10,7 +10,7 @@
import { QueryParams, RouteSynchronizer, Types } from '@app/framework'; import { QueryParams, RouteSynchronizer, Types } from '@app/framework';
import { StatusInfo } from './../services/contents.service'; import { StatusInfo } from './../services/contents.service';
import { LanguageDto } from './../services/languages.service'; import { LanguageDto } from './../services/languages.service';
import { MetaFields, SchemaDetailsDto } from './../services/schemas.service'; import { MetaFields, SchemaDto } from './../services/schemas.service';
export type QueryValueType = export type QueryValueType =
'boolean' | 'boolean' |
@ -304,7 +304,7 @@ const DEFAULT_FIELDS: QueryModelFields = {
} }
}; };
export function queryModelFromSchema(schema: SchemaDetailsDto, languages: ReadonlyArray<LanguageDto>, statuses: ReadonlyArray<StatusInfo> | undefined) { export function queryModelFromSchema(schema: SchemaDto, languages: ReadonlyArray<LanguageDto>, statuses: ReadonlyArray<StatusInfo> | undefined) {
const languagesCodes = languages.map(x => x.iso2Code); const languagesCodes = languages.map(x => x.iso2Code);
const model: QueryModel = { const model: QueryModel = {

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save