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()
{
var schema = new Schema(Name, Properties, Singleton);
var schema = new Schema(Name, Properties, Singleton ? SchemaType.Singleton : SchemaType.Default);
if (Publish)
{

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

@ -8,7 +8,6 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
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.
// ==========================================================================
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Rules
{
public abstract record RuleTrigger

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

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
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.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
{
public abstract class FieldBase
{
private Dictionary<string, object> metadata;
public long Id { get; }
public string Name { get; }
public IDictionary<string, object> Metadata
{
get => metadata ??= new Dictionary<string, object>();
}
protected FieldBase(long id, string name)
{
Guard.NotNullOrEmpty(name, nameof(name));
@ -24,5 +33,26 @@ namespace Squidex.Domain.Apps.Core.Schemas
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
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -6,9 +6,9 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Schemas.Json
@ -19,10 +19,10 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
public string Category { get; set; }
public bool IsSingleton { get; set; }
public bool IsPublished { get; set; }
public SchemaType Type { get; set; }
public SchemaProperties Properties { get; set; }
public SchemaScripts? Scripts { get; set; }
@ -35,7 +35,18 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
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)
{
@ -54,8 +65,6 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
Partitioning = x.Partitioning.Key,
Properties = x.RawProperties
}).ToArray();
PreviewUrls = source.PreviewUrls.ToDictionary(x => x.Key, x => x.Value);
}
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 schema = new Schema(Name, fields, Properties, IsPublished, IsSingleton);
var schema = new Schema(Name, fields, Properties, IsPublished, Type);
if (!string.IsNullOrWhiteSpace(Category))
{
@ -116,4 +125,4 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
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.Linq;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Schemas
{
public sealed class Schema
{
private static readonly Dictionary<string, string> EmptyPreviewUrls = new Dictionary<string, string>();
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 SchemaType Type { get; }
public string Category
{
get => category;
}
public string Name { get; }
public bool IsPublished
{
get => isPublished;
}
public string Category { get; private set; }
public bool IsSingleton
{
get => isSingleton;
}
public bool IsPublished { get; private set; }
public IReadOnlyList<RootField> Fields
{
get => fields.Ordered;
}
public FieldCollection<RootField> FieldCollection { get; private set; } = FieldCollection<RootField>.Empty;
public IReadOnlyDictionary<long, RootField> FieldsById
{
get => fields.ById;
}
public FieldRules FieldRules { get; private set; } = FieldRules.Empty;
public IReadOnlyDictionary<string, RootField> FieldsByName
{
get => fields.ByName;
}
public FieldNames FieldsInLists { get; private set; } = FieldNames.Empty;
public IReadOnlyDictionary<string, string> PreviewUrls
{
get => previewUrls;
}
public FieldNames FieldsInReferences { get; private set; } = FieldNames.Empty;
public FieldCollection<RootField> FieldCollection
{
get => fields;
}
public SchemaScripts Scripts { get; private set; } = SchemaScripts.Empty;
public FieldRules FieldRules
{
get => fieldRules;
}
public SchemaProperties Properties { get; private set; } = new SchemaProperties();
public FieldNames FieldsInLists
{
get => fieldsInLists;
}
public ImmutableDictionary<string, string> PreviewUrls { get; private set; } = ImmutableDictionary.Empty<string, string>();
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));
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)
: this(name, properties, isSingleton)
public Schema(string name, RootField[] fields, SchemaProperties? properties, bool isPublished = false, SchemaType type = SchemaType.Default)
: this(name, properties, type)
{
Guard.NotNull(fields, nameof(fields));
this.fields = new FieldCollection<RootField>(fields);
FieldCollection = new FieldCollection<RootField>(fields);
this.isPublished = isPublished;
IsPublished = isPublished;
}
[Pure]
@ -124,14 +82,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
newProperties ??= new SchemaProperties();
if (properties.Equals(newProperties))
if (Properties.Equals(newProperties))
{
return this;
}
return Clone(clone =>
{
clone.properties = newProperties;
clone.Properties = newProperties;
});
}
@ -140,14 +98,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
newScripts ??= new SchemaScripts();
if (scripts.Equals(newScripts))
if (Scripts.Equals(newScripts))
{
return this;
}
return Clone(clone =>
{
clone.scripts = newScripts;
clone.Scripts = newScripts;
});
}
@ -156,14 +114,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
names ??= FieldNames.Empty;
if (fieldsInLists.SequenceEqual(names))
if (FieldsInLists.SequenceEqual(names))
{
return this;
}
return Clone(clone =>
{
clone.fieldsInLists = names;
clone.FieldsInLists = names;
});
}
@ -178,14 +136,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
names ??= FieldNames.Empty;
if (fieldsInReferences.SequenceEqual(names))
if (FieldsInReferences.SequenceEqual(names))
{
return this;
}
return Clone(clone =>
{
clone.fieldsInReferences = names;
clone.FieldsInReferences = names;
});
}
@ -200,14 +158,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
rules ??= FieldRules.Empty;
if (fieldRules.Equals(rules))
if (FieldRules.Equals(rules))
{
return this;
}
return Clone(clone =>
{
clone.fieldRules = rules;
clone.FieldRules = rules;
});
}
@ -220,58 +178,58 @@ namespace Squidex.Domain.Apps.Core.Schemas
[Pure]
public Schema Publish()
{
if (isPublished)
if (IsPublished)
{
return this;
}
return Clone(clone =>
{
clone.isPublished = true;
clone.IsPublished = true;
});
}
[Pure]
public Schema Unpublish()
{
if (!isPublished)
if (!IsPublished)
{
return this;
}
return Clone(clone =>
{
clone.isPublished = false;
clone.IsPublished = false;
});
}
[Pure]
public Schema ChangeCategory(string newCategory)
public Schema ChangeCategory(string category)
{
if (string.Equals(category, newCategory))
if (string.Equals(Category, category))
{
return this;
}
return Clone(clone =>
{
clone.category = newCategory;
clone.Category = category;
});
}
[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 Clone(clone =>
{
clone.previewUrls = newPreviewUrls;
clone.PreviewUrls = previewUrls;
});
}
@ -285,9 +243,9 @@ namespace Squidex.Domain.Apps.Core.Schemas
return Clone(clone =>
{
clone.fields = fields.Remove(fieldId);
clone.fieldsInLists = fieldsInLists.Remove(field.Name);
clone.fieldsInReferences = fieldsInReferences.Remove(field.Name);
clone.FieldCollection = FieldCollection.Remove(fieldId);
clone.FieldsInLists = FieldsInLists.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)
{
var newFields = updater(fields);
var newFields = updater(FieldCollection);
if (ReferenceEquals(newFields, fields))
if (ReferenceEquals(newFields, FieldCollection))
{
return this;
}
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());
}
public static bool IsSingleton(this Schema schema)
{
return schema.Type == SchemaType.Singleton;
}
public static string TypeName(this Schema schema)
{
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 static readonly SchemaProperties Empty = new SchemaProperties();
public ImmutableList<string>? Tags { 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))
{
yield return new SchemaPreviewUrlsConfigured { PreviewUrls = target.PreviewUrls.ToDictionary() };
yield return new SchemaPreviewUrlsConfigured { PreviewUrls = target.PreviewUrls };
}
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()
{
command.IsSingleton = true;
command.Type = SchemaType.Singleton;
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 NodaTime;
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.DomainObject.Guards;
using Squidex.Domain.Apps.Events;
@ -95,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
await CreateCore(c, operation);
if (operation.Schema.SchemaDef.IsSingleton)
if (operation.Schema.SchemaDef.IsSingleton())
{
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.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Translations;
@ -15,7 +16,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
{
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"));
}
@ -23,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
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"));
}
@ -31,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
public static void MustNotDeleteSingleton(this OperationContext context)
{
if (context.SchemaDef.IsSingleton)
if (context.SchemaDef.IsSingleton())
{
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 Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
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)
{
if (!context.SchemaDef.IsSingleton)
if (!context.SchemaDef.IsSingleton())
{
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)
{
if (!context.SchemaDef.IsSingleton)
if (!context.SchemaDef.IsSingleton())
{
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)
{
content.IsSingleton = schema.SchemaDef.IsSingleton;
content.IsSingleton = schema.SchemaDef.IsSingleton();
content.SchemaName = schemaName;
content.SchemaDisplayName = schemaDisplayName;

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

@ -7,6 +7,7 @@
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
@ -21,7 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
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);

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

@ -5,12 +5,12 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Entities.Schemas.Commands
{
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
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands;
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 IsSingleton { get; set; }
public SchemaType Type { get; set; }
public SchemaField[]? Fields { get; set; }
@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
public SchemaProperties Properties { get; set; }
public Dictionary<string, string>? PreviewUrls { get; set; }
public ImmutableDictionary<string, string>? PreviewUrls { get; set; }
[IgnoreDataMember]
public override DomainId AggregateId
@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
{
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.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using SchemaField = Squidex.Domain.Apps.Entities.Schemas.Commands.UpsertSchemaField;
namespace Squidex.Domain.Apps.Entities.Schemas.Commands
@ -31,11 +31,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
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)
{

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

@ -5,8 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands;
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 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;
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
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -251,7 +251,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
};
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);
@ -411,4 +411,4 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
return J.AsTask<ISchemaEntity>(Snapshot);
}
}
}
}

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

@ -1,4 +1,4 @@
// ==========================================================================
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// 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)
{
if (schema.SchemaDef.IsSingleton)
if (schema.SchemaDef.IsSingleton())
{
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.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Schemas
@ -13,6 +13,6 @@ namespace Squidex.Domain.Apps.Events.Schemas
[EventType(nameof(SchemaPreviewUrlsConfigured))]
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>
/// Indicates if the user can access the api.
/// </summary>
[Obsolete("Usage role properties")]
[Obsolete("Use 'roleProperties' field now.")]
public bool CanAccessApi { get; set; }
/// <summary>

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

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

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

@ -34,7 +34,7 @@ namespace Squidex.Areas.Api.Controllers
/// <summary>
/// The id of the entity that has been handled successfully or not.
/// </summary>
[Obsolete("Use Id instead.")]
[Obsolete("Use 'id' field now.")]
public DomainId? ContentId => Id;
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
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -386,7 +386,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(BulkResultDto[]), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsCreate)]
[ApiCosts(5)]
[Obsolete("Use bulk endpoint")]
[Obsolete("Use bulk endpoint now.")]
public async Task<IActionResult> PostContents(string app, string name, [FromBody] ImportContentsDto request)
{
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>
/// True to automatically publish the content.
/// </summary>
[Obsolete("Use Jobs.Status")]
[Obsolete("Use 'jobs.status' fields now.")]
public bool Publish { get; set; }
/// <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.
/// </summary>
[FromQuery]
[Obsolete("Use status query string.")]
[Obsolete("Use 'status' query string now.")]
public bool Publish { get; set; }
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>
/// True to automatically publish the content.
/// </summary>
[Obsolete("Use Bulk endpoint")]
[Obsolete("Use bulk endpoint now.")]
public bool Publish { get; set; }
/// <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.
/// </summary>
[FromQuery]
[Obsolete("Use status query string.")]
[Obsolete("Use 'status' query string now.")]
public bool Publish { get; set; }
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 Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
@ -16,7 +17,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
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.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
@ -16,28 +16,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary>
/// The name of fields that are used in content lists.
/// </summary>
public List<string>? FieldsInLists { get; set; }
public FieldNames? FieldsInLists { get; set; }
/// <summary>
/// The name of fields that are used in content references.
/// </summary>
public List<string>? FieldsInReferences { get; set; }
public FieldNames? FieldsInReferences { get; set; }
public ConfigureUIFields ToCommand()
{
var command = new ConfigureUIFields();
if (FieldsInLists != null)
{
command.FieldsInLists = new FieldNames(FieldsInLists);
}
if (FieldsInReferences != null)
{
command.FieldsInReferences = new FieldNames(FieldsInReferences);
}
return command;
return SimpleMapper.Map(this, new ConfigureUIFields());
}
}
}

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

@ -5,6 +5,8 @@
// 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.Infrastructure.Validation;
@ -19,10 +21,26 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string Name { get; set; }
/// <summary>
/// The type of the schema.
/// </summary>
public SchemaType Type { get; set; }
/// <summary>
/// Set to true to allow a single content item only.
/// </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()
{

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>
/// True to resolve first image in the content list.
/// </summary>
[Obsolete("Use ResolveFirst now")]
[Obsolete("Use 'resolveFirst' field now")]
public bool ResolveImage
{
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.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using NodaTime;
using Squidex.Areas.Api.Controllers.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
using Squidex.Web;
@ -22,6 +27,18 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// </summary>
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>
/// The name of the schema. Unique within the app.
/// </summary>
@ -29,6 +46,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string Name { get; set; }
/// <summary>
/// The type of the schema.
/// </summary>
public SchemaType Type { get; set; }
/// <summary>
/// The name of the category.
/// </summary>
@ -43,7 +65,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary>
/// Indicates if the schema is a singleton.
/// </summary>
public bool IsSingleton { get; set; }
[Obsolete("Use 'type' field now.")]
public bool IsSingleton
{
get => Type == SchemaType.Singleton;
}
/// <summary>
/// Indicates if the schema is published.
@ -51,41 +77,74 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
public bool IsPublished { get; set; }
/// <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>
[LocalizedRequired]
public RefToken CreatedBy { get; set; }
public SchemaScriptsDto Scripts { get; set; } = new SchemaScriptsDto();
/// <summary>
/// The user that has updated the schema.
/// The preview Urls.
/// </summary>
[LocalizedRequired]
public RefToken LastModifiedBy { get; set; }
public ImmutableDictionary<string, string> PreviewUrls { get; set; }
/// <summary>
/// The date and time when the schema has been created.
/// The name of fields that are used in content lists.
/// </summary>
public Instant Created { get; set; }
[LocalizedRequired]
public FieldNames FieldsInLists { get; set; }
/// <summary>
/// The date and time when the schema has been modified last.
/// The name of fields that are used in content references.
/// </summary>
public Instant LastModified { get; set; }
[LocalizedRequired]
public FieldNames FieldsInReferences { get; set; }
/// <summary>
/// The version of the schema.
/// The field rules.
/// </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();
SimpleMapper.Map(schema, result);
SimpleMapper.Map(schema.SchemaDef, result);
SimpleMapper.Map(schema.SchemaDef.Scripts, result.Scripts);
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;
}
@ -144,6 +203,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
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 Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
@ -27,12 +28,12 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary>
/// The names of the fields that should be used in references.
/// </summary>
public string[]? FieldsInReferences { get; set; }
public FieldNames? FieldsInReferences { get; set; }
/// <summary>
/// The names of the fields that should be shown in lists, including meta fields.
/// </summary>
public string[]? FieldsInLists { get; set; }
public FieldNames? FieldsInLists { get; set; }
/// <summary>
/// Optional fields.
@ -42,7 +43,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary>
/// The optional preview urls.
/// </summary>
public Dictionary<string, string>? PreviewUrls { get; set; }
public ImmutableDictionary<string, string>? PreviewUrls { get; set; }
/// <summary>
/// The category.
@ -72,16 +73,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
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)
{
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>
[HttpPost]
[Route("apps/{app}/schemas/{name}/fields/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 201)]
[ProducesResponseType(typeof(SchemaDto), 201)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> PostField(string app, string name, [FromBody] AddFieldDto request)
@ -69,7 +69,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns>
[HttpPost]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/")]
[ProducesResponseType(typeof(SchemaDetailsDto), 201)]
[ProducesResponseType(typeof(SchemaDto), 201)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
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>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/ui/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> PutSchemaUIFields(string app, string name, [FromBody] ConfigureUIFieldsDto request)
@ -119,7 +119,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/ordering/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> PutSchemaFieldOrdering(string app, string name, [FromBody] ReorderFieldsDto request)
@ -145,7 +145,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/ordering/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
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>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
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>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
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>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/lock/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> LockField(string app, string name, long id)
@ -255,7 +255,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[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)]
[ApiCosts(1)]
public async Task<IActionResult> LockNestedField(string app, string name, long parentId, long id)
@ -283,7 +283,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/hide/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> HideField(string app, string name, long id)
@ -312,7 +312,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[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)]
[ApiCosts(1)]
public async Task<IActionResult> HideNestedField(string app, string name, long parentId, long id)
@ -340,7 +340,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/show/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> ShowField(string app, string name, long id)
@ -369,7 +369,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[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)]
[ApiCosts(1)]
public async Task<IActionResult> ShowNestedField(string app, string name, long parentId, long id)
@ -397,7 +397,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/enable/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> EnableField(string app, string name, long id)
@ -426,7 +426,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[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)]
[ApiCosts(1)]
public async Task<IActionResult> EnableNestedField(string app, string name, long parentId, long id)
@ -454,7 +454,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/disable/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> DisableField(string app, string name, long id)
@ -483,7 +483,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </remarks>
[HttpPut]
[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)]
[ApiCosts(1)]
public async Task<IActionResult> DisableNestedField(string app, string name, long parentId, long id)
@ -508,7 +508,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns>
[HttpDelete]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
public async Task<IActionResult> DeleteField(string app, string name, long id)
@ -534,7 +534,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// </returns>
[HttpDelete]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")]
[ProducesResponseType(typeof(SchemaDetailsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)]
[ApiCosts(1)]
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);
}
private async Task<SchemaDetailsDto> InvokeCommandAsync(ICommand command)
private async Task<SchemaDto> InvokeCommandAsync(ICommand command)
{
var context = await CommandBus.PublishAsync(command);
var result = context.Result<ISchemaEntity>();
var response = SchemaDetailsDto.FromSchemaWithDetails(result, Resources);
var response = SchemaDto.FromSchema(result, Resources);
return response;
}
}
}
}

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

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

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

@ -11,6 +11,7 @@ using System.Linq;
using FluentAssertions;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Infrastructure.Collections;
using Xunit;
#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>
{
["web"] = "Url"
};
}.ToImmutableDictionary();
var urls2 = new Dictionary<string, string>
{
["web"] = "Url"
};
}.ToImmutableDictionary();
var schema_1 = schema_0.SetPreviewUrls(urls1);
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()
{
var schemaSource =
TestUtils.MixedSchema(true)
TestUtils.MixedSchema(SchemaType.Singleton)
.ChangeCategory("Category")
.SetFieldRules(FieldRule.Hide("2"))
.SetFieldsInLists("field2")
@ -465,7 +466,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
.SetPreviewUrls(new Dictionary<string, string>
{
["web"] = "Url"
})
}.ToImmutableDictionary())
.SetScripts(new SchemaScripts
{
Create = "<create-script>"
@ -476,6 +477,25 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
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)
{
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.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
@ -88,7 +89,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var previewUrls = new Dictionary<string, string>
{
["web"] = "Url"
};
}.ToImmutableDictionary();
var sourceSchema =
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);
});
var edmModel = TestUtils.MixedSchema().BuildEdmType(true, languagesConfig.ToResolver(), typeFactory);
var edmModel =
TestUtils.MixedSchema()
.BuildEdmType(true, languagesConfig.ToResolver(), typeFactory);
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);
}
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()
.AddArray(101, "root-array", Partitioning.Language, f => f
.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.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
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));
singletonSchema =
Mocks.Schema(appId, schemaId, new Schema(schemaId.Name, isSingleton: true));
Mocks.Schema(appId, schemaId, new Schema(schemaId.Name, type: SchemaType.Singleton));
}
[Fact]

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

@ -8,6 +8,7 @@
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Commands;
@ -23,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
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 =
new CommandContext(command, commandBus)
@ -38,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
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 =
new CommandContext(command, commandBus)
@ -53,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
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 =
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.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Validation;
using Xunit;
@ -657,7 +657,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards
[Fact]
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);
}

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.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands;
using Squidex.Log;
using Xunit;
@ -55,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
{
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);
@ -64,12 +65,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject
Assert.Equal(AppId, sut.Snapshot.AppId.Id);
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name);
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name);
Assert.True(sut.Snapshot.SchemaDef.IsSingleton);
Assert.Equal(SchemaType.Singleton, sut.Snapshot.SchemaDef.Type);
LastEvents
.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>
{
["Web"] = "web-url"
}
}.ToImmutableDictionary()
};
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.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Schemas
@ -47,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
PreviewUrls = new Dictionary<string, string>
{
["mobile"] = "http://mobile"
},
}.ToImmutableDictionary(),
Category = "myCategory"
};
@ -69,10 +70,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas
.SetPreviewUrls(new Dictionary<string, string>
{
["mobile"] = "http://mobile"
})
}.ToImmutableDictionary())
.Publish();
var actual = command.BuildSchema("my-schema", false);
var actual = command.BuildSchema("my-schema", SchemaType.Default);
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 schema1 = CreateSchema("schemaA1", false);
var schema1 = CreateSchema("schemaA1");
A.CallTo(() => appProvider.GetSchemasAsync(appId.Id))
.Returns(new List<ISchemaEntity> { schema1 });
@ -61,9 +61,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas
var ctx = ContextWithPermission(permission.Id);
var schema1 = CreateSchema("schemaA1", false);
var schema2 = CreateSchema("schemaA2", false);
var schema3 = CreateSchema("schemaB2", false);
var schema1 = CreateSchema("schemaA1");
var schema2 = CreateSchema("schemaA2");
var schema3 = CreateSchema("schemaB2");
A.CallTo(() => appProvider.GetSchemasAsync(appId.Id))
.Returns(new List<ISchemaEntity> { schema1, schema2, schema3 });
@ -93,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
var ctx = ContextWithPermission(permission.Id);
var schema1 = CreateSchema("schemaA1", true);
var schema1 = CreateSchema("schemaA1", SchemaType.Singleton);
A.CallTo(() => appProvider.GetSchemasAsync(appId.Id))
.Returns(new List<ISchemaEntity> { schema1 });
@ -112,9 +112,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas
.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)

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

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
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', () => {
eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => throwError('error'));
.returns(() => throwError('Service Error'));
eventConsumersState.load().pipe(onErrorResumeNext()).subscribe();
@ -68,7 +68,7 @@ describe('EventConsumersState', () => {
it('should show notification on load error if silent is false', () => {
eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => throwError({})).verifiable();
.returns(() => throwError('Service Error')).verifiable();
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', () => {
usersService.setup(x => x.getUsers(10, 0, undefined))
.returns(() => throwError('error'));
.returns(() => throwError('Service Error'));
usersState.load().pipe(onErrorResumeNext()).subscribe();
@ -136,11 +136,11 @@ describe('UsersState', () => {
it('should return null on select if user is not found', () => {
usersService.setup(x => x.getUser('unknown'))
.returns(() => throwError({})).verifiable();
.returns(() => throwError('Service Error')).verifiable();
let userSelected: UserDto;
usersState.select('unknown').subscribe(x => {
usersState.select('unknown').pipe(onErrorResumeNext()).subscribe(x => {
userSelected = x!;
}).unsubscribe();

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

@ -9,7 +9,7 @@
import { Component, OnInit, ViewChild } from '@angular/core';
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 { filter, map, tap } from 'rxjs/operators';
import { ContentReferencesComponent } from './references/content-references.component';
@ -29,7 +29,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
@ViewChild(ContentReferencesComponent)
public references: ContentReferencesComponent;
public schema: SchemaDetailsDto;
public schema: SchemaDto;
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 { AppLanguageDto, EditContentForm, FieldForm, FieldSection, RootFieldDto, SchemaDetailsDto, Version } from '@app/shared';
import { AppLanguageDto, EditContentForm, FieldForm, FieldSection, RootFieldDto, SchemaDto, Version } from '@app/shared';
@Component({
selector: 'sqx-content-editor',
@ -30,7 +30,7 @@ export class ContentEditorComponent {
public contentFormCompare?: EditContentForm | null;
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
@Input()
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 { 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 { distinctUntilChanged, map, switchMap, take, tap } from 'rxjs/operators';
import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component';
@ -29,7 +29,7 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit {
@ViewChild('dueTimeSelector', { static: false })
public dueTimeSelector: DueTimeSelectorComponent;
public schema: SchemaDetailsDto;
public schema: SchemaDto;
public tableView: TableFields;
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 { 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 { take } from 'rxjs/operators';
@ -35,7 +35,7 @@ export class PreviewButtonComponent extends StatefulComponent<State> implements
public content: ContentDto;
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
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 { 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({
selector: 'sqx-content-creator',
@ -38,7 +38,7 @@ export class ContentCreatorComponent extends ResourceOwner implements OnInit {
@Input()
public formContext: any;
public schema: SchemaDetailsDto;
public schema: SchemaDto;
public schemas: ReadonlyArray<SchemaDto> = [];
public contentForm: EditContentForm;

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

@ -8,7 +8,7 @@
/* tslint:disable: component-selector */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { ContentDto, LanguageDto, SchemaDetailsDto } from '@app/shared';
import { ContentDto, LanguageDto, SchemaDto } from '@app/shared';
@Component({
selector: '[sqxContentSelectorItem]',
@ -30,7 +30,7 @@ export class ContentSelectorItemComponent {
public language: LanguageDto;
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
@Input('sqxContentSelectorItem')
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 { 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({
selector: 'sqx-content-selector',
@ -35,7 +35,7 @@ export class ContentSelectorComponent extends ResourceOwner implements OnInit {
@Input()
public alreadySelected: ReadonlyArray<ContentDto>;
public schema: SchemaDetailsDto;
public schema: SchemaDto;
public schemas: ReadonlyArray<SchemaDto> = [];
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 { FormBuilder } from '@angular/forms';
import { EditSchemaForm, SchemaDetailsDto, SchemasState } from '@app/shared';
import { EditSchemaForm, SchemaDto, SchemasState } from '@app/shared';
@Component({
selector: 'sqx-schema-edit-form',
@ -16,7 +16,7 @@ import { EditSchemaForm, SchemaDetailsDto, SchemasState } from '@app/shared';
})
export class SchemaEditFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
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 { FormBuilder } from '@angular/forms';
import { SchemaDetailsDto, SchemasState, SynchronizeSchemaForm } from '@app/shared';
import { SchemaDto, SchemasState, SynchronizeSchemaForm } from '@app/shared';
@Component({
selector: 'sqx-schema-export-form',
@ -16,7 +16,7 @@ import { SchemaDetailsDto, SchemasState, SynchronizeSchemaForm } from '@app/shar
})
export class SchemaExportFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
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 { 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') };
@ -21,7 +21,7 @@ export class FieldWizardComponent implements OnInit {
public nameInput: ElementRef<HTMLElement>;
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
@Input()
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 { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
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({
selector: 'sqx-field',
@ -23,7 +23,7 @@ export class FieldComponent implements OnChanges {
public field: NestedFieldDto | RootFieldDto;
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
@Input()
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 { 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({
selector: 'sqx-schema-fields',
@ -18,7 +18,7 @@ export class SchemaFieldsComponent implements OnInit {
public fieldTypes = fieldTypes;
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
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 { FormBuilder } from '@angular/forms';
import { ConfigurePreviewUrlsForm, SchemaDetailsDto, SchemasState } from '@app/shared';
import { ConfigurePreviewUrlsForm, SchemaDto, SchemasState } from '@app/shared';
@Component({
selector: 'sqx-schema-preview-urls-form',
@ -16,7 +16,7 @@ import { ConfigurePreviewUrlsForm, SchemaDetailsDto, SchemasState } from '@app/s
})
export class SchemaPreviewUrlsFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
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 { 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({
selector: 'sqx-schema-field-rules-form',
@ -16,7 +16,7 @@ import { ConfigureFieldRulesForm, FIELD_RULE_ACTIONS, SchemaDetailsDto, SchemasS
})
export class SchemaFieldRulesFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
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 { 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 { SchemaCloning } from './../messages';
@ -22,7 +22,7 @@ import { SchemaCloning } from './../messages';
export class SchemaPageComponent extends ResourceOwner implements OnInit {
public readonly exact = { exact: true };
public schema: SchemaDetailsDto;
public schema: SchemaDto;
public schemaTab = this.route.queryParams.pipe(map(x => x['tab'] || 'fields'));
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 { 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';
@Component({
@ -17,7 +17,7 @@ import { EMPTY, Observable } from 'rxjs';
})
export class SchemaScriptsFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
public schemaScript = 'query';
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 { 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);
@ -24,7 +24,7 @@ export class FieldListComponent implements OnChanges {
public emptyText = '';
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
@Input()
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 { SchemaDetailsDto, SchemasState } from '@app/shared';
import { SchemaDto, SchemasState } from '@app/shared';
type State = { fieldsInLists: ReadonlyArray<string>, fieldsInReferences: ReadonlyArray<string> };
@ -17,7 +17,7 @@ type State = { fieldsInLists: ReadonlyArray<string>, fieldsInReferences: Readonl
})
export class SchemaUIFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
public schema: SchemaDto;
public selectedTab = 0;

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

@ -29,11 +29,11 @@
<div class="row">
<div class="col-6 type">
<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="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>
</div>
</div>
@ -47,11 +47,11 @@
</div>
<div class="col-6 type">
<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="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>
</div>
</div>

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

@ -37,7 +37,7 @@ export class SchemaFormComponent implements OnInit {
}
public ngOnInit() {
this.createForm.load({ ...this.import, name: '' });
this.createForm.load({ type: 'Default', ...this.import, name: '' });
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 { SchemaDetailsDto, SchemasState } from '@app/shared/internal';
import { SchemaDto, SchemasState } from '@app/shared/internal';
import { of } from 'rxjs';
import { IMock, It, Mock, Times } from 'typemoq';
import { SchemaMustExistPublishedGuard } from './schema-must-exist-published.guard';
@ -30,7 +30,7 @@ describe('SchemaMustExistPublishedGuard', () => {
it('should load schema and return true if found', () => {
schemasState.setup(x => x.select('123'))
.returns(() => of(<SchemaDetailsDto>{ isPublished: true }));
.returns(() => of(<SchemaDto>{ isPublished: true }));
let result: boolean;
@ -45,7 +45,7 @@ describe('SchemaMustExistPublishedGuard', () => {
it('should load schema and return false if not found', () => {
schemasState.setup(x => x.select('123'))
.returns(() => of(<SchemaDetailsDto>{ isPublished: false }));
.returns(() => of(<SchemaDto>{ isPublished: false }));
let result: boolean;

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

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

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

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

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

@ -36,18 +36,20 @@ export class AppDto {
constructor(links: ResourceLinks,
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 label: string | undefined,
public readonly description: string | undefined,
public readonly permissions: ReadonlyArray<string>,
public readonly created: DateTime,
public readonly lastModified: DateTime,
public readonly canAccessApi: boolean,
public readonly canAccessContent: boolean,
public readonly planName: string | undefined,
public readonly planUpgrade: string | undefined,
public readonly roleProperties: {},
public readonly version: Version
public readonly roleProperties: {}
) {
this._links = links;
@ -283,18 +285,18 @@ export class AppsService {
function parseApp(response: any) {
return new AppDto(response._links,
response.id,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()),
response.name,
response.label,
response.description,
response.permissions,
DateTime.parseISO(response.created),
DateTime.parseISO(response.lastModified),
response.canAccessApi,
response.canAccessContent,
response.planName,
response.planUpgrade,
response.roleProperties,
new Version(response.version.toString()));
response.roleProperties);
}
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}`,
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),
`My Name${key}.png`,
`My Hash${key}`,
'png',
@ -546,8 +547,7 @@ export function createAsset(id: number, tags?: ReadonlyArray<string>, suffix = '
tags || [
'tag1',
'tag2'
],
new Version(key));
]);
}
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 lastModified: DateTime,
public readonly lastModifiedBy: string,
public readonly version: Version,
public readonly fileName: string,
public readonly fileHash: string,
public readonly fileType: string,
@ -71,8 +72,7 @@ export class AssetDto {
public readonly metadataText: string,
public readonly metadata: any,
public readonly slug: string,
public readonly tags: ReadonlyArray<string>,
public readonly version: Version
public readonly tags: ReadonlyArray<string>
) {
this.canPreview =
(this.mimeType !== MIME_TIFF && this.type === 'Image') ||
@ -417,6 +417,7 @@ function parseAsset(response: any) {
response.id,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()),
response.fileName,
response.fileHash,
response.fileType,
@ -429,8 +430,7 @@ function parseAsset(response: any) {
response.metadataText,
response.metadata,
response.slug,
response.tags || [],
new Version(response.version.toString()));
response.tags || []);
}
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,
`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}`,
'black',
`NewStatus${key}`,
'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`)),
{},
'my-schema',
'MySchema',
{},
[],
new Version(key));
[]);
}

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

@ -60,21 +60,21 @@ export class ContentDto {
constructor(links: ResourceLinks,
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 createdBy: string,
public readonly lastModified: DateTime,
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 data: ContentData,
public readonly schemaName: string,
public readonly schemaDisplayName: string,
public readonly referenceData: ContentReferences,
public readonly referenceFields: ReadonlyArray<RootFieldDto>,
public readonly version: Version
public readonly referenceFields: ReadonlyArray<RootFieldDto>
) {
this._links = links;
@ -389,19 +389,19 @@ function buildQuery(q?: ContentQueryDto) {
function parseContent(response: any) {
return new ContentDto(response._links,
response.id,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()),
response.status,
response.statusColor,
response.newStatus,
response.newStatusColor,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
parseScheduleJob(response.scheduleJob),
response.data,
response.schemaName,
response.schemaDisplayName,
response.referenceData,
response.referenceFields.map(parseField),
new Version(response.version.toString()));
response.referenceFields.map(parseField));
}
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 { 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', () => {
const version = new Version('1');
@ -74,7 +74,7 @@ describe('SchemasService', () => {
it('should make get request to get schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.getSchema('my-app', 'my-schema').subscribe(result => {
schema = result;
@ -85,9 +85,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('GET');
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',
@ -95,7 +95,7 @@ describe('SchemasService', () => {
const dto = { name: 'name' };
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.postSchema('my-app', dto).subscribe(result => {
schema = result;
@ -106,9 +106,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('POST');
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',
@ -122,7 +122,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putSchema('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -133,9 +133,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -149,7 +149,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putScripts('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -160,9 +160,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -176,7 +176,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putFieldRules('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -187,9 +187,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -203,7 +203,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putSchemaSync('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -214,9 +214,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -230,7 +230,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putCategory('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -241,9 +241,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -257,7 +257,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putPreviewUrls('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -268,9 +268,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -284,7 +284,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.postField('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -295,9 +295,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('POST');
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',
@ -309,7 +309,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.publishSchema('my-app', resource, version).subscribe(result => {
schema = result;
@ -320,9 +320,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -334,7 +334,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.unpublishSchema('my-app', resource, version).subscribe(result => {
schema = result;
@ -345,9 +345,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -361,7 +361,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putField('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -372,9 +372,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -388,7 +388,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putUIFields('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -399,9 +399,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -415,7 +415,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.putFieldOrdering('my-app', resource, dto, version).subscribe(result => {
schema = result;
@ -426,9 +426,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -440,7 +440,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.lockField('my-app', resource, version).subscribe(result => {
schema = result;
@ -451,9 +451,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -465,7 +465,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.enableField('my-app', resource, version).subscribe(result => {
schema = result;
@ -476,9 +476,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -490,7 +490,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.disableField('my-app', resource, version).subscribe(result => {
schema = result;
@ -501,9 +501,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -515,7 +515,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.showField('my-app', resource, version).subscribe(result => {
schema = result;
@ -526,9 +526,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -540,7 +540,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.hideField('my-app', resource, version).subscribe(result => {
schema = result;
@ -551,9 +551,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT');
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',
@ -565,7 +565,7 @@ describe('SchemasService', () => {
}
};
let schema: SchemaDetailsDto;
let schema: SchemaDto;
schemasService.deleteField('my-app', resource, version).subscribe(result => {
schema = result;
@ -576,9 +576,9 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('DELETE');
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',
@ -621,36 +621,15 @@ describe('SchemasService', () => {
return {
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`,
createdBy: `creator${id}`,
lastModified: `${id % 1000 + 2000}-11-11T10:10:00Z`,
lastModifiedBy: `modifier${id}`,
properties: schemaPropertiesResponse(id, suffix),
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}`,
category: `schema-category${key}`,
isSingleton: id % 2 === 0,
type: id % 2 === 0 ? 'Default' : 'Singleton',
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),
previewUrls: {
Default: 'url'
@ -846,33 +825,14 @@ export function createSchema(id: number, suffix = '') {
return new SchemaDto(links,
`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}-11-11T10:10:00Z`), `modifier${id}`,
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}`,
new Version(key),
`schema-name${key}`,
`schema-category${key}`,
createSchemaProperties(id, suffix),
id % 2 === 0,
id % 2 === 0 ? 'Default' : 'Singleton',
id % 3 === 0,
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),
createSchemaProperties(id, suffix),
[
new RootFieldDto({}, 11, 'field11', createProperties('Array'), 'language', 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'
}],
{
Default: 'url'
},
{
query: '<script-query>',
create: '<script-create>',
change: '<script-change>',
delete: '<script-delete>',
update: '<script-update>'
},
{
Default: 'url'
});
}

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

@ -28,6 +28,8 @@ export const MetaFields = {
version: 'meta.version'
};
export type SchemaType = 'Default' | 'Singleton';
export class SchemaDto {
public readonly _links: ResourceLinks;
@ -50,18 +52,32 @@ export class SchemaDto {
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,
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 createdBy: string,
public readonly lastModified: DateTime,
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;
@ -83,32 +99,9 @@ export class SchemaDto {
this.canUpdateRules = hasAnyLink(links, 'update/rules');
this.displayName = StringHelper.firstNonEmpty(this.properties.label, this.name);
}
}
export class SchemaDetailsDto extends SchemaDto {
public readonly contentFields: ReadonlyArray<RootFieldDto>;
public readonly defaultListFields: ReadonlyArray<TableField>;
public readonly defaultReferenceFields: ReadonlyArray<TableField>;
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);
this.isDefault = type === 'Default';
this.isSingleton = type === 'Singleton';
if (fields) {
this.contentFields = fields.filter(x => x.properties.isContentField);
@ -193,7 +186,7 @@ export class SchemaDetailsDto extends SchemaDto {
return copy;
}),
isPublished: this.isPublished
type: this.type
};
return result;
@ -337,7 +330,7 @@ export type UpdateUIFields =
Readonly<{ fieldsInLists?: Tags; fieldsInReferences?: Tags; }>;
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 =
Readonly<{ name?: string; }>;
@ -370,22 +363,22 @@ export class SchemasService {
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}`);
return HTTP.getVersioned(this.http, url).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
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`);
return HTTP.postVersioned(this.http, url, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'Created', appName);
@ -393,14 +386,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'ScriptsConfigured', appName);
@ -408,14 +401,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, { fieldRules: dto }).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'RulesConfigured', appName);
@ -423,14 +416,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'Updated', appName);
@ -438,14 +431,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'Updated', appName);
@ -453,14 +446,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'CategoryChanged', appName);
@ -468,14 +461,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'PreviewUrlsConfigured', appName);
@ -483,14 +476,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'Published', appName);
@ -498,14 +491,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'Unpublished', appName);
@ -513,14 +506,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldCreated', appName);
@ -528,14 +521,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'UIFieldsConfigured', appName);
@ -543,14 +536,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, { fieldIds: dto }).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldsReordered', appName);
@ -558,14 +551,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, dto).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldUpdated', appName);
@ -573,14 +566,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldLocked', appName);
@ -588,14 +581,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldEnabled', appName);
@ -603,14 +596,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldDisabled', appName);
@ -618,14 +611,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldShown', appName);
@ -633,14 +626,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldHidden', appName);
@ -648,14 +641,14 @@ export class SchemasService {
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 url = this.apiUrl.buildUrl(link.href);
return HTTP.requestVersioned(this.http, link.method, url, version, {}).pipe(
map(({ payload }) => {
return parseSchemaWithDetails(payload.body);
return parseSchema(payload.body);
}),
tap(() => {
this.analytics.trackEvent('Schema', 'FieldDeleted', appName);
@ -685,42 +678,32 @@ export class SchemasService {
function parseSchemas(response: any) {
const raw: any[] = response.items;
const items = raw.map(item =>
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 items = raw.map(parseSchema);
const _links = response._links;
return { items, _links, canCreate: hasAnyLink(_links, 'create') };
}
function parseSchemaWithDetails(response: any) {
function parseSchema(response: any) {
const fields = response.fields.map(parseField);
return new SchemaDetailsDto(response._links,
return new SchemaDto(response._links,
response.id,
response.name,
response.category,
parseProperties(response.properties),
response.isSingleton,
response.isPublished,
DateTime.parseISO(response.created), response.createdBy,
DateTime.parseISO(response.lastModified), response.lastModifiedBy,
new Version(response.version.toString()),
response.name,
response.category,
response.type,
response.isPublished,
parseProperties(response.properties),
fields,
response.fieldsInLists,
response.fieldsInReferences,
response.fieldRules,
response.scripts || {},
response.previewUrls || {});
response.previewUrls || {},
response.scripts || {});
}
function parseProperties(response: any) {

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

@ -12,9 +12,10 @@ import { TestValues } from './../state/_test-helpers';
const {
createField,
createSchema} = TestValues;
createSchema
} = TestValues;
describe('SchemaDetailsDto', () => {
describe('SchemaDto', () => {
const field1 = createField({ properties: createProperties('Array'), id: 1 });
const field2 = createField({ properties: createProperties('Array'), id: 2 });
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', () => {
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', () => {
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', () => {

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

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

@ -7,7 +7,7 @@
import { of } from 'rxjs';
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 creation = DateTime.today().addDays(-2);
@ -42,16 +42,18 @@ type SchemaValues = {
function createSchema({ properties, id, fields, fieldsInLists, fieldsInReferences, fieldRules }: SchemaValues = {}) {
id = id || 1;
return new SchemaDetailsDto({},
`schema${1}`,
`schema${1}`,
'category',
properties || new SchemaPropertiesDto(), false, true,
return new SchemaDto({},
`schema${id}`,
creation,
creator,
modified,
modifier,
new Version('1'),
`schema-name${id}`,
`schema-category${id}`,
'Default',
true,
properties || new SchemaPropertiesDto(),
fields,
fieldsInLists || [],
fieldsInReferences || [],

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

@ -7,6 +7,7 @@
import { AppDto, AppsService, AppsState, DialogService } from '@app/shared/internal';
import { of, throwError } from 'rxjs';
import { onErrorResumeNext } from 'rxjs/operators';
import { IMock, It, Mock, Times } from 'typemoq';
import { createApp, createAppSettings } from './../services/apps.service.spec';
@ -107,9 +108,9 @@ describe('AppsState', () => {
let appSelected: AppDto;
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!;
});

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

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

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

@ -279,7 +279,7 @@ describe('AssetsState', () => {
const request = { parentId: 'newParent' };
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();
@ -318,7 +318,7 @@ describe('AssetsState', () => {
const request = { parentId: 'newParent' };
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();

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

@ -52,7 +52,7 @@ describe('BackupsState', () => {
it('should reset loading state if loading failed', () => {
backupsService.setup(x => x.getBackups(app))
.returns(() => throwError('error'));
.returns(() => throwError('Service Error'));
backupsState.load().pipe(onErrorResumeNext()).subscribe();
@ -72,7 +72,7 @@ describe('BackupsState', () => {
it('should show notification on load error if silent is false', () => {
backupsService.setup(x => x.getBackups(app))
.returns(() => throwError({}));
.returns(() => throwError('Service Error'));
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', () => {
backupsService.setup(x => x.getBackups(app))
.returns(() => throwError({}));
.returns(() => throwError('Service Error'));
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', () => {
clientsService.setup(x => x.getClients(app))
.returns(() => throwError('error'));
.returns(() => throwError('Service Error'));
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 { AppLanguageDto } from './../services/app-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 { AbstractContentForm, AbstractContentFormState, CompiledRule, FieldSection, PartitionConfig } from './contents.forms-helpers';
import { FieldDefaultValue, FieldsValidators } from './contents.forms.visitors';
@ -91,7 +91,7 @@ export class EditContentForm extends Form<FormGroup, any> {
return this.valueChange$.value;
}
constructor(languages: ReadonlyArray<AppLanguageDto>, schema: SchemaDetailsDto,
constructor(languages: ReadonlyArray<AppLanguageDto>, schema: SchemaDto,
private context: any, debounce = 100
) {
super(new FormGroup({}));

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

@ -8,120 +8,15 @@
// tslint:disable: max-line-length
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';
const {
createField,
createSchema
createField
} = TestValues;
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', () => {
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', () => {
contributorsService.setup(x => x.getContributors(app))
.returns(() => throwError('error'));
.returns(() => throwError('Service Error'));
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', () => {
languagesService.setup(x => x.getLanguages(app))
.returns(() => throwError('error'));
.returns(() => throwError('Service Error'));
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', () => {
plansService.setup(x => x.getPlans(app))
.returns(() => throwError('error'));
.returns(() => throwError('Service Error'));
plansState.load().pipe(onErrorResumeNext()).subscribe();

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

@ -10,7 +10,7 @@
import { QueryParams, RouteSynchronizer, Types } from '@app/framework';
import { StatusInfo } from './../services/contents.service';
import { LanguageDto } from './../services/languages.service';
import { MetaFields, SchemaDetailsDto } from './../services/schemas.service';
import { MetaFields, SchemaDto } from './../services/schemas.service';
export type QueryValueType =
'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 model: QueryModel = {

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

Loading…
Cancel
Save