diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/App_Backup.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/App_Backup.cs index 6295325d..c581a312 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/App_Backup.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/App_Backup.cs @@ -53,7 +53,7 @@ public async Task Create(CreateArguments arguments) while (!tcs.Token.IsCancellationRequested) { var backups = await session.Backups.GetBackupsAsync(session.App, tcs.Token); - var backup = backups.Items.FirstOrDefault(x => x.Started >= backupStarted); + var backup = backups.Items.Find(x => x.Started >= backupStarted); if (backup?.Stopped != null) { @@ -97,7 +97,7 @@ public async Task Create(CreateArguments arguments) } [Validator(typeof(Validator))] - public sealed class CreateArguments: AppArguments + public sealed class CreateArguments : AppArguments { [Operand(Name = "file", Description = "The target file.")] public string File { get; set; } diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/ConsoleLogger.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/ConsoleLogger.cs index a954f67b..3b8ae81f 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/ConsoleLogger.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/ConsoleLogger.cs @@ -37,7 +37,9 @@ public void StepStart(string message) { if (message.Length > MaxActionLength - 3) { - message = message.Substring(0, MaxActionLength - 3); + var length = MaxActionLength - 3; + + message = message[..length]; } message += "..."; diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/App/AppSynchronizer.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/App/AppSynchronizer.cs index 973f583e..46434bf6 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/App/AppSynchronizer.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/App/AppSynchronizer.cs @@ -143,7 +143,7 @@ await log.DoSafeAsync($"Client '{client.Id}' deleting", async () => foreach (var (clientId, _) in model.Clients) { - var existing = current.Items.FirstOrDefault(x => x.Id == clientId); + var existing = current.Items.Find(x => x.Id == clientId); if (existing != null) { @@ -160,7 +160,7 @@ await log.DoSafeAsync($"Client '{clientId}' creating", async () => foreach (var (clientId, value) in model.Clients) { - var existing = current.Items.FirstOrDefault(x => x.Id == clientId); + var existing = current.Items.Find(x => x.Id == clientId); if (existing == null || value.JsonEquals(existing)) { @@ -198,7 +198,7 @@ await log.DoSafeAsync($"Language '{language.Iso2Code}' deleting", async () => foreach (var (isoCode, _) in model.Languages) { - var existing = current.Items.FirstOrDefault(x => x.Iso2Code == isoCode); + var existing = current.Items.Find(x => x.Iso2Code == isoCode); if (existing != null) { @@ -215,7 +215,7 @@ await log.DoSafeAsync($"Language '{isoCode}' creating", async () => foreach (var (isoCode, value) in model.Languages) { - var existing = current.Items.FirstOrDefault(x => x.Iso2Code == isoCode); + var existing = current.Items.Find(x => x.Iso2Code == isoCode); if (existing == null || value.JsonEquals(existing)) { @@ -256,7 +256,7 @@ await log.DoSafeAsync($"Role '{role.Name}' deleting", async () => foreach (var (roleName, _) in model.Roles) { - var existing = current.Items.FirstOrDefault(x => x.Name == roleName); + var existing = current.Items.Find(x => x.Name == roleName); if (existing != null) { @@ -273,7 +273,7 @@ await log.DoSafeAsync($"Role '{roleName}' creating", async () => foreach (var (roleName, value) in model.Roles) { - var existing = current.Items.FirstOrDefault(x => x.Name == roleName); + var existing = current.Items.Find(x => x.Name == roleName); if (existing == null || value.JsonEquals(existing)) { diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/ContentsSynchronizer.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/ContentsSynchronizer.cs index 1b9b446f..aa383493 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/ContentsSynchronizer.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/ContentsSynchronizer.cs @@ -40,6 +40,7 @@ public Task CleanupAsync(IFileSystem fs) public async Task ExportAsync(ISyncService sync, SyncOptions options, ISession session) { var schemas = await session.Schemas.GetSchemasAsync(session.App); + var schemaMap = schemas.Items.ToDictionary(x => x.Id, x => x.Name); var context = QueryContext.Default.Unpublished().IgnoreFallback(); @@ -65,6 +66,8 @@ Task SaveAsync() await client.GetAllAsync(async content => { + content.MapComponents(schemaMap); + contents.Add(content.ToModel(schema.Name)); if (contents.Count > 50) @@ -90,6 +93,7 @@ public async Task ImportAsync(ISyncService sync, SyncOptions options, ISession s .Select(x => (x, sync.Read(x, log))); var schemas = await session.Schemas.GetSchemasAsync(session.App); + var schemaMap = schemas.Items.ToDictionary(x => x.Name, x => x.Id); foreach (var (file, model) in models) { @@ -97,7 +101,12 @@ public async Task ImportAsync(ISyncService sync, SyncOptions options, ISession s { model.Clear(options.Languages); - var client = session.Contents(model.Contents.First().Schema); + foreach (var content in model.Contents) + { + content.MapComponents(schemaMap); + } + + var client = session.Contents(model.Contents[0].Schema); var request = new BulkUpdate { @@ -115,7 +124,7 @@ public async Task ImportAsync(ISyncService sync, SyncOptions options, ISession s foreach (var content in model.Contents) { - var result = results.FirstOrDefault(x => x.JobIndex == contentIndex); + var result = results.Find(x => x.JobIndex == contentIndex); log.StepStart($"Upserting #{contentIndex}"); diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/Extensions.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/Extensions.cs index 2460a7a7..e909ef9a 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/Extensions.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Contents/Extensions.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; @@ -20,7 +21,7 @@ public static BulkUpdateJob ToJob(this ContentModel model, SchemasDto schemas) var id = model.Id; #pragma warning disable CS0618 // Type or member is obsolete - var singleton = schemas.Items.FirstOrDefault(x => x.Name == model.Schema && x.IsSingleton); + var singleton = schemas.Items.Find(x => x.Name == model.Schema && x.IsSingleton); #pragma warning restore CS0618 // Type or member is obsolete if (singleton != null) { @@ -48,6 +49,52 @@ public static ContentModel ToModel(this DynamicContent content, string schema) }; } + public static void MapComponents(this DynamicContent content, Dictionary map) + { + MapComponents(content.Data, map); + } + + public static void MapComponents(this ContentModel content, Dictionary map) + { + MapComponents(content.Data, map); + } + + private static void MapComponents(this DynamicData data, Dictionary map) + { + static void Map(JToken token, Dictionary map) + { + if (token is JObject obj) + { + foreach (var value in obj.Values()) + { + Map(value, map); + } + + if (!obj.TryGetValue(Component.Discriminator, StringComparison.Ordinal, out var schemaId)) + { + return; + } + + if (schemaId.Type == JTokenType.String && map.TryGetValue(schemaId.Value(), out var target)) + { + obj[Component.Discriminator] = target; + } + } + else if (token is JArray array) + { + foreach (var value in array) + { + Map(value, map); + } + } + } + + foreach (var field in data.Values) + { + Map(field, map); + } + } + public static void Clear(this ContentsModel model, string[] languages) { if (languages?.Length > 0 && model.Contents?.Count > 0) diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/Extensions.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/Extensions.cs index 6e4eaac9..707901be 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/Extensions.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/Extensions.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using Squidex.ClientLibrary.Management; #pragma warning disable CS0618 // Type or member is obsolete @@ -26,5 +27,65 @@ public static CreateSchemaDto ToRequest(this SchemaCreateModel model) return new CreateSchemaDto { Name = model.Name, IsSingleton = isSingleton, Type = type }; } + + public static void MapReferences(this SynchronizeSchemaDto schema, Dictionary map) + { + if (schema.Fields != null) + { + foreach (var field in schema.Fields) + { + MapReferences(field, map); + } + } + } + + public static void MapReferences(this UpsertSchemaFieldDto field, Dictionary map) + { + MapReferences(field.Properties, map); + + if (field.Nested != null) + { + foreach (var nested in field.Nested) + { + MapReferences(nested.Properties, map); + } + } + } + + private static void MapReferences(FieldPropertiesDto properties, Dictionary map) + { + if (properties is ReferencesFieldPropertiesDto references) + { + references.SchemaIds = MapReferences(references.SchemaIds, map); + } + else if (properties is ComponentFieldPropertiesDto component) + { + component.SchemaIds = MapReferences(component.SchemaIds, map); + } + else if (properties is ComponentsFieldPropertiesDto components) + { + components.SchemaIds = MapReferences(components.SchemaIds, map); + } + } + + private static List MapReferences(List ids, Dictionary map) + { + if (ids == null || ids.Count == 0) + { + return ids; + } + + var result = new List(); + + foreach (var id in ids) + { + if (map.TryGetValue(id, out var target)) + { + result.Add(target); + } + } + + return result; + } } } diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemeModel.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemaModel.cs similarity index 90% rename from cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemeModel.cs rename to cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemaModel.cs index b5dae389..1375504e 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemeModel.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemaModel.cs @@ -9,7 +9,7 @@ namespace Squidex.CLI.Commands.Implementation.Sync.Schemas { - public sealed class SchemeModel : SchemaCreateModel + public sealed class SchemaModel : SchemaCreateModel { public SynchronizeSchemaDto Schema { get; set; } } diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemasSynchronizer.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemasSynchronizer.cs index a6aa22e9..3a07f7db 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemasSynchronizer.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Schemas/SchemasSynchronizer.cs @@ -52,7 +52,7 @@ await log.DoSafeAsync($"Exporting '{schema.Name}'", async () => { var details = await session.Schemas.GetSchemaAsync(session.App, schema.Name); - var model = new SchemeModel + var model = new SchemaModel { Name = schema.Name, Schema = sync.Convert(details), @@ -60,7 +60,7 @@ await log.DoSafeAsync($"Exporting '{schema.Name}'", async () => IsSingleton = details.IsSingleton }; - MapReferences(model.Schema, schemaMap); + model.Schema.MapReferences(schemaMap); await sync.WriteWithSchema(new FilePath($"schemas", $"{schema.Name}.json"), model, Ref); }); @@ -117,12 +117,12 @@ await log.DoSafeAsync($"Schema {model.Name} creating", async () => var models = GetSchemaFiles(sync.FileSystem) - .Select(x => sync.Read(x, log)) + .Select(x => sync.Read(x, log)) .ToList(); foreach (var model in models) { - MapReferences(model.Schema, schemaMap); + model.Schema.MapReferences(schemaMap); var version = schemasByName[model.Name].Version; @@ -158,9 +158,9 @@ private static IEnumerable GetSchemaFiles(IFileSystem fs) public async Task GenerateSchemaAsync(ISyncService sync) { - await sync.WriteJsonSchemaAsync(new FilePath("schema.json")); + await sync.WriteJsonSchemaAsync(new FilePath("schema.json")); - var sample = new SchemeModel + var sample = new SchemaModel { Name = "my-schema", Schema = new SynchronizeSchemaDto @@ -187,65 +187,5 @@ public async Task GenerateSchemaAsync(ISyncService sync) await sync.WriteWithSchema(new FilePath("schemas", "__schema.json"), sample, Ref); } - - private static void MapReferences(SynchronizeSchemaDto schema, Dictionary map) - { - if (schema.Fields != null) - { - foreach (var field in schema.Fields) - { - MapReferences(field, map); - } - } - } - - private static void MapReferences(UpsertSchemaFieldDto field, Dictionary map) - { - MapReferences(field.Properties, map); - - if (field.Nested != null) - { - foreach (var nested in field.Nested) - { - MapReferences(nested.Properties, map); - } - } - } - - private static void MapReferences(FieldPropertiesDto properties, Dictionary map) - { - if (properties is ReferencesFieldPropertiesDto references) - { - references.SchemaIds = MapReferences(references.SchemaIds, map); - } - else if (properties is ComponentFieldPropertiesDto component) - { - component.SchemaIds = MapReferences(component.SchemaIds, map); - } - else if (properties is ComponentsFieldPropertiesDto components) - { - components.SchemaIds = MapReferences(components.SchemaIds, map); - } - } - - private static List MapReferences(List ids, Dictionary map) - { - if (ids == null || ids.Count == 0) - { - return ids; - } - - var result = new List(); - - foreach (var id in ids) - { - if (map.TryGetValue(id, out var target)) - { - result.Add(target); - } - } - - return result; - } } } diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Workflows/WorkflowsSynchronizer.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Workflows/WorkflowsSynchronizer.cs index 9c68e03f..40445eca 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Workflows/WorkflowsSynchronizer.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/Sync/Workflows/WorkflowsSynchronizer.cs @@ -116,7 +116,7 @@ await log.DoSafeAsync($"Workflow '{workflow.Name}' creating", async () => var created = await session.Apps.PostWorkflowAsync(session.App, request); - workflowsByName[workflow.Name] = created.Items.FirstOrDefault(x => x.Name == workflow.Name); + workflowsByName[workflow.Name] = created.Items.Find(x => x.Name == workflow.Name); }); } diff --git a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/TestData/LoremIpsum.cs b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/TestData/LoremIpsum.cs index cb90d9ec..2e53778a 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/TestData/LoremIpsum.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Commands/Implementation/TestData/LoremIpsum.cs @@ -110,7 +110,7 @@ void Append(string value) if (sb.Length == 0) { - return Words[0].Substring(0, maxCharacters); + return Words[0][..maxCharacters]; } while (sb.Length < maxCharacters) diff --git a/cli/Squidex.CLI/Squidex.CLI/Helper.cs b/cli/Squidex.CLI/Squidex.CLI/Helper.cs index e7036a60..d83cb43f 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Helper.cs +++ b/cli/Squidex.CLI/Squidex.CLI/Helper.cs @@ -81,7 +81,9 @@ public static string Truncate(this string value, int maxLength) } else { - return value.Substring(0, maxLength - 3) + "..."; + var length = maxLength - 3; + + return value[..maxLength] + "..."; } } } diff --git a/cli/Squidex.CLI/Squidex.CLI/Squidex.CLI.csproj b/cli/Squidex.CLI/Squidex.CLI/Squidex.CLI.csproj index 23e002e3..c9ad1baf 100644 --- a/cli/Squidex.CLI/Squidex.CLI/Squidex.CLI.csproj +++ b/cli/Squidex.CLI/Squidex.CLI/Squidex.CLI.csproj @@ -14,7 +14,7 @@ net5.0 true sq - 7.21 + 7.22 diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Component.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Component.cs new file mode 100644 index 00000000..ab5f4977 --- /dev/null +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Component.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.ClientLibrary +{ + /// + /// Holds constants to work with components. + /// + public static class Component + { + /// + /// The used discriminator key for components. + /// + public const string Discriminator = "schemaId"; + } +}