Browse Source

Improve handling of default values. (#983)

* Improve handling of default values.

* Open Api fixes and parent id.
pull/984/head
Sebastian Stehle 4 years ago
committed by GitHub
parent
commit
46837d6195
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesSurrogate.cs
  2. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs
  3. 10
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs
  4. 103
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddDefaultValues.cs
  5. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddSchemaNames.cs
  6. 61
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs
  7. 92
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/DefaultValueChecker.cs
  8. 6
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/DefaultValueFactory.cs
  9. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs
  10. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeHidden.cs
  11. 31
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/IConverter.cs
  12. 31
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveFromPreviousPartitioning.cs
  13. 24
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveLanguages.cs
  14. 57
      backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs
  15. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs
  16. 4
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs
  17. 4
      backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs
  18. 5
      backend/src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs
  19. 29
      backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs
  20. 4
      backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetFolderRepository.cs
  21. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs
  22. 12
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs
  23. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedInputGraphType.cs
  24. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonGraphType.cs
  25. 17
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  26. 11
      backend/src/Squidex.Domain.Users.MongoDb/MongoUserStore.cs
  27. 2
      backend/src/Squidex.Web/ErrorDto.cs
  28. 13
      backend/src/Squidex.Web/OpenApiRequestAttribute.cs
  29. 18
      backend/src/Squidex/Areas/Api/Config/OpenApi/OpenApiServices.cs
  30. 46
      backend/src/Squidex/Areas/Api/Config/OpenApi/RequiredSchemaProcessor.cs
  31. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddLanguageDto.cs
  32. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddRoleDto.cs
  33. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddWorkflowDto.cs
  34. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  35. 3
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs
  36. 1
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs
  37. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppSettingsDto.cs
  38. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs
  39. 1
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientsDto.cs
  40. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/CreateAppDto.cs
  41. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/CreateClientDto.cs
  42. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/EditorDto.cs
  43. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/PatternDto.cs
  44. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs
  45. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs
  46. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/TransferToTeamDto.cs
  47. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppDto.cs
  48. 7
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs
  49. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAssetScriptsDto.cs
  50. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateClientDto.cs
  51. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs
  52. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateRoleDto.cs
  53. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateWorkflowDto.cs
  54. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowDto.cs
  55. 3
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs
  56. 3
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs
  57. 46
      backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs
  58. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AnnotateAssetDto.cs
  59. 9
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs
  60. 1
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderDto.cs
  61. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFoldersDto.cs
  62. 1
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs
  63. 3
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/BulkUpdateAssetsDto.cs
  64. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/BulkUpdateAssetsJobDto.cs
  65. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/CreateAssetDto.cs
  66. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/CreateAssetFolderDto.cs
  67. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/DeleteAssetDto.cs
  68. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/MoveAssetDto.cs
  69. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/MoveAssetFolderDto.cs
  70. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/RenameAssetFolderDto.cs
  71. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/UpsertAssetDto.cs
  72. 2
      backend/src/Squidex/Areas/Api/Controllers/AssignContributorDto.cs
  73. 2
      backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobsDto.cs
  74. 3
      backend/src/Squidex/Areas/Api/Controllers/Backups/Models/RestoreJobDto.cs
  75. 2
      backend/src/Squidex/Areas/Api/Controllers/Backups/Models/RestoreRequestDto.cs
  76. 4
      backend/src/Squidex/Areas/Api/Controllers/Comments/Models/CommentDto.cs
  77. 2
      backend/src/Squidex/Areas/Api/Controllers/Comments/Models/UpsertCommentDto.cs
  78. 1
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  79. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/AllContentsByGetDto.cs
  80. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/AllContentsByPostDto.cs
  81. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs
  82. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsJobDto.cs
  83. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ChangeStatusDto.cs
  84. 3
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  85. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs
  86. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs
  87. 1
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ScheduleJobDto.cs
  88. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/StatusInfoDto.cs
  89. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/UpsertContentDto.cs
  90. 3
      backend/src/Squidex/Areas/Api/Controllers/ContributorDto.cs
  91. 1
      backend/src/Squidex/Areas/Api/Controllers/ContributorsDto.cs
  92. 3
      backend/src/Squidex/Areas/Api/Controllers/History/Models/HistoryEventDto.cs
  93. 3
      backend/src/Squidex/Areas/Api/Controllers/LanguageDto.cs
  94. 2
      backend/src/Squidex/Areas/Api/Controllers/News/Models/FeatureDto.cs
  95. 1
      backend/src/Squidex/Areas/Api/Controllers/News/Models/FeaturesDto.cs
  96. 2
      backend/src/Squidex/Areas/Api/Controllers/Plans/Models/ChangePlanDto.cs
  97. 3
      backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlanDto.cs
  98. 1
      backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansDto.cs
  99. 2
      backend/src/Squidex/Areas/Api/Controllers/RenameTagDto.cs
  100. 2
      backend/src/Squidex/Areas/Api/Controllers/Rules/Models/CreateRuleDto.cs

4
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesSurrogate.cs

@ -25,7 +25,7 @@ public sealed class RolesSurrogate : Dictionary<string, JsonValue>, ISurrogate<R
} }
var role = var role =
new JsonObject() JsonValue.Object()
.Add("permissions", permissions) .Add("permissions", permissions)
.Add("properties", customRole.Properties); .Add("properties", customRole.Properties);
@ -44,7 +44,7 @@ public sealed class RolesSurrogate : Dictionary<string, JsonValue>, ISurrogate<R
{ {
var (key, value) = x; var (key, value) = x;
var properties = new JsonObject(); var properties = JsonValue.Object();
var permissions = PermissionSet.Empty; var permissions = PermissionSet.Empty;
if (value.Value is JsonArray a) if (value.Value is JsonArray a)

4
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs

@ -38,7 +38,7 @@ public sealed record Role(string Name, PermissionSet? Permissions = null, JsonOb
public string Name { get; } = Guard.NotNullOrEmpty(Name); public string Name { get; } = Guard.NotNullOrEmpty(Name);
public JsonObject Properties { get; } = Properties ?? new JsonObject(); public JsonObject Properties { get; } = Properties ?? JsonValue.Object();
public PermissionSet Permissions { get; } = Permissions ?? PermissionSet.Empty; public PermissionSet Permissions { get; } = Permissions ?? PermissionSet.Empty;
@ -49,7 +49,7 @@ public sealed record Role(string Name, PermissionSet? Permissions = null, JsonOb
public static Role WithPermissions(string name, params string[] permissions) public static Role WithPermissions(string name, params string[] permissions)
{ {
return new Role(name, new PermissionSet(permissions), new JsonObject()); return new Role(name, new PermissionSet(permissions), JsonValue.Object());
} }
public static Role WithProperties(string name, JsonObject properties) public static Role WithProperties(string name, JsonObject properties)

10
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs

@ -25,13 +25,13 @@ public sealed class Roles
new Role(Role.Owner, new Role(Role.Owner,
new PermissionSet( new PermissionSet(
WithoutPrefix(PermissionIds.App)), WithoutPrefix(PermissionIds.App)),
new JsonObject()), JsonValue.Object()),
[Role.Reader] = [Role.Reader] =
new Role(Role.Reader, new Role(Role.Reader,
new PermissionSet( new PermissionSet(
WithoutPrefix(PermissionIds.AppAssetsRead), WithoutPrefix(PermissionIds.AppAssetsRead),
WithoutPrefix(PermissionIds.AppContentsRead)), WithoutPrefix(PermissionIds.AppContentsRead)),
new JsonObject() JsonValue.Object()
.Add("ui.api.hide", true)), .Add("ui.api.hide", true)),
[Role.Editor] = [Role.Editor] =
new Role(Role.Editor, new Role(Role.Editor,
@ -40,7 +40,7 @@ public sealed class Roles
WithoutPrefix(PermissionIds.AppContents), WithoutPrefix(PermissionIds.AppContents),
WithoutPrefix(PermissionIds.AppRolesRead), WithoutPrefix(PermissionIds.AppRolesRead),
WithoutPrefix(PermissionIds.AppWorkflowsRead)), WithoutPrefix(PermissionIds.AppWorkflowsRead)),
new JsonObject() JsonValue.Object()
.Add("ui.api.hide", true)), .Add("ui.api.hide", true)),
[Role.Developer] = [Role.Developer] =
new Role(Role.Developer, new Role(Role.Developer,
@ -51,7 +51,7 @@ public sealed class Roles
WithoutPrefix(PermissionIds.AppRules), WithoutPrefix(PermissionIds.AppRules),
WithoutPrefix(PermissionIds.AppSchemas), WithoutPrefix(PermissionIds.AppSchemas),
WithoutPrefix(PermissionIds.AppWorkflows)), WithoutPrefix(PermissionIds.AppWorkflows)),
new JsonObject()) JsonValue.Object())
}; };
public static readonly Roles Empty = new Roles(new ReadonlyDictionary<string, Role>()); public static readonly Roles Empty = new Roles(new ReadonlyDictionary<string, Role>());
@ -105,7 +105,7 @@ public sealed class Roles
return this; return this;
} }
var newRole = new Role(name, null, new JsonObject()); var newRole = new Role(name, null, JsonValue.Object());
if (!inner.TryAdd(name, newRole, out var updated)) if (!inner.TryAdd(name, newRole, out var updated))
{ {

103
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddDefaultValues.cs

@ -0,0 +1,103 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent;
public sealed class AddDefaultValues : IContentDataConverter, IContentItemConverter, IContentFieldConverter
{
private readonly PartitionResolver partitionResolver;
private readonly IClock clock;
private Instant now;
public bool IgnoreRequiredFields { get; init; }
public bool IgnoreNonMasterFields { get; init; }
public AddDefaultValues(PartitionResolver partitionResolver, IClock? clock = null)
{
this.partitionResolver = partitionResolver;
this.clock = clock ?? SystemClock.Instance;
}
public void ConvertDataBefore(Schema schema, ContentData data)
{
foreach (var field in schema.Fields)
{
if (data.TryGetValue(field.Name, out var fieldData) && fieldData != null)
{
continue;
}
if ((field.RawProperties.IsRequired && IgnoreRequiredFields) || !DefaultValueChecker.HasDefaultValue(field))
{
continue;
}
data[field.Name] = new ContentFieldData();
}
}
public ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source)
{
var partitioning = partitionResolver(field.Partitioning);
foreach (var partitionKey in partitioning.AllKeys)
{
if (!partitioning.IsMaster(partitionKey) && IgnoreNonMasterFields)
{
continue;
}
Enrich(field, source, partitionKey);
}
return source;
}
public JsonObject ConvertItemBefore(IField parentField, JsonObject source, IEnumerable<IField> schema)
{
foreach (var field in schema)
{
Enrich(field, source, field.Name);
}
return source;
}
private void Enrich(IField field, Dictionary<string, JsonValue> fieldData, string key)
{
if (fieldData.TryGetValue(key, out _) || (field.RawProperties.IsRequired && IgnoreRequiredFields))
{
return;
}
var defaultValue = DefaultValueFactory.CreateDefaultValue(field, GetNow(), key);
if (defaultValue == default)
{
return;
}
fieldData[key] = defaultValue;
}
private Instant GetNow()
{
if (now == default)
{
now = clock.GetCurrentInstant();
}
return now;
}
}

8
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddSchemaNames.cs

@ -21,9 +21,9 @@ public sealed class AddSchemaNames : IContentItemConverter
this.components = components; this.components = components;
} }
public JsonObject ConvertItem(IField field, JsonObject source) public JsonObject ConvertItemBefore(IField parentField, JsonObject source, IEnumerable<IField> schema)
{ {
if (field is IArrayField) if (parentField is IArrayField)
{ {
return source; return source;
} }
@ -40,9 +40,9 @@ public sealed class AddSchemaNames : IContentItemConverter
var id = DomainId.Create(discriminator); var id = DomainId.Create(discriminator);
if (components.TryGetValue(id, out var schema)) if (components.TryGetValue(id, out var component))
{ {
source["schemaName"] = schema.Name; source["schemaName"] = component.Name;
} }
return source; return source;

61
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs

@ -16,8 +16,8 @@ namespace Squidex.Domain.Apps.Core.ConvertContent;
public sealed class ContentConverter public sealed class ContentConverter
{ {
private readonly List<IContentDataConverter> dataConverters = new List<IContentDataConverter>();
private readonly List<IContentItemConverter> itemConverters = new List<IContentItemConverter>(); private readonly List<IContentItemConverter> itemConverters = new List<IContentItemConverter>();
private readonly List<IContentFieldAfterConverter> fieldAfterConverters = new List<IContentFieldAfterConverter>();
private readonly List<IContentFieldConverter> fieldConverters = new List<IContentFieldConverter>(); private readonly List<IContentFieldConverter> fieldConverters = new List<IContentFieldConverter>();
private readonly List<IContentValueConverter> valueConverters = new List<IContentValueConverter>(); private readonly List<IContentValueConverter> valueConverters = new List<IContentValueConverter>();
private readonly ResolvedComponents components; private readonly ResolvedComponents components;
@ -31,6 +31,11 @@ public sealed class ContentConverter
public ContentConverter Add(IConverter converter) public ContentConverter Add(IConverter converter)
{ {
if (converter is IContentDataConverter contentConverter)
{
dataConverters.Add(contentConverter);
}
if (converter is IContentItemConverter itemConverter) if (converter is IContentItemConverter itemConverter)
{ {
itemConverters.Add(itemConverter); itemConverters.Add(itemConverter);
@ -41,11 +46,6 @@ public sealed class ContentConverter
fieldConverters.Add(fieldConverter); fieldConverters.Add(fieldConverter);
} }
if (converter is IContentFieldAfterConverter fieldAfterConverter)
{
fieldAfterConverters.Add(fieldAfterConverter);
}
if (converter is IContentValueConverter valueConverter) if (converter is IContentValueConverter valueConverter)
{ {
valueConverters.Add(valueConverter); valueConverters.Add(valueConverter);
@ -56,12 +56,16 @@ public sealed class ContentConverter
public ContentData Convert(ContentData content) public ContentData Convert(ContentData content)
{ {
Guard.NotNull(schema);
// The conversion process assumes that we have ownership of the data and can manipulate it. // The conversion process assumes that we have ownership of the data and can manipulate it.
// Clones are only created to save allocations. // Clones are only created to save allocations.
var result = new ContentData(content.Count); var result = new ContentData(content.Count);
// Some conversions should be made early, e.g. calculating default values.
foreach (var converter in dataConverters)
{
converter.ConvertDataBefore(schema, content);
}
foreach (var (fieldName, fieldData) in content) foreach (var (fieldName, fieldData) in content)
{ {
if (fieldData == null || !schema.FieldsByName.TryGetValue(fieldName, out var field)) if (fieldData == null || !schema.FieldsByName.TryGetValue(fieldName, out var field))
@ -88,34 +92,44 @@ public sealed class ContentConverter
} }
} }
// Some conversions should be done later.
foreach (var converter in dataConverters)
{
converter.ConvertDataAfter(schema, result);
}
return result; return result;
} }
private ContentFieldData? ConvertField(IRootField field, ContentFieldData? data) private ContentFieldData? ConvertField(IRootField field, ContentFieldData data)
{ {
foreach (var converter in fieldConverters) foreach (var converter in fieldConverters)
{ {
data = converter.ConvertField(field, data!); var newData = converter.ConvertFieldBefore(field, data);
if (data == null) if (newData == null)
{ {
break; return null;
} }
data = newData;
} }
return data; return data;
} }
private ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData? data) private ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData data)
{ {
foreach (var converter in fieldAfterConverters) foreach (var converter in fieldConverters)
{ {
data = converter.ConvertFieldAfter(field, data!); var newData = converter.ConvertFieldAfter(field, data);
if (data == null) if (newData == null)
{ {
break; return null;
} }
data = newData;
} }
return data; return data;
@ -206,7 +220,7 @@ public sealed class ContentConverter
return (true, default); return (true, default);
} }
return (false, ConvertNested(component.FieldCollection, obj, parent)); return (false, ConvertNested(component.FieldCollection, obj, parent, component.Fields));
} }
private (bool Remove, JsonValue) ConvertArrayItem(IArrayField field, JsonValue value) private (bool Remove, JsonValue) ConvertArrayItem(IArrayField field, JsonValue value)
@ -216,7 +230,7 @@ public sealed class ContentConverter
return (true, default); return (true, default);
} }
return (false, ConvertNested(field.FieldCollection, obj, field)); return (false, ConvertNested(field.FieldCollection, obj, field, field.Fields));
} }
private ContentFieldData ConvertValues(IField field, ContentFieldData source) private ContentFieldData ConvertValues(IField field, ContentFieldData source)
@ -244,8 +258,13 @@ public sealed class ContentConverter
return result ?? source; return result ?? source;
} }
private JsonValue ConvertNested<T>(FieldCollection<T> fields, JsonObject source, IField parent) where T : IField private JsonValue ConvertNested<T>(FieldCollection<T> fields, JsonObject source, IField parent, IEnumerable<IField> fieldSchema) where T : IField
{ {
foreach (var converter in itemConverters)
{
source = converter.ConvertItemBefore(parent, source, fieldSchema);
}
JsonObject? result = null; JsonObject? result = null;
foreach (var (key, oldValue) in source) foreach (var (key, oldValue) in source)
@ -281,7 +300,7 @@ public sealed class ContentConverter
foreach (var converter in itemConverters) foreach (var converter in itemConverters)
{ {
result = converter.ConvertItem(parent, result); result = converter.ConvertItemAfter(parent, result, fieldSchema);
} }
return result ?? source; return result ?? source;

92
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/DefaultValueChecker.cs

@ -0,0 +1,92 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.ConvertContent;
internal sealed class DefaultValueChecker : IFieldPropertiesVisitor<bool, None>
{
private static readonly DefaultValueChecker Instance = new DefaultValueChecker();
private DefaultValueChecker()
{
}
public static bool HasDefaultValue(IField field)
{
Guard.NotNull(field);
return field.RawProperties.Accept(Instance, None.Value);
}
public bool Visit(ArrayFieldProperties properties, None args)
{
return true;
}
public bool Visit(AssetsFieldProperties properties, None args)
{
return properties.DefaultValue != null || properties.DefaultValues != null;
}
public bool Visit(BooleanFieldProperties properties, None args)
{
return properties.DefaultValue != null || properties.DefaultValues != null;
}
public bool Visit(ComponentFieldProperties properties, None args)
{
return false;
}
public bool Visit(ComponentsFieldProperties properties, None args)
{
return true;
}
public bool Visit(DateTimeFieldProperties properties, None args)
{
return properties.DefaultValue != null || properties.DefaultValues != null || properties.CalculatedDefaultValue != null;
}
public bool Visit(GeolocationFieldProperties properties, None args)
{
return false;
}
public bool Visit(JsonFieldProperties properties, None args)
{
return false;
}
public bool Visit(NumberFieldProperties properties, None args)
{
return properties.DefaultValue != null || properties.DefaultValues != null;
}
public bool Visit(ReferencesFieldProperties properties, None args)
{
return properties.DefaultValue != null || properties.DefaultValues != null;
}
public bool Visit(StringFieldProperties properties, None args)
{
return properties.DefaultValue != null || properties.DefaultValues != null;
}
public bool Visit(TagsFieldProperties properties, None args)
{
return properties.DefaultValue != null || properties.DefaultValues != null;
}
public bool Visit(UIFieldProperties properties, None args)
{
return false;
}
}

6
backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs → backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/DefaultValueFactory.cs

@ -13,7 +13,7 @@ using Squidex.Infrastructure.Json.Objects;
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter #pragma warning disable SA1313 // Parameter names should begin with lower-case letter
namespace Squidex.Domain.Apps.Core.DefaultValues; namespace Squidex.Domain.Apps.Core.ConvertContent;
public sealed class DefaultValueFactory : IFieldPropertiesVisitor<JsonValue, DefaultValueFactory.Args> public sealed class DefaultValueFactory : IFieldPropertiesVisitor<JsonValue, DefaultValueFactory.Args>
{ {
@ -30,9 +30,7 @@ public sealed class DefaultValueFactory : IFieldPropertiesVisitor<JsonValue, Def
Guard.NotNull(field); Guard.NotNull(field);
Guard.NotNull(partition); Guard.NotNull(partition);
var x = field.RawProperties.Accept(Instance, new Args(now, partition)); return field.RawProperties.Accept(Instance, new Args(now, partition));
return x;
} }
public JsonValue Visit(ArrayFieldProperties properties, Args args) public JsonValue Visit(ArrayFieldProperties properties, Args args)

2
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs

@ -22,7 +22,7 @@ public sealed class ExcludeChangedTypes : IContentFieldConverter, IContentValueC
this.serializer = serializer; this.serializer = serializer;
} }
public ContentFieldData? ConvertField(IRootField field, ContentFieldData source) public ContentFieldData? ConvertFieldBefore(IRootField field, ContentFieldData source)
{ {
foreach (var (_, value) in source) foreach (var (_, value) in source)
{ {

2
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeHidden.cs

@ -19,7 +19,7 @@ public sealed class ExcludeHidden : IContentFieldConverter, IContentValueConvert
{ {
} }
public ContentFieldData? ConvertField(IRootField field, ContentFieldData source) public ContentFieldData? ConvertFieldBefore(IRootField field, ContentFieldData source)
{ {
return field.IsForApi() ? source : null; return field.IsForApi() ? source : null;
} }

31
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/IConverter.cs

@ -8,6 +8,7 @@
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using static Google.Rpc.Context.AttributeContext.Types;
#pragma warning disable MA0048 // File name must match type name #pragma warning disable MA0048 // File name must match type name
@ -17,19 +18,41 @@ public interface IConverter
{ {
} }
public interface IContentFieldAfterConverter : IConverter public interface IContentDataConverter
{ {
ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source); void ConvertDataBefore(Schema schema, ContentData source)
{
}
void ConvertDataAfter(Schema schema, ContentData source)
{
}
} }
public interface IContentFieldConverter : IConverter public interface IContentFieldConverter : IConverter
{ {
ContentFieldData? ConvertField(IRootField field, ContentFieldData source); ContentFieldData? ConvertFieldBefore(IRootField field, ContentFieldData source)
{
return source;
}
ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source)
{
return source;
}
} }
public interface IContentItemConverter : IConverter public interface IContentItemConverter : IConverter
{ {
JsonObject ConvertItem(IField field, JsonObject source); JsonObject ConvertItemBefore(IField parentField, JsonObject source, IEnumerable<IField> schema)
{
return source;
}
JsonObject ConvertItemAfter(IField parentField, JsonObject source, IEnumerable<IField> schema)
{
return source;
}
} }
public interface IContentValueConverter : IConverter public interface IContentValueConverter : IConverter

31
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveInvariant.cs → backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveFromPreviousPartitioning.cs

@ -11,22 +11,47 @@ using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Core.ConvertContent; namespace Squidex.Domain.Apps.Core.ConvertContent;
public sealed class ResolveInvariant : IContentFieldAfterConverter public sealed class ResolveFromPreviousPartitioning : IContentFieldConverter
{ {
private readonly LanguagesConfig languages; private readonly LanguagesConfig languages;
public ResolveInvariant(LanguagesConfig languages) public ResolveFromPreviousPartitioning(LanguagesConfig languages)
{ {
this.languages = languages; this.languages = languages;
} }
public ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source) public ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source)
{ {
if (!field.Partitioning.Equals(Partitioning.Invariant)) if (field.Partitioning.Equals(Partitioning.Invariant))
{
return ResolveInvariant(source);
}
else
{
return ResolveLocalized(source);
}
}
private ContentFieldData ResolveLocalized(ContentFieldData source)
{
if (source.TryGetNonNull(languages.Master, out _))
{ {
return source; return source;
} }
if (source.TryGetNonNull(InvariantPartitioning.Key, out var value))
{
source = new ContentFieldData
{
[languages.Master] = value
};
}
return source;
}
private ContentFieldData ResolveInvariant(ContentFieldData source)
{
if (source.TryGetNonNull(InvariantPartitioning.Key, out _)) if (source.TryGetNonNull(InvariantPartitioning.Key, out _))
{ {
return source; return source;

24
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveLanguages.cs

@ -12,13 +12,14 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.ConvertContent; namespace Squidex.Domain.Apps.Core.ConvertContent;
public sealed class ResolveLanguages : IContentFieldAfterConverter public sealed class ResolveLanguages : IContentFieldConverter
{ {
private readonly LanguagesConfig languages; private readonly LanguagesConfig languages;
private readonly bool resolveFallback;
private readonly HashSet<string> languageCodes; private readonly HashSet<string> languageCodes;
public ResolveLanguages(LanguagesConfig languages, bool resolveFallback = true, params Language[] filteredLanguages) public bool ResolveFallback { get; init; }
public ResolveLanguages(LanguagesConfig languages, params Language[] filteredLanguages)
{ {
this.languages = languages; this.languages = languages;
@ -35,8 +36,6 @@ public sealed class ResolveLanguages : IContentFieldAfterConverter
{ {
languageCodes.Add(languages.Master); languageCodes.Add(languages.Master);
} }
this.resolveFallback = resolveFallback;
} }
public ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source) public ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source)
@ -46,15 +45,7 @@ public sealed class ResolveLanguages : IContentFieldAfterConverter
return source; return source;
} }
if (source.TryGetNonNull(InvariantPartitioning.Key, out var value)) if (ResolveFallback)
{
source = new ContentFieldData
{
[languages.Master] = value
};
}
if (resolveFallback)
{ {
foreach (var languageCode in languageCodes) foreach (var languageCode in languageCodes)
{ {
@ -65,6 +56,11 @@ public sealed class ResolveLanguages : IContentFieldAfterConverter
foreach (var fallback in languages.GetPriorities(languageCode)) foreach (var fallback in languages.GetPriorities(languageCode))
{ {
if (fallback == languageCode)
{
continue;
}
if (source.TryGetNonNull(fallback, out var fallbackValue)) if (source.TryGetNonNull(fallback, out var fallbackValue))
{ {
source[languageCode] = fallbackValue; source[languageCode] = fallbackValue;

57
backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs

@ -1,57 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.DefaultValues;
public static class DefaultValueExtensions
{
public static void GenerateDefaultValues(this ContentData data, Schema schema, PartitionResolver partitionResolver)
{
Guard.NotNull(schema);
Guard.NotNull(partitionResolver);
foreach (var field in schema.Fields)
{
var fieldData = data.GetOrCreate(field.Name, _ => new ContentFieldData());
if (fieldData != null)
{
var partitioning = partitionResolver(field.Partitioning);
foreach (var partitionKey in partitioning.AllKeys)
{
Enrich(field, fieldData, partitionKey);
}
if (fieldData.Count > 0)
{
data[field.Name] = fieldData;
}
}
}
}
private static void Enrich(IField field, ContentFieldData fieldData, string partitionKey)
{
var defaultValue = DefaultValueFactory.CreateDefaultValue(field, SystemClock.Instance.GetCurrentInstant(), partitionKey);
if (field.RawProperties.IsRequired || defaultValue == default)
{
return;
}
if (!fieldData.TryGetValue(partitionKey, out _))
{
fieldData.AddLocalized(partitionKey, defaultValue);
}
}
}

2
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs

@ -105,7 +105,7 @@ public static class ContentReferencesExtensions
Guard.NotNull(schema); Guard.NotNull(schema);
Guard.NotNull(partitioning); Guard.NotNull(partitioning);
var result = new JsonObject(); var result = JsonValue.Object();
foreach (var partitionKey in partitioning.AllKeys) foreach (var partitionKey in partitioning.AllKeys)
{ {

4
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs

@ -38,7 +38,7 @@ public sealed partial class MongoAssetFolderRepository : MongoRepositoryBase<Mon
}, ct); }, ct);
} }
public async Task<IResultList<IAssetFolderEntity>> QueryAsync(DomainId appId, DomainId parentId, public async Task<IResultList<IAssetFolderEntity>> QueryAsync(DomainId appId, DomainId? parentId,
CancellationToken ct = default) CancellationToken ct = default)
{ {
using (Telemetry.Activities.StartActivity("MongoAssetFolderRepository/QueryAsync")) using (Telemetry.Activities.StartActivity("MongoAssetFolderRepository/QueryAsync"))
@ -53,7 +53,7 @@ public sealed partial class MongoAssetFolderRepository : MongoRepositoryBase<Mon
} }
} }
public async Task<IReadOnlyList<DomainId>> QueryChildIdsAsync(DomainId appId, DomainId parentId, public async Task<IReadOnlyList<DomainId>> QueryChildIdsAsync(DomainId appId, DomainId? parentId,
CancellationToken ct = default) CancellationToken ct = default)
{ {
using (Telemetry.Activities.StartActivity("MongoAssetFolderRepository/QueryChildIdsAsync")) using (Telemetry.Activities.StartActivity("MongoAssetFolderRepository/QueryChildIdsAsync"))

4
backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs

@ -18,7 +18,7 @@ public sealed class AppUISettings : IAppUISettings, IDeleter
[CollectionName("UISettings")] [CollectionName("UISettings")]
public sealed class State public sealed class State
{ {
public JsonObject Settings { get; set; } = new JsonObject(); public JsonObject Settings { get; set; } = JsonValue.Object();
public bool Set(JsonObject settings) public bool Set(JsonObject settings)
{ {
@ -82,7 +82,7 @@ public sealed class AppUISettings : IAppUISettings, IDeleter
{ {
if (add) if (add)
{ {
found = new JsonObject(); found = JsonValue.Object();
current[segment] = found; current[segment] = found;
} }

5
backend/src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs

@ -14,10 +14,7 @@ public interface IAssetQueryService
Task<IResultList<IEnrichedAssetEntity>> QueryAsync(Context context, DomainId? parentId, Q q, Task<IResultList<IEnrichedAssetEntity>> QueryAsync(Context context, DomainId? parentId, Q q,
CancellationToken ct = default); CancellationToken ct = default);
Task<IResultList<IAssetFolderEntity>> QueryAssetFoldersAsync(Context context, DomainId parentId, Task<IResultList<IAssetFolderEntity>> QueryAssetFoldersAsync(Context context, DomainId? parentId,
CancellationToken ct = default);
Task<IResultList<IAssetFolderEntity>> QueryAssetFoldersAsync(DomainId appId, DomainId parentId,
CancellationToken ct = default); CancellationToken ct = default);
Task<IReadOnlyList<IAssetFolderEntity>> FindAssetFolderAsync(DomainId appId, DomainId id, Task<IReadOnlyList<IAssetFolderEntity>> FindAssetFolderAsync(DomainId appId, DomainId id,

29
backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs

@ -64,20 +64,7 @@ public sealed class AssetQueryService : IAssetQueryService
} }
} }
public async Task<IResultList<IAssetFolderEntity>> QueryAssetFoldersAsync(DomainId appId, DomainId parentId, public async Task<IResultList<IAssetFolderEntity>> QueryAssetFoldersAsync(Context context, DomainId? parentId,
CancellationToken ct = default)
{
using (var activity = Telemetry.Activities.StartActivity("AssetQueryService/QueryAssetFoldersAsync"))
{
activity?.SetTag("folderId", parentId);
var assetFolders = await QueryFoldersCoreAsync(appId, parentId, ct);
return assetFolders;
}
}
public async Task<IResultList<IAssetFolderEntity>> QueryAssetFoldersAsync(Context context, DomainId parentId,
CancellationToken ct = default) CancellationToken ct = default)
{ {
using (var activity = Telemetry.Activities.StartActivity("AssetQueryService/QueryAssetFoldersAsync")) using (var activity = Telemetry.Activities.StartActivity("AssetQueryService/QueryAssetFoldersAsync"))
@ -243,7 +230,7 @@ public sealed class AssetQueryService : IAssetQueryService
} }
} }
private async Task<IResultList<IAssetFolderEntity>> QueryFoldersCoreAsync(Context context, DomainId parentId, private async Task<IResultList<IAssetFolderEntity>> QueryFoldersCoreAsync(Context context, DomainId? parentId,
CancellationToken ct) CancellationToken ct)
{ {
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct)) using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct))
@ -255,18 +242,6 @@ public sealed class AssetQueryService : IAssetQueryService
} }
} }
private async Task<IResultList<IAssetFolderEntity>> QueryFoldersCoreAsync(DomainId appId, DomainId parentId,
CancellationToken ct)
{
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
// Enforce a hard timeout
combined.CancelAfter(options.TimeoutQuery);
return await assetFolderRepository.QueryAsync(appId, parentId, combined.Token);
}
}
private async Task<IResultList<IAssetEntity>> QueryCoreAsync(Context context, DomainId? parentId, Q q, private async Task<IResultList<IAssetEntity>> QueryCoreAsync(Context context, DomainId? parentId, Q q,
CancellationToken ct) CancellationToken ct)
{ {

4
backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetFolderRepository.cs

@ -11,10 +11,10 @@ namespace Squidex.Domain.Apps.Entities.Assets.Repositories;
public interface IAssetFolderRepository public interface IAssetFolderRepository
{ {
Task<IResultList<IAssetFolderEntity>> QueryAsync(DomainId appId, DomainId parentId, Task<IResultList<IAssetFolderEntity>> QueryAsync(DomainId appId, DomainId? parentId,
CancellationToken ct = default); CancellationToken ct = default);
Task<IReadOnlyList<DomainId>> QueryChildIdsAsync(DomainId appId, DomainId parentId, Task<IReadOnlyList<DomainId>> QueryChildIdsAsync(DomainId appId, DomainId? parentId,
CancellationToken ct = default); CancellationToken ct = default);
Task<IAssetFolderEntity?> FindAssetFolderAsync(DomainId appId, DomainId id, Task<IAssetFolderEntity?> FindAssetFolderAsync(DomainId appId, DomainId id,

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

@ -246,7 +246,7 @@ public partial class ContentDomainObject : DomainObject<ContentDomainObject.Stat
c.Data = await operation.ExecuteCreateScriptAsync(c.Data, status, ct); c.Data = await operation.ExecuteCreateScriptAsync(c.Data, status, ct);
} }
operation.GenerateDefaultValues(c.Data); c.Data = operation.GenerateDefaultValues(c.Data);
if (!c.DoNotValidate) if (!c.DoNotValidate)
{ {

12
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs

@ -7,7 +7,7 @@
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.DefaultValues; using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
@ -87,9 +87,15 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors(); operation.AddErrors(validator.Errors).ThrowOnErrors();
} }
public static void GenerateDefaultValues(this ContentOperation operation, ContentData data) public static ContentData GenerateDefaultValues(this ContentOperation operation, ContentData data)
{ {
data.GenerateDefaultValues(operation.Schema.SchemaDef, operation.Partition()); var converter =
new ContentConverter(
operation.Components,
operation.Schema.SchemaDef);
converter.Add(new AddDefaultValues(operation.Partition()) { IgnoreRequiredFields = true });
return converter.Convert(data);
} }
public static async Task CheckReferrersAsync(this ContentOperation operation, public static async Task CheckReferrersAsync(this ContentOperation operation,

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedInputGraphType.cs

@ -41,7 +41,7 @@ internal sealed class NestedInputGraphType : InputObjectGraphType
public override object ParseDictionary(IDictionary<string, object?> value) public override object ParseDictionary(IDictionary<string, object?> value)
{ {
var result = new JsonObject(); var result = JsonValue.Object();
foreach (var field in Fields) foreach (var field in Fields)
{ {

4
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonGraphType.cs

@ -59,7 +59,7 @@ public sealed class JsonGraphType : JsonNoopGraphType
case GraphQLObjectValue objectValue: case GraphQLObjectValue objectValue:
{ {
var json = new JsonObject(); var json = JsonValue.Object();
if (objectValue.Fields != null) if (objectValue.Fields != null)
{ {
@ -86,7 +86,7 @@ public sealed class JsonGraphType : JsonNoopGraphType
case IDictionary<string, object> obj: case IDictionary<string, object> obj:
{ {
var json = new JsonObject(); var json = JsonValue.Object();
foreach (var (key, value) in obj) foreach (var (key, value) in obj)
{ {

17
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs

@ -126,13 +126,20 @@ public sealed class ConvertData : IContentEnricherStep
converter.Add(cleanReferences); converter.Add(cleanReferences);
} }
converter.Add(new ResolveInvariant(context.App.Languages)); converter.Add(new ResolveFromPreviousPartitioning(context.App.Languages));
if (!context.IsFrontendClient)
{
converter.Add(new AddDefaultValues(context.App.PartitionResolver()) { IgnoreNonMasterFields = true });
}
converter.Add( converter.Add(
new ResolveLanguages(context.App.Languages, new ResolveLanguages(
context.IsFrontendClient == false && context.App.Languages,
context.ShouldResolveLanguages(), context.Languages().ToArray())
context.Languages().ToArray())); {
ResolveFallback = !context.IsFrontendClient && context.ShouldResolveLanguages()
});
if (!context.IsFrontendClient) if (!context.IsFrontendClient)
{ {

11
backend/src/Squidex.Domain.Users.MongoDb/MongoUserStore.cs

@ -210,7 +210,7 @@ public sealed class MongoUserStore :
public async Task<IdentityUser?> FindByLoginAsync(string loginProvider, string providerKey, public async Task<IdentityUser?> FindByLoginAsync(string loginProvider, string providerKey,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var result = await Collection.Find(x => x.Logins.Exists(y => y.LoginProvider == loginProvider && y.ProviderKey == providerKey)).FirstOrDefaultAsync(cancellationToken); var result = await Collection.Find(Filter.ElemMatch(x => x.Logins, BuildFilter(loginProvider, providerKey))).FirstOrDefaultAsync(cancellationToken);
return result; return result;
} }
@ -650,4 +650,13 @@ public sealed class MongoUserStore :
return Task.FromResult(false); return Task.FromResult(false);
} }
private static FilterDefinition<UserLogin> BuildFilter(string loginProvider, string providerKey)
{
var filter = Builders<UserLogin>.Filter;
return filter.And(
filter.Eq(x => x.LoginProvider, loginProvider),
filter.Eq(x => x.ProviderKey, providerKey));
}
} }

2
backend/src/Squidex.Web/ErrorDto.cs

@ -14,7 +14,7 @@ public sealed class ErrorDto
{ {
[LocalizedRequired] [LocalizedRequired]
[Display(Description = "Error message.")] [Display(Description = "Error message.")]
public string? Message { get; set; } public string Message { get; set; }
[Display(Description = "The error code.")] [Display(Description = "The error code.")]
public string? ErrorCode { get; set; } public string? ErrorCode { get; set; }

13
backend/src/Squidex.Web/OpenApiRequestAttribute.cs

@ -0,0 +1,13 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Web;
[AttributeUsage(AttributeTargets.Class)]
public sealed class OpenApiRequestAttribute : Attribute
{
}

18
backend/src/Squidex/Areas/Api/Config/OpenApi/OpenApiServices.cs

@ -86,6 +86,16 @@ public static class OpenApiServices
private static void ConfigureSchemaSettings(JsonSchemaGeneratorSettings settings, TypeRegistry typeRegistry, bool flatten) private static void ConfigureSchemaSettings(JsonSchemaGeneratorSettings settings, TypeRegistry typeRegistry, bool flatten)
{ {
settings.AllowReferencesWithProperties = true;
settings.DefaultDictionaryValueReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull;
settings.DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull;
settings.FlattenInheritanceHierarchy = flatten;
settings.ReflectionService = new ReflectionServices();
settings.SchemaNameGenerator = new SchemaNameGenerator();
settings.SchemaProcessors.Add(new DiscriminatorProcessor(typeRegistry));
settings.SchemaProcessors.Add(new RequiredSchemaProcessor());
settings.SchemaType = NJsonSchema.SchemaType.OpenApi3;
settings.TypeMappers = new List<ITypeMapper> settings.TypeMappers = new List<ITypeMapper>
{ {
CreateAnyMap<FilterNode<JsonValue>>(), CreateAnyMap<FilterNode<JsonValue>>(),
@ -106,14 +116,6 @@ public static class OpenApiServices
CreateStringMap<RefToken>(), CreateStringMap<RefToken>(),
CreateStringMap<Status>(), CreateStringMap<Status>(),
}; };
settings.AllowReferencesWithProperties = true;
settings.DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull;
settings.FlattenInheritanceHierarchy = flatten;
settings.SchemaNameGenerator = new SchemaNameGenerator();
settings.SchemaProcessors.Add(new DiscriminatorProcessor(typeRegistry));
settings.SchemaType = NJsonSchema.SchemaType.OpenApi3;
settings.ReflectionService = new ReflectionServices();
} }
private static ITypeMapper CreateObjectMap<T>() private static ITypeMapper CreateObjectMap<T>()

46
backend/src/Squidex/Areas/Api/Config/OpenApi/RequiredSchemaProcessor.cs

@ -0,0 +1,46 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using NJsonSchema;
using NJsonSchema.Generation;
using Squidex.Web;
namespace Squidex.Areas.Api.Config.OpenApi;
public sealed class RequiredSchemaProcessor : ISchemaProcessor
{
public void Process(SchemaProcessorContext context)
{
if (context.ContextualType.GetAttribute<OpenApiRequestAttribute>() != null)
{
return;
}
FixRequired(context.Schema);
foreach (var schema in context.Schema.AllOf)
{
FixRequired(schema);
}
foreach (var schema in context.Schema.OneOf)
{
FixRequired(schema);
}
static void FixRequired(JsonSchema schema)
{
foreach (var property in schema.Properties.Values)
{
if (!property.IsNullable(SchemaType.OpenApi3))
{
property.IsRequired = true;
}
}
}
}
}

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

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class AddLanguageDto public sealed class AddLanguageDto
{ {
/// <summary> /// <summary>

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

@ -7,9 +7,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class AddRoleDto public sealed class AddRoleDto
{ {
/// <summary> /// <summary>

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

@ -7,9 +7,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class AddWorkflowDto public sealed class AddWorkflowDto
{ {
/// <summary> /// <summary>

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

@ -36,7 +36,6 @@ public sealed class AppDto : Resource
/// <summary> /// <summary>
/// The name of the app. /// The name of the app.
/// </summary> /// </summary>
[LocalizedRequired]
[LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")] [LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string Name { get; set; } public string Name { get; set; }
@ -94,7 +93,6 @@ public sealed class AppDto : Resource
/// <summary> /// <summary>
/// The properties from the role. /// The properties from the role.
/// </summary> /// </summary>
[LocalizedRequired]
public JsonObject RoleProperties { get; set; } public JsonObject RoleProperties { get; set; }
public static AppDto FromDomain(IAppEntity app, string userId, bool isFrontend, Resources resources) public static AppDto FromDomain(IAppEntity app, string userId, bool isFrontend, Resources resources)
@ -125,7 +123,7 @@ public sealed class AppDto : Resource
} }
else else
{ {
result.RoleProperties = new JsonObject(); result.RoleProperties = JsonValue.Object();
} }
foreach (var (key, value) in resources.Context.UserPrincipal.Claims.GetUIProperties(app.Name)) foreach (var (key, value) in resources.Context.UserPrincipal.Claims.GetUIProperties(app.Name))

3
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs

@ -18,19 +18,16 @@ public sealed class AppLanguageDto : Resource
/// <summary> /// <summary>
/// The iso code of the language. /// The iso code of the language.
/// </summary> /// </summary>
[LocalizedRequired]
public string Iso2Code { get; set; } public string Iso2Code { get; set; }
/// <summary> /// <summary>
/// The english name of the language. /// The english name of the language.
/// </summary> /// </summary>
[LocalizedRequired]
public string EnglishName { get; set; } public string EnglishName { get; set; }
/// <summary> /// <summary>
/// The fallback languages. /// The fallback languages.
/// </summary> /// </summary>
[LocalizedRequired]
public Language[] Fallback { get; set; } public Language[] Fallback { get; set; }
/// <summary> /// <summary>

1
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs

@ -16,7 +16,6 @@ public sealed class AppLanguagesDto : Resource
/// <summary> /// <summary>
/// The languages. /// The languages.
/// </summary> /// </summary>
[LocalizedRequired]
public AppLanguageDto[] Items { get; set; } public AppLanguageDto[] Items { get; set; }
public static AppLanguagesDto FromDomain(IAppEntity app, Resources resources) public static AppLanguagesDto FromDomain(IAppEntity app, Resources resources)

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

@ -16,13 +16,11 @@ public sealed class AppSettingsDto : Resource
/// <summary> /// <summary>
/// The configured app patterns. /// The configured app patterns.
/// </summary> /// </summary>
[LocalizedRequired]
public PatternDto[] Patterns { get; set; } public PatternDto[] Patterns { get; set; }
/// <summary> /// <summary>
/// The configured UI editors. /// The configured UI editors.
/// </summary> /// </summary>
[LocalizedRequired]
public EditorDto[] Editors { get; set; } public EditorDto[] Editors { get; set; }
/// <summary> /// <summary>

4
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs

@ -7,7 +7,6 @@
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
using Squidex.Web; using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
@ -17,19 +16,16 @@ public sealed class ClientDto : Resource
/// <summary> /// <summary>
/// The client id. /// The client id.
/// </summary> /// </summary>
[LocalizedRequired]
public string Id { get; set; } public string Id { get; set; }
/// <summary> /// <summary>
/// The client secret. /// The client secret.
/// </summary> /// </summary>
[LocalizedRequired]
public string Secret { get; set; } public string Secret { get; set; }
/// <summary> /// <summary>
/// The client name. /// The client name.
/// </summary> /// </summary>
[LocalizedRequired]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>

1
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientsDto.cs

@ -16,7 +16,6 @@ public sealed class ClientsDto : Resource
/// <summary> /// <summary>
/// The clients. /// The clients.
/// </summary> /// </summary>
[LocalizedRequired]
public ClientDto[] Items { get; set; } public ClientDto[] Items { get; set; }
public static ClientsDto FromApp(IAppEntity app, Resources resources) public static ClientsDto FromApp(IAppEntity app, Resources resources)

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

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class CreateAppDto public sealed class CreateAppDto
{ {
/// <summary> /// <summary>

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

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class CreateClientDto public sealed class CreateClientDto
{ {
/// <summary> /// <summary>

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

@ -16,13 +16,11 @@ public sealed class EditorDto
/// <summary> /// <summary>
/// The name of the editor. /// The name of the editor.
/// </summary> /// </summary>
[LocalizedRequired]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// The url to the editor. /// The url to the editor.
/// </summary> /// </summary>
[LocalizedRequired]
public string Url { get; set; } public string Url { get; set; }
public static EditorDto FromDomain(Editor editor) public static EditorDto FromDomain(Editor editor)

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

@ -16,13 +16,11 @@ public sealed class PatternDto
/// <summary> /// <summary>
/// The name of the suggestion. /// The name of the suggestion.
/// </summary> /// </summary>
[LocalizedRequired]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// The regex pattern. /// The regex pattern.
/// </summary> /// </summary>
[LocalizedRequired]
public string Regex { get; set; } public string Regex { get; set; }
/// <summary> /// <summary>

4
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs

@ -8,7 +8,6 @@
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Validation;
using Squidex.Web; using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
@ -18,7 +17,6 @@ public sealed class RoleDto : Resource
/// <summary> /// <summary>
/// The role name. /// The role name.
/// </summary> /// </summary>
[LocalizedRequired]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
@ -39,13 +37,11 @@ public sealed class RoleDto : Resource
/// <summary> /// <summary>
/// Associated list of permissions. /// Associated list of permissions.
/// </summary> /// </summary>
[LocalizedRequired]
public string[] Permissions { get; set; } public string[] Permissions { get; set; }
/// <summary> /// <summary>
/// Associated list of UI properties. /// Associated list of UI properties.
/// </summary> /// </summary>
[LocalizedRequired]
public JsonObject Properties { get; set; } public JsonObject Properties { get; set; }
public static RoleDto FromDomain(Role role, IAppEntity app) public static RoleDto FromDomain(Role role, IAppEntity app)

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

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure.Validation;
using Squidex.Web; using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
@ -16,7 +15,6 @@ public sealed class RolesDto : Resource
/// <summary> /// <summary>
/// The roles. /// The roles.
/// </summary> /// </summary>
[LocalizedRequired]
public RoleDto[] Items { get; set; } public RoleDto[] Items { get; set; }
public static RolesDto FromDomain(IAppEntity app, Resources resources) public static RolesDto FromDomain(IAppEntity app, Resources resources)

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

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class TransferToTeamDto public sealed class TransferToTeamDto
{ {
/// <summary> /// <summary>

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

@ -7,9 +7,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class UpdateAppDto public sealed class UpdateAppDto
{ {
/// <summary> /// <summary>

7
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs

@ -9,21 +9,24 @@ using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class UpdateAppSettingsDto public sealed class UpdateAppSettingsDto
{ {
/// <summary> /// <summary>
/// The configured app patterns. /// The configured app patterns.
/// </summary> /// </summary>
[Required] [LocalizedRequired]
public PatternDto[] Patterns { get; set; } public PatternDto[] Patterns { get; set; }
/// <summary> /// <summary>
/// The configured UI editors. /// The configured UI editors.
/// </summary> /// </summary>
[Required] [LocalizedRequired]
public EditorDto[] Editors { get; set; } public EditorDto[] Editors { get; set; }
/// <summary> /// <summary>

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

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Core.Assets; using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class UpdateAssetScriptsDto public sealed class UpdateAssetScriptsDto
{ {
/// <summary> /// <summary>

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

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class UpdateClientDto public sealed class UpdateClientDto
{ {
/// <summary> /// <summary>

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

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class UpdateLanguageDto public sealed class UpdateLanguageDto
{ {
/// <summary> /// <summary>

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

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class UpdateRoleDto public sealed class UpdateRoleDto
{ {
/// <summary> /// <summary>

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

@ -10,9 +10,11 @@ using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class UpdateWorkflowDto public sealed class UpdateWorkflowDto
{ {
/// <summary> /// <summary>

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

@ -14,6 +14,7 @@ using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class WorkflowDto : Resource public sealed class WorkflowDto : Resource
{ {
/// <summary> /// <summary>
@ -29,7 +30,6 @@ public sealed class WorkflowDto : Resource
/// <summary> /// <summary>
/// The workflow steps. /// The workflow steps.
/// </summary> /// </summary>
[LocalizedRequired]
public Dictionary<Status, WorkflowStepDto> Steps { get; set; } public Dictionary<Status, WorkflowStepDto> Steps { get; set; }
/// <summary> /// <summary>

3
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs

@ -9,16 +9,17 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
using NoUpdateType = Squidex.Domain.Apps.Core.Contents.NoUpdate; using NoUpdateType = Squidex.Domain.Apps.Core.Contents.NoUpdate;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
[OpenApiRequest]
public sealed class WorkflowStepDto public sealed class WorkflowStepDto
{ {
/// <summary> /// <summary>
/// The transitions. /// The transitions.
/// </summary> /// </summary>
[LocalizedRequired]
public Dictionary<Status, WorkflowTransitionDto> Transitions { get; set; } public Dictionary<Status, WorkflowTransitionDto> Transitions { get; set; }
/// <summary> /// <summary>

3
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs

@ -7,7 +7,6 @@
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Infrastructure.Validation;
using Squidex.Web; using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models; namespace Squidex.Areas.Api.Controllers.Apps.Models;
@ -17,13 +16,11 @@ public sealed class WorkflowsDto : Resource
/// <summary> /// <summary>
/// The workflow. /// The workflow.
/// </summary> /// </summary>
[LocalizedRequired]
public WorkflowDto[] Items { get; set; } public WorkflowDto[] Items { get; set; }
/// <summary> /// <summary>
/// The errros that should be fixed. /// The errros that should be fixed.
/// </summary> /// </summary>
[LocalizedRequired]
public string[] Errors { get; set; } public string[] Errors { get; set; }
public static async Task<WorkflowsDto> FromAppAsync(IWorkflowsValidator workflowsValidator, IAppEntity app, Resources resources) public static async Task<WorkflowsDto> FromAppAsync(IWorkflowsValidator workflowsValidator, IAppEntity app, Resources resources)

46
backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs

@ -49,11 +49,31 @@ public sealed class AssetFoldersController : ApiController
[ProducesResponseType(typeof(AssetFoldersDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(AssetFoldersDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(PermissionIds.AppAssetsRead)] [ApiPermissionOrAnonymous(PermissionIds.AppAssetsRead)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> GetAssetFolders(string app, [FromQuery] DomainId parentId, [FromQuery] AssetFolderScope scope = AssetFolderScope.PathAndItems) public async Task<IActionResult> GetAssetFolders(string app, [FromQuery] DomainId? parentId, [FromQuery] AssetFolderScope scope = AssetFolderScope.PathAndItems)
{ {
Task<IReadOnlyList<IAssetFolderEntity>> GetAssetPathAsync()
{
if (scope == AssetFolderScope.Items || parentId == null)
{
return Task.FromResult<IReadOnlyList<IAssetFolderEntity>>(ReadonlyList.Empty<IAssetFolderEntity>());
}
return assetQuery.FindAssetFolderAsync(Context.App.Id, parentId.Value, HttpContext.RequestAborted);
}
Task<IResultList<IAssetFolderEntity>> GetAssetFoldersAsync()
{
if (scope == AssetFolderScope.Path)
{
return Task.FromResult(ResultList.Empty<IAssetFolderEntity>());
}
return assetQuery.QueryAssetFoldersAsync(Context, parentId, HttpContext.RequestAborted);
}
var (folders, path) = await AsyncHelper.WhenAll( var (folders, path) = await AsyncHelper.WhenAll(
GetAssetFoldersAsync(parentId, scope), GetAssetFoldersAsync(),
GetAssetPathAsync(parentId, scope)); GetAssetPathAsync());
var response = Deferred.Response(() => var response = Deferred.Response(() =>
{ {
@ -163,24 +183,4 @@ public sealed class AssetFoldersController : ApiController
return AssetFolderDto.FromDomain(context.Result<IAssetFolderEntity>(), Resources); return AssetFolderDto.FromDomain(context.Result<IAssetFolderEntity>(), Resources);
} }
private Task<IReadOnlyList<IAssetFolderEntity>> GetAssetPathAsync(DomainId parentId, AssetFolderScope scope)
{
if (scope == AssetFolderScope.Items)
{
return Task.FromResult<IReadOnlyList<IAssetFolderEntity>>(ReadonlyList.Empty<IAssetFolderEntity>());
}
return assetQuery.FindAssetFolderAsync(Context.App.Id, parentId, HttpContext.RequestAborted);
}
private Task<IResultList<IAssetFolderEntity>> GetAssetFoldersAsync(DomainId parentId, AssetFolderScope scope)
{
if (scope == AssetFolderScope.Path)
{
return Task.FromResult(ResultList.Empty<IAssetFolderEntity>());
}
return assetQuery.QueryAssetFoldersAsync(Context, parentId, HttpContext.RequestAborted);
}
} }

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AnnotateAssetDto.cs

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class AnnotateAssetDto public sealed class AnnotateAssetDto
{ {
/// <summary> /// <summary>

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

@ -31,7 +31,6 @@ public sealed class AssetDto : Resource
/// <summary> /// <summary>
/// The file name. /// The file name.
/// </summary> /// </summary>
[LocalizedRequired]
public string FileName { get; set; } public string FileName { get; set; }
/// <summary> /// <summary>
@ -47,25 +46,21 @@ public sealed class AssetDto : Resource
/// <summary> /// <summary>
/// The slug. /// The slug.
/// </summary> /// </summary>
[LocalizedRequired]
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The mime type. /// The mime type.
/// </summary> /// </summary>
[LocalizedRequired]
public string MimeType { get; set; } public string MimeType { get; set; }
/// <summary> /// <summary>
/// The file type. /// The file type.
/// </summary> /// </summary>
[LocalizedRequired]
public string FileType { get; set; } public string FileType { get; set; }
/// <summary> /// <summary>
/// The formatted text representation of the metadata. /// The formatted text representation of the metadata.
/// </summary> /// </summary>
[LocalizedRequired]
public string MetadataText { get; set; } public string MetadataText { get; set; }
/// <summary> /// <summary>
@ -76,13 +71,11 @@ public sealed class AssetDto : Resource
/// <summary> /// <summary>
/// The asset metadata. /// The asset metadata.
/// </summary> /// </summary>
[LocalizedRequired]
public AssetMetadata Metadata { get; set; } public AssetMetadata Metadata { get; set; }
/// <summary> /// <summary>
/// The asset tags. /// The asset tags.
/// </summary> /// </summary>
[LocalizedRequired]
public HashSet<string>? Tags { get; set; } public HashSet<string>? Tags { get; set; }
/// <summary> /// <summary>
@ -103,13 +96,11 @@ public sealed class AssetDto : Resource
/// <summary> /// <summary>
/// The user that has created the schema. /// The user that has created the schema.
/// </summary> /// </summary>
[LocalizedRequired]
public RefToken CreatedBy { get; set; } public RefToken CreatedBy { get; set; }
/// <summary> /// <summary>
/// The user that has updated the asset. /// The user that has updated the asset.
/// </summary> /// </summary>
[LocalizedRequired]
public RefToken LastModifiedBy { get; set; } public RefToken LastModifiedBy { get; set; }
/// <summary> /// <summary>

1
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderDto.cs

@ -28,7 +28,6 @@ public sealed class AssetFolderDto : Resource
/// <summary> /// <summary>
/// The folder name. /// The folder name.
/// </summary> /// </summary>
[LocalizedRequired]
public string FolderName { get; set; } public string FolderName { get; set; }
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFoldersDto.cs

@ -22,13 +22,11 @@ public sealed class AssetFoldersDto : Resource
/// <summary> /// <summary>
/// The assets folders. /// The assets folders.
/// </summary> /// </summary>
[LocalizedRequired]
public AssetFolderDto[] Items { get; set; } public AssetFolderDto[] Items { get; set; }
/// <summary> /// <summary>
/// The path to the current folder. /// The path to the current folder.
/// </summary> /// </summary>
[LocalizedRequired]
public AssetFolderDto[] Path { get; set; } public AssetFolderDto[] Path { get; set; }
public static AssetFoldersDto FromDomain(IResultList<IAssetFolderEntity> assetFolders, IEnumerable<IAssetFolderEntity> path, Resources resources) public static AssetFoldersDto FromDomain(IResultList<IAssetFolderEntity> assetFolders, IEnumerable<IAssetFolderEntity> path, Resources resources)

1
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs

@ -22,7 +22,6 @@ public sealed class AssetsDto : Resource
/// <summary> /// <summary>
/// The assets. /// The assets.
/// </summary> /// </summary>
[LocalizedRequired]
public AssetDto[] Items { get; set; } public AssetDto[] Items { get; set; }
public static AssetsDto FromDomain(IResultList<IEnrichedAssetEntity> assets, Resources resources) public static AssetsDto FromDomain(IResultList<IEnrichedAssetEntity> assets, Resources resources)

3
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/BulkUpdateAssetsDto.cs

@ -8,15 +8,16 @@
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class BulkUpdateAssetsDto public sealed class BulkUpdateAssetsDto
{ {
/// <summary> /// <summary>
/// The contents to update or insert. /// The contents to update or insert.
/// </summary> /// </summary>
[LocalizedRequired]
public BulkUpdateAssetsJobDto[]? Jobs { get; set; } public BulkUpdateAssetsJobDto[]? Jobs { get; set; }
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/BulkUpdateAssetsJobDto.cs

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public class BulkUpdateAssetsJobDto public class BulkUpdateAssetsJobDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/CreateAssetDto.cs

@ -10,9 +10,11 @@ using Squidex.Assets;
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class CreateAssetDto public sealed class CreateAssetDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/CreateAssetFolderDto.cs

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class CreateAssetFolderDto public sealed class CreateAssetFolderDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/DeleteAssetDto.cs

@ -9,9 +9,11 @@ using Microsoft.AspNetCore.Mvc;
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class DeleteAssetDto public sealed class DeleteAssetDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/MoveAssetDto.cs

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class MoveAssetDto public sealed class MoveAssetDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/MoveAssetFolderDto.cs

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class MoveAssetFolderDto public sealed class MoveAssetFolderDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/RenameAssetFolderDto.cs

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class RenameAssetFolderDto public sealed class RenameAssetFolderDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/UpsertAssetDto.cs

@ -10,9 +10,11 @@ using Squidex.Assets;
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models; namespace Squidex.Areas.Api.Controllers.Assets.Models;
[OpenApiRequest]
public sealed class UpsertAssetDto public sealed class UpsertAssetDto
{ {
/// <summary> /// <summary>

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

@ -6,10 +6,12 @@
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
using Roles = Squidex.Domain.Apps.Core.Apps.Role; using Roles = Squidex.Domain.Apps.Core.Apps.Role;
namespace Squidex.Areas.Api.Controllers; namespace Squidex.Areas.Api.Controllers;
[OpenApiRequest]
public sealed class AssignContributorDto public sealed class AssignContributorDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobsDto.cs

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Entities.Backup; using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Infrastructure.Validation;
using Squidex.Web; using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Backups.Models; namespace Squidex.Areas.Api.Controllers.Backups.Models;
@ -16,7 +15,6 @@ public sealed class BackupJobsDto : Resource
/// <summary> /// <summary>
/// The backups. /// The backups.
/// </summary> /// </summary>
[LocalizedRequired]
public BackupJobDto[] Items { get; set; } public BackupJobDto[] Items { get; set; }
public static BackupJobsDto FromDomain(IEnumerable<IBackupJob> backups, Resources resources) public static BackupJobsDto FromDomain(IEnumerable<IBackupJob> backups, Resources resources)

3
backend/src/Squidex/Areas/Api/Controllers/Backups/Models/RestoreJobDto.cs

@ -9,6 +9,7 @@ using NodaTime;
using Squidex.Domain.Apps.Entities.Backup; using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Backups.Models; namespace Squidex.Areas.Api.Controllers.Backups.Models;
@ -17,13 +18,11 @@ public sealed class RestoreJobDto
/// <summary> /// <summary>
/// The uri to load from. /// The uri to load from.
/// </summary> /// </summary>
[LocalizedRequired]
public Uri Url { get; set; } public Uri Url { get; set; }
/// <summary> /// <summary>
/// The status log. /// The status log.
/// </summary> /// </summary>
[LocalizedRequired]
public List<string> Log { get; set; } public List<string> Log { get; set; }
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Backups/Models/RestoreRequestDto.cs

@ -6,9 +6,11 @@
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Backups.Models; namespace Squidex.Areas.Api.Controllers.Backups.Models;
[OpenApiRequest]
public sealed class RestoreRequestDto public sealed class RestoreRequestDto
{ {
/// <summary> /// <summary>

4
backend/src/Squidex/Areas/Api/Controllers/Comments/Models/CommentDto.cs

@ -10,7 +10,6 @@ using Squidex.Domain.Apps.Core.Comments;
using Squidex.Domain.Apps.Entities.Comments.Commands; using Squidex.Domain.Apps.Entities.Comments.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
namespace Squidex.Areas.Api.Controllers.Comments.Models; namespace Squidex.Areas.Api.Controllers.Comments.Models;
@ -24,19 +23,16 @@ public sealed class CommentDto
/// <summary> /// <summary>
/// The time when the comment was created or updated last. /// The time when the comment was created or updated last.
/// </summary> /// </summary>
[LocalizedRequired]
public Instant Time { get; set; } public Instant Time { get; set; }
/// <summary> /// <summary>
/// The user who created or updated the comment. /// The user who created or updated the comment.
/// </summary> /// </summary>
[LocalizedRequired]
public RefToken User { get; set; } public RefToken User { get; set; }
/// <summary> /// <summary>
/// The text of the comment. /// The text of the comment.
/// </summary> /// </summary>
[LocalizedRequired]
public string Text { get; set; } public string Text { get; set; }
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Comments/Models/UpsertCommentDto.cs

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Entities.Comments.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Comments.Models; namespace Squidex.Areas.Api.Controllers.Comments.Models;
[OpenApiRequest]
public sealed class UpsertCommentDto public sealed class UpsertCommentDto
{ {
/// <summary> /// <summary>

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

@ -21,6 +21,7 @@ using Squidex.Web.Pipeline;
namespace Squidex.Areas.Api.Controllers.Contents; namespace Squidex.Areas.Api.Controllers.Contents;
[SchemaMustBePublished] [SchemaMustBePublished]
[ApiExplorerSettings(GroupName = nameof(Contents))]
public sealed class ContentsController : ApiController public sealed class ContentsController : ApiController
{ {
private readonly IContentQueryService contentQuery; private readonly IContentQueryService contentQuery;

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

@ -11,9 +11,11 @@ using Squidex.Domain.Apps.Entities;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents.Models; namespace Squidex.Areas.Api.Controllers.Contents.Models;
[OpenApiRequest]
public sealed class AllContentsByGetDto public sealed class AllContentsByGetDto
{ {
/// <summary> /// <summary>

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

@ -12,9 +12,11 @@ using Squidex.Domain.Apps.Entities;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents.Models; namespace Squidex.Areas.Api.Controllers.Contents.Models;
[OpenApiRequest]
public sealed class AllContentsByPostDto public sealed class AllContentsByPostDto
{ {
/// <summary> /// <summary>

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

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents.Models; namespace Squidex.Areas.Api.Controllers.Contents.Models;
[OpenApiRequest]
public sealed class BulkUpdateContentsDto public sealed class BulkUpdateContentsDto
{ {
/// <summary> /// <summary>

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

@ -10,9 +10,11 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents.Models; namespace Squidex.Areas.Api.Controllers.Contents.Models;
[OpenApiRequest]
public class BulkUpdateContentsJobDto public class BulkUpdateContentsJobDto
{ {
/// <summary> /// <summary>

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

@ -11,9 +11,11 @@ using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents.Models; namespace Squidex.Areas.Api.Controllers.Contents.Models;
[OpenApiRequest]
public sealed class ChangeStatusDto public sealed class ChangeStatusDto
{ {
/// <summary> /// <summary>

3
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs

@ -27,19 +27,16 @@ public sealed class ContentDto : Resource
/// <summary> /// <summary>
/// The user that has created the content item. /// The user that has created the content item.
/// </summary> /// </summary>
[LocalizedRequired]
public RefToken CreatedBy { get; set; } public RefToken CreatedBy { get; set; }
/// <summary> /// <summary>
/// The user that has updated the content item. /// The user that has updated the content item.
/// </summary> /// </summary>
[LocalizedRequired]
public RefToken LastModifiedBy { get; set; } public RefToken LastModifiedBy { get; set; }
/// <summary> /// <summary>
/// The data of the content item. /// The data of the content item.
/// </summary> /// </summary>
[LocalizedRequired]
public object Data { get; set; } public object Data { get; set; }
/// <summary> /// <summary>

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

@ -23,13 +23,11 @@ public sealed class ContentsDto : Resource
/// <summary> /// <summary>
/// The content items. /// The content items.
/// </summary> /// </summary>
[LocalizedRequired]
public ContentDto[] Items { get; set; } public ContentDto[] Items { get; set; }
/// <summary> /// <summary>
/// The possible statuses. /// The possible statuses.
/// </summary> /// </summary>
[LocalizedRequired]
public StatusInfoDto[] Statuses { get; set; } public StatusInfoDto[] Statuses { get; set; }
public static async Task<ContentsDto> FromContentsAsync(IResultList<IEnrichedContentEntity> contents, Resources resources, public static async Task<ContentsDto> FromContentsAsync(IResultList<IEnrichedContentEntity> contents, Resources resources,

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

@ -9,9 +9,11 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents.Models; namespace Squidex.Areas.Api.Controllers.Contents.Models;
[OpenApiRequest]
public sealed class ImportContentsDto public sealed class ImportContentsDto
{ {
/// <summary> /// <summary>

1
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ScheduleJobDto.cs

@ -37,6 +37,5 @@ public sealed class ScheduleJobDto
/// <summary> /// <summary>
/// The user who schedule the content. /// The user who schedule the content.
/// </summary> /// </summary>
[LocalizedRequired]
public RefToken ScheduledBy { get; set; } public RefToken ScheduledBy { get; set; }
} }

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

@ -15,13 +15,11 @@ public sealed class StatusInfoDto
/// <summary> /// <summary>
/// The name of the status. /// The name of the status.
/// </summary> /// </summary>
[LocalizedRequired]
public Status Status { get; set; } public Status Status { get; set; }
/// <summary> /// <summary>
/// The color of the status. /// The color of the status.
/// </summary> /// </summary>
[LocalizedRequired]
public string Color { get; set; } public string Color { get; set; }
public static StatusInfoDto FromDomain(StatusInfo statusInfo) public static StatusInfoDto FromDomain(StatusInfo statusInfo)

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

@ -10,10 +10,12 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Web;
using StatusType = Squidex.Domain.Apps.Core.Contents.Status; using StatusType = Squidex.Domain.Apps.Core.Contents.Status;
namespace Squidex.Areas.Api.Controllers.Contents.Models; namespace Squidex.Areas.Api.Controllers.Contents.Models;
[OpenApiRequest]
public class UpsertContentDto public class UpsertContentDto
{ {
/// <summary> /// <summary>

3
backend/src/Squidex/Areas/Api/Controllers/ContributorDto.cs

@ -20,19 +20,16 @@ public sealed class ContributorDto : Resource
/// <summary> /// <summary>
/// The ID of the user that contributes to the app. /// The ID of the user that contributes to the app.
/// </summary> /// </summary>
[LocalizedRequired]
public string ContributorId { get; set; } public string ContributorId { get; set; }
/// <summary> /// <summary>
/// The display name. /// The display name.
/// </summary> /// </summary>
[LocalizedRequired]
public string ContributorName { get; set; } public string ContributorName { get; set; }
/// <summary> /// <summary>
/// The email address. /// The email address.
/// </summary> /// </summary>
[LocalizedRequired]
public string ContributorEmail { get; set; } public string ContributorEmail { get; set; }
/// <summary> /// <summary>

1
backend/src/Squidex/Areas/Api/Controllers/ContributorsDto.cs

@ -22,7 +22,6 @@ public sealed class ContributorsDto : Resource
/// <summary> /// <summary>
/// The contributors. /// The contributors.
/// </summary> /// </summary>
[LocalizedRequired]
public ContributorDto[] Items { get; set; } public ContributorDto[] Items { get; set; }
/// <summary> /// <summary>

3
backend/src/Squidex/Areas/Api/Controllers/History/Models/HistoryEventDto.cs

@ -18,19 +18,16 @@ public sealed class HistoryEventDto
/// <summary> /// <summary>
/// The message for the event. /// The message for the event.
/// </summary> /// </summary>
[LocalizedRequired]
public string Message { get; set; } public string Message { get; set; }
/// <summary> /// <summary>
/// The type of the original event. /// The type of the original event.
/// </summary> /// </summary>
[LocalizedRequired]
public string EventType { get; set; } public string EventType { get; set; }
/// <summary> /// <summary>
/// The user who called the action. /// The user who called the action.
/// </summary> /// </summary>
[LocalizedRequired]
public string Actor { get; set; } public string Actor { get; set; }
/// <summary> /// <summary>

3
backend/src/Squidex/Areas/Api/Controllers/LanguageDto.cs

@ -16,19 +16,16 @@ public sealed class LanguageDto
/// <summary> /// <summary>
/// The iso code of the language. /// The iso code of the language.
/// </summary> /// </summary>
[LocalizedRequired]
public string Iso2Code { get; set; } public string Iso2Code { get; set; }
/// <summary> /// <summary>
/// The english name of the language. /// The english name of the language.
/// </summary> /// </summary>
[LocalizedRequired]
public string EnglishName { get; set; } public string EnglishName { get; set; }
/// <summary> /// <summary>
/// The native name of the language. /// The native name of the language.
/// </summary> /// </summary>
[LocalizedRequired]
public string NativeName { get; set; } public string NativeName { get; set; }
public static LanguageDto FromDomain(Language language) public static LanguageDto FromDomain(Language language)

2
backend/src/Squidex/Areas/Api/Controllers/News/Models/FeatureDto.cs

@ -14,12 +14,10 @@ public sealed class FeatureDto
/// <summary> /// <summary>
/// The name of the feature. /// The name of the feature.
/// </summary> /// </summary>
[LocalizedRequired]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// The description text. /// The description text.
/// </summary> /// </summary>
[LocalizedRequired]
public string Text { get; set; } public string Text { get; set; }
} }

1
backend/src/Squidex/Areas/Api/Controllers/News/Models/FeaturesDto.cs

@ -14,7 +14,6 @@ public class FeaturesDto
/// <summary> /// <summary>
/// The latest features. /// The latest features.
/// </summary> /// </summary>
[LocalizedRequired]
public List<FeatureDto> Features { get; } = new List<FeatureDto>(); public List<FeatureDto> Features { get; } = new List<FeatureDto>();
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Plans/Models/ChangePlanDto.cs

@ -6,9 +6,11 @@
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Plans.Models; namespace Squidex.Areas.Api.Controllers.Plans.Models;
[OpenApiRequest]
public sealed class ChangePlanDto public sealed class ChangePlanDto
{ {
/// <summary> /// <summary>

3
backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlanDto.cs

@ -16,19 +16,16 @@ public sealed class PlanDto
/// <summary> /// <summary>
/// The ID of the plan. /// The ID of the plan.
/// </summary> /// </summary>
[LocalizedRequired]
public string Id { get; set; } public string Id { get; set; }
/// <summary> /// <summary>
/// The name of the plan. /// The name of the plan.
/// </summary> /// </summary>
[LocalizedRequired]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// The monthly costs of the plan. /// The monthly costs of the plan.
/// </summary> /// </summary>
[LocalizedRequired]
public string Costs { get; set; } public string Costs { get; set; }
/// <summary> /// <summary>

1
backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansDto.cs

@ -15,7 +15,6 @@ public sealed class PlansDto
/// <summary> /// <summary>
/// The available plans. /// The available plans.
/// </summary> /// </summary>
[LocalizedRequired]
public PlanDto[] Plans { get; set; } public PlanDto[] Plans { get; set; }
/// <summary> /// <summary>

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

@ -6,9 +6,11 @@
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers; namespace Squidex.Areas.Api.Controllers;
[OpenApiRequest]
public sealed class RenameTagDto public sealed class RenameTagDto
{ {
/// <summary> /// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Rules/Models/CreateRuleDto.cs

@ -8,9 +8,11 @@
using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Rules.Models; namespace Squidex.Areas.Api.Controllers.Rules.Models;
[OpenApiRequest]
public sealed class CreateRuleDto public sealed class CreateRuleDto
{ {
/// <summary> /// <summary>

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

Loading…
Cancel
Save