diff --git a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs index 89a7eeb37..f0f0bbd38 100644 --- a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs +++ b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs @@ -181,8 +181,19 @@ public abstract partial class DomainObject : IAggregate where T : class, IDom var deletedId = DomainId.Combine(UniqueId, DomainId.Create("deleted")); var deletedStream = persistenceFactory.WithEventSourcing(GetType(), deletedId, null); - // Write to the deleted stream first so we never loose this information. - await deletedStream.WriteEventsAsync(uncomittedEvents, ct); + try + { + // Write to the deleted stream first so we never loose this information. + await deletedStream.WriteEventsAsync(uncomittedEvents, ct); + } + catch (InconsistentStateException) + { + + // There is another deletion event, therefore we have to fetch the version. + await deletedStream.ReadAsync(ct: default); + + await deletedStream.WriteEventsAsync(uncomittedEvents, ct); + } } // Cleanup the primary stream second. diff --git a/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs b/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs index 163eada50..b6418bdf2 100644 --- a/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs +++ b/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs @@ -29,107 +29,74 @@ public class ContentUpdateTests : IClassFixture [Fact] public async Task Should_return_published_content() { - TestEntity content = null; - try + // STEP 1: Create the item unpublished. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create the item unpublished. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }); + Number = 1 + }); - // STEP 2: Publish the item. - await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus - { - Status = "Published" - }); + // STEP 2: Publish the item. + await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus + { + Status = "Published" + }); - // STEP 3: Retrieve the item. - await _.Contents.GetAsync(content.Id); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + // STEP 3: Retrieve the item. + await _.Contents.GetAsync(content.Id); } [Fact] public async Task Should_not_return_archived_content() { - TestEntity content = null; - try + // STEP 1: Create the item published. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create the item published. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }, ContentCreateOptions.AsPublish); + Number = 1 + }, ContentCreateOptions.AsPublish); - // STEP 2: Archive the item. - await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus - { - Status = "Archived" - }); + // STEP 2: Archive the item. + await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus + { + Status = "Archived" + }); - // STEP 3. Get a 404 for the item because it is not published anymore. - await Assert.ThrowsAnyAsync(() => - { - return _.Contents.GetAsync(content.Id); - }); - } - finally + // STEP 3. Get a 404 for the item because it is not published anymore. + await Assert.ThrowsAnyAsync(() => { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + return _.Contents.GetAsync(content.Id); + }); } [Fact] public async Task Should_not_return_unpublished_content() { - TestEntity content = null; - try + // STEP 1: Create the item unpublished. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create the item unpublished. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }); + Number = 1 + }); - // STEP 2: Change the status to publiushed and then to draft. - await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus - { - Status = "Published" - }); - await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus - { - Status = "Draft" - }); + // STEP 2: Change the status to publiushed and then to draft. + await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus + { + Status = "Published" + }); + await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus + { + Status = "Draft" + }); - // STEP 3. Get a 404 for the item because it is not published anymore. - await Assert.ThrowsAnyAsync(() => - { - return _.Contents.GetAsync(content.Id); - }); - } - finally + // STEP 3. Get a 404 for the item because it is not published anymore. + await Assert.ThrowsAnyAsync(() => { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + return _.Contents.GetAsync(content.Id); + }); } [Fact] @@ -137,933 +104,441 @@ public class ContentUpdateTests : IClassFixture { const string text = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"; - TestEntity content = null; - try + // STEP 1: Create a content item with a text that caused a bug before. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a content item with a text that caused a bug before. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = text - }, ContentCreateOptions.AsPublish); + String = text + }, ContentCreateOptions.AsPublish); - // STEP 2: Get the item and ensure that the text is the same. - var queried = await _.Contents.GetAsync(content.Id); + // STEP 2: Get the item and ensure that the text is the same. + var queried = await _.Contents.GetAsync(content.Id); - Assert.Equal(text, queried.Data.String); + Assert.Equal(text, queried.Data.String); - await Verify(queried); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + await Verify(queried); } [Fact] - public async Task Should_create_null_text() + public async Task Should_create_null_localized_text() { - TestEntity content = null; - try + // STEP 1: Create a content item with a text that caused a bug before. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a content item with a text that caused a bug before. - content = await _.Contents.CreateAsync(new TestEntityData + Localized = new Dictionary { - Localized = new Dictionary - { - ["en"] = null - } - }, ContentCreateOptions.AsPublish); + ["en"] = null + } + }, ContentCreateOptions.AsPublish); - // STEP 2: Get the item and ensure that the text is the same. - var queried = await _.Contents.GetAsync(content.Id, QueryContext.Default.IgnoreFallback()); + // STEP 2: Get the item and ensure that the text is the same. + var queried = await _.Contents.GetAsync(content.Id, QueryContext.Default.IgnoreFallback()); - Assert.Null(queried.Data.Localized["en"]); + Assert.Null(queried.Data.Localized["en"]); - await Verify(queried); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + await Verify(queried); } [Fact] public async Task Should_create_json_with_dot() { - TestEntity content = null; - try + // STEP 1: Create a content item with a text that caused a bug before. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a content item with a text that caused a bug before. - content = await _.Contents.CreateAsync(new TestEntityData + Json = new JObject { - Json = new JObject - { - ["field.with.dot"] = 42 - } - }, ContentCreateOptions.AsPublish); + ["field.with.dot"] = 42 + } + }, ContentCreateOptions.AsPublish); - // STEP 2: Get the item and ensure that the text is the same. - var queried = await _.Contents.GetAsync(content.Id, QueryContext.Default.IgnoreFallback()); + // STEP 2: Get the item and ensure that the text is the same. + var queried = await _.Contents.GetAsync(content.Id, QueryContext.Default.IgnoreFallback()); - Assert.Equal(42, (int)queried.Data.Json["field.with.dot"]); + Assert.Equal(42, (int)queried.Data.Json["field.with.dot"]); - await Verify(queried); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + await Verify(queried); } [Fact] public async Task Should_create_default_text() { - TestEntity content = null; - try + // STEP 1: Create a content item with a text that caused a bug before. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a content item with a text that caused a bug before. - content = await _.Contents.CreateAsync(new TestEntityData - { - Localized = new Dictionary() - }, ContentCreateOptions.AsPublish); + Localized = new Dictionary() + }, ContentCreateOptions.AsPublish); - // STEP 2: Get the item and ensure that the text is the same. - var updated = await _.Contents.GetAsync(content.Id); + // STEP 2: Get the item and ensure that the text is the same. + var updated = await _.Contents.GetAsync(content.Id); - Assert.Equal("default", updated.Data.Localized["en"]); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Assert.Equal("default", updated.Data.Localized["en"]); } [Fact] public async Task Should_create_non_published_content() { - TestEntity content = null; - try + // STEP 1: Create the item unpublished. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create the item unpublished. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }); - + Number = 1 + }); - // STEP 2. Get a 404 for the item because it is not published. - await Assert.ThrowsAnyAsync(() => - { - return _.Contents.GetAsync(content.Id); - }); - await Verify(content); - } - finally + // STEP 2. Get a 404 for the item because it is not published. + await Assert.ThrowsAnyAsync(() => { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + return _.Contents.GetAsync(content.Id); + }); + + await Verify(content); } [Fact] public async Task Should_create_published_content() { - TestEntity content = null; - try - { - // STEP 1: Create the item published. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }, ContentCreateOptions.AsPublish); - - - // STEP 2: Get the item. - await _.Contents.GetAsync(content.Id); - } - finally + // STEP 1: Create the item published. + var content = await _.Contents.CreateAsync(new TestEntityData { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } - } - - [Fact] - public async Task Should_create_content_with_custom_id() - { - var id = Guid.NewGuid().ToString(); + Number = 1 + }, ContentCreateOptions.AsPublish); - TestEntity content = null; - try - { - // STEP 1: Create a new item with a custom id. - var options = new ContentCreateOptions { Id = id, Publish = true }; - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }, options); + // STEP 2: Get the item. + await _.Contents.GetAsync(content.Id); - Assert.Equal(id, content.Id); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + await Verify(content); } [Fact] - public async Task Should_not_create_content_with_custom_id_twice() + public async Task Should_create_draft_version() { - var id = Guid.NewGuid().ToString(); - - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a new item with a custom id. - var options = new ContentCreateOptions { Id = id, Publish = true }; - - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }, options); - - Assert.Equal(id, content.Id); - + Number = 1 + }, ContentCreateOptions.AsPublish); - // STEP 2: Create a new item with a custom id. - var ex = await Assert.ThrowsAnyAsync(() => - { - return _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }, options); - }); - Assert.Equal(409, ex.StatusCode); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } - } + // STEP 2: Create draft. + content = await _.Contents.CreateDraftAsync(content.Id); - [Fact] - public async Task Should_create_content_with_custom_id_and_upsert() - { - var id = Guid.NewGuid().ToString(); - TestEntity content = null; - try + // STEP 3: Update the item and ensure that the data has not changed. + await _.Contents.PatchAsync(content.Id, new TestEntityData { - // STEP 1: Upsert a new item with a custom id. - content = await _.Contents.UpsertAsync(id, new TestEntityData - { - Number = 1 - }, ContentUpsertOptions.AsPublish); + Number = 2 + }); - Assert.Equal(id, content.Id); + var updated_1 = await _.Contents.GetAsync(content.Id); + Assert.Equal(1, updated_1.Data.Number); - // STEP 2: Make an update with the upsert endpoint. - content = await _.Contents.UpsertAsync(id, new TestEntityData - { - Number = 2 - }); - Assert.Equal(2, content.Data.Number); + // STEP 4: Get the unpublished version + var unpublished = await _.Contents.GetAsync(content.Id, QueryContext.Default.Unpublished()); + Assert.Equal(2, unpublished.Data.Number); - // STEP 3: Make an update with the update endpoint. - content = await _.Contents.UpdateAsync(id, new TestEntityData - { - Number = 3 - }); - Assert.Equal(3, content.Data.Number); - } - finally + // STEP 5: Publish draft and ensure that it has been updated. + await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } - } - - [Fact] - public async Task Should_update_content() - { - TestEntity content = null; - try - { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 2 - }, ContentCreateOptions.AsPublish); - - - // STEP 2: Update the item and ensure that the data has changed. - await _.Contents.UpdateAsync(content.Id, new TestEntityData - { - Number = 2 - }); + Status = "Published" + }); - var updated = await _.Contents.GetAsync(content.Id); + var updated_2 = await _.Contents.GetAsync(content.Id); - Assert.Equal(2, content.Data.Number); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Assert.Equal(2, updated_2.Data.Number); } [Fact] - public async Task Should_update_content_in_parallel() + public async Task Should_create_content_with_custom_id() { - TestEntity content = null; - try - { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 2 - }, ContentCreateOptions.AsPublish); - - - // STEP 3: Make parallel updates. - await Parallel.ForEachAsync(Enumerable.Range(0, 20), async (i, ct) => - { - try - { - await _.Contents.UpdateAsync(content.Id, new TestEntityData - { - Number = i - }); - } - catch (SquidexException ex) when (ex.StatusCode is 409 or 412) - { - return; - } - }); - - - // STEP 3: Make an normal update to ensure nothing is corrupt. - await _.Contents.UpdateAsync(content.Id, new TestEntityData - { - Number = 2 - }); + var id = $"custom-{Guid.NewGuid()}"; - var updated = await _.Contents.GetAsync(content.Id); + // STEP 1: Create a new item with a custom id. + var options = new ContentCreateOptions { Id = id, Publish = true }; - Assert.Equal(2, content.Data.Number); - } - finally + var content = await _.Contents.CreateAsync(new TestEntityData { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Number = 1 + }, options); + + Assert.Equal(id, content.Id); } [Fact] - public async Task Should_upsert_content_in_parallel() + public async Task Should_not_create_content_with_custom_id_twice() { - TestEntity content = null; - try - { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 2 - }, ContentCreateOptions.AsPublish); - + var id = $"custom-{Guid.NewGuid()}"; - // STEP 3: Make parallel upserts. - await Parallel.ForEachAsync(Enumerable.Range(0, 20), async (i, ct) => - { - try - { - await _.Contents.UpsertAsync(content.Id, new TestEntityData - { - Number = i - }); - } - catch (SquidexException ex) when (ex.StatusCode is 409 or 412) - { - return; - } - }); - - - // STEP 3: Make an normal update to ensure nothing is corrupt. - await _.Contents.UpdateAsync(content.Id, new TestEntityData - { - Number = 2 - }); - - var updated = await _.Contents.GetAsync(content.Id); - - Assert.Equal(2, content.Data.Number); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } - } + // STEP 1: Create a new item with a custom id. + var options = new ContentCreateOptions { Id = id, Publish = true }; - [Fact] - public async Task Should_update_content_to_null() - { - TestEntity content = null; - try + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = "initial" - }, ContentCreateOptions.AsPublish); - + Number = 1 + }, options); - // STEP 2: Update the item and ensure that the data has changed. - await _.Contents.UpdateAsync(content.Id, new TestEntityData - { - String = null - }); + Assert.Equal(id, content.Id); - var updated = await _.Contents.GetAsync(content.Id); - Assert.Null(updated.Data.String); - } - finally + // STEP 2: Create a new item with a custom id. + var ex = await Assert.ThrowsAnyAsync(() => { - if (content != null) + return _.Contents.CreateAsync(new TestEntityData { - await _.Contents.DeleteAsync(content.Id); - } - } + Number = 1 + }, options); + }); + + Assert.Equal(409, ex.StatusCode); } [Fact] - public async Task Should_patch_content() + public async Task Should_create_content_with_custom_id_and_upsert() { - TestEntity content = null; - try - { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = "test" - }, ContentCreateOptions.AsPublish); - + var id = $"custom-{Guid.NewGuid()}"; - // STEP 2: Patch an item. - await _.Contents.PatchAsync(content.Id, new TestEntityData - { - Number = 1 - }); + // STEP 1: Upsert a new item with a custom id. + var content = await _.Contents.UpsertAsync(id, new TestEntityData + { + Number = 1 + }, ContentUpsertOptions.AsPublish); + Assert.Equal(id, content.Id); - // STEP 3: Update the item and ensure that the data has changed. - await _.Contents.PatchAsync(content.Id, new TestEntityData - { - Number = 2 - }); - var updated = await _.Contents.GetAsync(content.Id); + // STEP 2: Make an update with the upsert endpoint. + content = await _.Contents.UpsertAsync(id, new TestEntityData + { + Number = 2 + }); - Assert.Equal(2, updated.Data.Number); + Assert.Equal(2, content.Data.Number); - // Should not change other value with patch. - Assert.Equal("test", updated.Data.String); - await Verify(updated); - } - finally + // STEP 3: Make an update with the update endpoint. + content = await _.Contents.UpdateAsync(id, new TestEntityData { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Number = 3 + }); + + Assert.Equal(3, content.Data.Number); } - [Fact] - public async Task Should_patch_id_data_value() + [Theory] + [InlineData(Strategies.Update.Normal)] + [InlineData(Strategies.Update.Upsert)] + [InlineData(Strategies.Update.UpsertBulk)] + [InlineData(Strategies.Update.Bulk)] + [InlineData(Strategies.Update.BulkShared)] + [InlineData(Strategies.Update.BulkWithSchema)] + public async Task Should_update_content(Strategies.Update strategy) { - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - Id = "id1" - }, ContentCreateOptions.AsPublish); + String = "2" + }, ContentCreateOptions.AsPublish); - // STEP 2: Update the item and ensure that the data has changed. - await _.Contents.PatchAsync(content.Id, new TestEntityData - { - Id = "id2" - }); + // STEP 2: Update with selected strategy. + await _.ClientManager.UpdateAsync(content, new TestEntityData + { + Number = 200 + }, strategy); - var updated = await _.Contents.GetAsync(content.Id); + var updated = await _.Contents.GetAsync(content.Id); - Assert.Equal("id2", updated.Data.Id); + Assert.Equal(200, updated.Data.Number); - await Verify(updated); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + // Other data fields are overwritten. + Assert.Null(updated.Data.String); } - [Fact] - public async Task Should_patch_content_to_null() + [Theory] + [InlineData(Strategies.Update.Normal)] + [InlineData(Strategies.Update.Upsert)] + [InlineData(Strategies.Update.UpsertBulk)] + [InlineData(Strategies.Update.Bulk)] + [InlineData(Strategies.Update.BulkShared)] + [InlineData(Strategies.Update.BulkWithSchema)] + public async Task Should_update_content_to_null(Strategies.Update strategy) { - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = "initial" - }, ContentCreateOptions.AsPublish); + String = "initial" + }, ContentCreateOptions.AsPublish); - // STEP 2: Update the item and ensure that the data has changed. - await _.Contents.PatchAsync(content.Id, new - { - @string = new - { - iv = (object)null - } - }); - - var updated = await _.Contents.GetAsync(content.Id); + // STEP 2: Update with selected strategy. + await _.ClientManager.UpdateAsync(content, new TestEntityData + { + String = null + }, strategy); - Assert.Null(updated.Data.String); + var updated = await _.Contents.GetAsync(content.Id); - await Verify(updated); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Assert.Null(updated.Data.String); } - [Fact] - public async Task Should_patch_content_with_upsert() + [Theory] + [InlineData(Strategies.Patch.Normal)] + [InlineData(Strategies.Patch.Upsert)] + [InlineData(Strategies.Patch.UpsertBulk)] + [InlineData(Strategies.Patch.Bulk)] + [InlineData(Strategies.Patch.BulkShared)] + [InlineData(Strategies.Patch.BulkWithSchema)] + public async Task Should_patch_content(Strategies.Patch strategy) { - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = "test" - }, ContentCreateOptions.AsPublish); - - - // STEP 2: Patch an item. - await _.Contents.UpsertAsync(content.Id, new TestEntityData - { - Number = 1 - }, ContentUpsertOptions.AsPatch); - + String = "initial" + }, ContentCreateOptions.AsPublish); - // STEP 3: Update the item and ensure that the data has changed. - await _.Contents.UpsertAsync(content.Id, new TestEntityData - { - Number = 2 - }, ContentUpsertOptions.AsPatch); - var updated = await _.Contents.GetAsync(content.Id); + // STEP 2: Patch with selected strategy. + await _.ClientManager.PatchAsync(content, new TestEntityData + { + Number = 200 + }, strategy); - Assert.Equal(2, updated.Data.Number); + var updated = await _.Contents.GetAsync(content.Id); - // Should not change other value with patch. - Assert.Equal("test", updated.Data.String); + Assert.Equal(200, updated.Data.Number); - await Verify(updated); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + // Other data fields cannot be changed. + Assert.Equal("initial", updated.Data.String); } - [Fact] - public async Task Should_patch_content_with_bulk() + [Theory] + [InlineData(Strategies.Patch.Normal)] + [InlineData(Strategies.Patch.Upsert)] + [InlineData(Strategies.Patch.UpsertBulk)] + [InlineData(Strategies.Patch.Bulk)] + [InlineData(Strategies.Patch.BulkShared)] + [InlineData(Strategies.Patch.BulkWithSchema)] + public async Task Should_patch_id_data_value(Strategies.Patch strategy) { - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = "test" - }, ContentCreateOptions.AsPublish); - - - // STEP 2: Patch an item. - await _.Contents.BulkUpdateAsync(new BulkUpdate - { - Jobs = new List - { - new BulkUpdateJob - { - Id = content.Id, - Data = new - { - number = new - { - iv = 1 - } - }, - Patch = true - } - } - }); - - - // STEP 3: Update the item and ensure that the data has changed. - await _.Contents.BulkUpdateAsync(new BulkUpdate - { - Jobs = new List - { - new BulkUpdateJob - { - Id = content.Id, - Data = new - { - number = new - { - iv = 2 - } - }, - Patch = true - } - } - }); + Id = "id1" + }, ContentCreateOptions.AsPublish); - var updated = await _.Contents.GetAsync(content.Id); - Assert.Equal(2, updated.Data.Number); + // STEP 2: Patch with selected strategy. + await _.ClientManager.PatchAsync(content, new TestEntityData + { + Id = "id2" + }, strategy); - // Should not change other value with patch. - Assert.Equal("test", updated.Data.String); + var updated = await _.Contents.GetAsync(content.Id); - await Verify(updated); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Assert.Equal("id2", updated.Data.Id); } - [Fact] - public async Task Should_update_content_with_bulk_and_overriden_schema_name() + [Theory] + [InlineData(Strategies.Patch.Normal)] + [InlineData(Strategies.Patch.Upsert)] + [InlineData(Strategies.Patch.UpsertBulk)] + [InlineData(Strategies.Patch.Bulk)] + [InlineData(Strategies.Patch.BulkShared)] + [InlineData(Strategies.Patch.BulkWithSchema)] + public async Task Should_patch_content_to_null(Strategies.Patch strategy) { - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - var schemaName = $"schema-{Guid.NewGuid()}"; - - // STEP 0: Create dummy schema. - var createSchema = new CreateSchemaDto - { - Name = schemaName, - - // Publish it to avoid validations issues. - IsPublished = true - }; - - await _.Schemas.PostSchemaAsync(_.AppName, createSchema); - - - - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = "test" - }, ContentCreateOptions.AsPublish); - - - // STEP 2: Patch an item. - var client = _.ClientManager.CreateContentsClient(schemaName); - - await client.BulkUpdateAsync(new BulkUpdate - { - Jobs = new List - { - new BulkUpdateJob - { - Id = content.Id, - Data = new - { - number = new - { - iv = 1 - } - }, - Schema = _.SchemaName - } - } - }); + String = "initial" + }, ContentCreateOptions.AsPublish); - // STEP 3: Update the item and ensure that the data has changed. - await client.BulkUpdateAsync(new BulkUpdate - { - Jobs = new List - { - new BulkUpdateJob - { - Id = content.Id, - Data = new - { - number = new - { - iv = 2 - } - }, - Schema = _.SchemaName - } - } - }); + // STEP 2: Patch with selected strategy. + await _.ClientManager.PatchAsync(content, new + { + @string = new { iv = (string)null } + }, strategy); - var updated = await _.Contents.GetAsync(content.Id); + var updated = await _.Contents.GetAsync(content.Id); - Assert.Equal(2, updated.Data.Number); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Assert.Null(updated.Data.String); } - [Fact] - public async Task Should_update_content_with_bulk_and_shared_client() + [Theory] + [InlineData(Strategies.Deletion.SingleSoft)] + [InlineData(Strategies.Deletion.SinglePermanent)] + [InlineData(Strategies.Deletion.BulkSoft)] + [InlineData(Strategies.Deletion.BulkPermanent)] + public async Task Should_delete_content(Strategies.Deletion strategy) { - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - var schemaName = $"schema-{Guid.NewGuid()}"; - - // STEP 0: Create dummy schema. - var createSchema = new CreateSchemaDto - { - Name = schemaName, - - // Publish it to avoid validations issues. - IsPublished = true - }; + Number = 2 + }, ContentCreateOptions.AsPublish); - await _.Schemas.PostSchemaAsync(_.AppName, createSchema); + // STEP 2: Delete with selected strategy. + await _.ClientManager.DeleteAsync(content, strategy); - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - String = "test" - }, ContentCreateOptions.AsPublish); - + // STEP 3: Retrieve all items and ensure that the deleted item does not exist. + var updated = await _.Contents.GetAsync(); - // STEP 2: Patch an item. - await _.SharedContents.BulkUpdateAsync(new BulkUpdate - { - Jobs = new List - { - new BulkUpdateJob - { - Id = content.Id, - Data = new - { - number = new - { - iv = 1 - } - }, - Schema = _.SchemaName - } - } - }); + Assert.DoesNotContain(updated.Items, x => x.Id == content.Id); - // STEP 3: Update the item and ensure that the data has changed. - await _.SharedContents.BulkUpdateAsync(new BulkUpdate - { - Jobs = new List - { - new BulkUpdateJob - { - Id = content.Id, - Data = new - { - number = new - { - iv = 2 - } - }, - Schema = _.SchemaName - } - } - }); + // STEP 4: Retrieve all deleted items and check if found. + var q = new ContentQuery { Filter = "isDeleted eq true" }; - var updated = await _.Contents.GetAsync(content.Id); + var deleted = await _.Contents.GetAsync(q, QueryContext.Default.Unpublished(true)); - Assert.Equal(2, updated.Data.Number); - } - finally - { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } + Assert.Equal(strategy is Strategies.Deletion.SingleSoft or Strategies.Deletion.BulkSoft, deleted.Items.Any(x => x.Id == content.Id)); } - [Fact] - public async Task Should_create_draft_version() + [Theory] + [InlineData(Strategies.Deletion.SingleSoft)] + [InlineData(Strategies.Deletion.SinglePermanent)] + [InlineData(Strategies.Deletion.BulkSoft)] + [InlineData(Strategies.Deletion.BulkPermanent)] + public async Task Should_create_content_with_custom_id_and_delete_it(Strategies.Deletion strategy) { - TestEntity content = null; - try - { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }, ContentCreateOptions.AsPublish); - - - // STEP 2: Create draft. - content = await _.Contents.CreateDraftAsync(content.Id); - + var id = $"custom-{Guid.NewGuid()}"; - // STEP 3: Update the item and ensure that the data has not changed. - await _.Contents.PatchAsync(content.Id, new TestEntityData - { - Number = 2 - }); - - var updated_1 = await _.Contents.GetAsync(content.Id); - - Assert.Equal(1, updated_1.Data.Number); - - - // STEP 4: Get the unpublished version - var unpublished = await _.Contents.GetAsync(content.Id, QueryContext.Default.Unpublished()); - - Assert.Equal(2, unpublished.Data.Number); - - - // STEP 5: Publish draft and ensure that it has been updated. - await _.Contents.ChangeStatusAsync(content.Id, new ChangeStatus - { - Status = "Published" - }); - - var updated_2 = await _.Contents.GetAsync(content.Id); + // STEP 1: Create a new item with a custom id. + var options = new ContentCreateOptions { Id = id, Publish = true }; - Assert.Equal(2, updated_2.Data.Number); - } - finally + var content = await _.Contents.CreateAsync(new TestEntityData { - if (content != null) - { - await _.Contents.DeleteAsync(content.Id); - } - } - } + Number = 1 + }, options); - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task Should_delete_content(bool permanent) - { - // STEP 1: Create a new item. - var content_1 = await _.Contents.CreateAsync(new TestEntityData - { - Number = 2 - }, ContentCreateOptions.AsPublish); + Assert.Equal(id, content.Id); - // STEP 2: Delete the item. - await _.Contents.DeleteAsync(content_1.Id, new ContentDeleteOptions { Permanent = permanent }); + // STEP 2: Delete with selected strategy. + await _.ClientManager.DeleteAsync(content, strategy); // STEP 3: Retrieve all items and ensure that the deleted item does not exist. var updated = await _.Contents.GetAsync(); - Assert.DoesNotContain(updated.Items, x => x.Id == content_1.Id); - - - // STEP 4: Retrieve all deleted items and check if found. - var q = new ContentQuery { Filter = "isDeleted eq true" }; - - var deleted = await _.Contents.GetAsync(q, QueryContext.Default.Unpublished(true)); - - Assert.Equal(!permanent, deleted.Items.Any(x => x.Id == content_1.Id)); + Assert.DoesNotContain(updated.Items, x => x.Id == id); } [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task Should_recreate_deleted_content(bool permanent) + [InlineData(Strategies.Deletion.SingleSoft)] + [InlineData(Strategies.Deletion.SinglePermanent)] + [InlineData(Strategies.Deletion.BulkSoft)] + [InlineData(Strategies.Deletion.BulkPermanent)] + public async Task Should_recreate_deleted_content(Strategies.Deletion strategy) { // STEP 1: Create a new item. var content_1 = await _.Contents.CreateAsync(new TestEntityData @@ -1072,19 +547,17 @@ public class ContentUpdateTests : IClassFixture }, ContentCreateOptions.AsPublish); - // STEP 2: Delete the item. - var createOptions = new ContentDeleteOptions { Permanent = permanent }; - - await _.Contents.DeleteAsync(content_1.Id, createOptions); + // STEP 2: Delete with selected strategy. + await _.ClientManager.DeleteAsync(content_1, strategy); // STEP 3: Recreate the item with the same id. - var deleteOptions = new ContentCreateOptions { Id = content_1.Id, Publish = true }; + var createOptions = new ContentCreateOptions { Id = content_1.Id, Publish = true }; var content_2 = await _.Contents.CreateAsync(new TestEntityData { Number = 2 - }, deleteOptions); + }, createOptions); Assert.Equal(Status.Published, content_2.Status); @@ -1092,15 +565,17 @@ public class ContentUpdateTests : IClassFixture // STEP 4: Check if we can find it again with a query. var q = new ContentQuery { Filter = $"id eq '{content_1.Id}'" }; - var contents_4 = await _.Contents.GetAsync(q); + var contents = await _.Contents.GetAsync(q); - Assert.NotNull(contents_4.Items.Find(x => x.Id == content_1.Id)); + Assert.Contains(contents.Items, x => x.Id == content_2.Id); } [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task Should_recreate_deleted_content_with_upsert(bool permanent) + [InlineData(Strategies.Deletion.SingleSoft)] + [InlineData(Strategies.Deletion.SinglePermanent)] + [InlineData(Strategies.Deletion.BulkSoft)] + [InlineData(Strategies.Deletion.BulkPermanent)] + public async Task Should_recreate_deleted_content_with_upsert(Strategies.Deletion strategy) { // STEP 1: Create a new item. var content_1 = await _.Contents.CreateAsync(new TestEntityData @@ -1109,10 +584,8 @@ public class ContentUpdateTests : IClassFixture }, ContentCreateOptions.AsPublish); - // STEP 2: Delete the item. - var deleteOptions = new ContentDeleteOptions { Permanent = permanent }; - - await _.Contents.DeleteAsync(content_1.Id, deleteOptions); + // STEP 2: Delete with selected strategy. + await _.ClientManager.DeleteAsync(content_1, strategy); // STEP 3: Recreate the item with the same id. @@ -1122,14 +595,41 @@ public class ContentUpdateTests : IClassFixture }, ContentUpsertOptions.AsPublish); Assert.Equal(Status.Published, content_2.Status); + } + [Theory] + [InlineData(Strategies.Deletion.SingleSoft)] + [InlineData(Strategies.Deletion.SinglePermanent)] + [InlineData(Strategies.Deletion.BulkSoft)] + [InlineData(Strategies.Deletion.BulkPermanent)] + public async Task Should_delete_recreated_content(Strategies.Deletion strategy) + { + var id = $"custom-{Guid.NewGuid()}"; - // STEP 4: Check if we can find it again with a query. - var q = new ContentQuery { Filter = $"id eq '{content_1.Id}'" }; + // STEP 1: Create a new item with a custom id. + var options = new ContentCreateOptions { Id = id, Publish = true }; + + var content_1 = await _.Contents.CreateAsync(new TestEntityData + { + Number = 1 + }, options); + + + // STEP 2: Permanently delete content with custom id. + await _.Contents.DeleteAsync(content_1.Id, new ContentDeleteOptions { Permanent = true }); + + + // STEP 3: Create a new item with same custom id. + var content_2 = await _.Contents.CreateAsync(new TestEntityData + { + Number = 2 + }, options); - var contents_4 = await _.Contents.GetAsync(q); + Assert.Equal(2, content_2.Data.Number); - Assert.NotNull(contents_4.Items.Find(x => x.Id == content_1.Id)); + + // STEP 3: Permanently delete content with custom id again. + await _.ClientManager.DeleteAsync(content_2, strategy); } [Fact] @@ -1181,84 +681,113 @@ public class ContentUpdateTests : IClassFixture [Fact] public async Task Should_get_content_by_version() { - TestEntity content = null; - try + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData { - // STEP 1: Create a new item. - content = await _.Contents.CreateAsync(new TestEntityData - { - Number = 1 - }, ContentCreateOptions.AsPublish); + Number = 1 + }, ContentCreateOptions.AsPublish); - // STEP 2: Update content. - content = await _.Contents.UpdateAsync(content.Id, new TestEntityData - { - Number = 2 - }); + // STEP 2: Update content. + content = await _.Contents.UpdateAsync(content.Id, new TestEntityData + { + Number = 2 + }); + + + // STEP 3: Get current version. + var content_latest = await _.Contents.GetAsync(content.Id); + + Assert.Equal(2, content_latest.Data.Number); - // STEP 3: Get current version. - var content_latest = await _.Contents.GetAsync(content.Id); + // STEP 4: Get current version. + var data_2 = await _.Contents.GetDataAsync(content.Id, content.Version); - Assert.Equal(2, content_latest.Data.Number); + Assert.Equal(2, data_2.Number); - // STEP 4: Get current version. - var data_2 = await _.Contents.GetDataAsync(content.Id, content.Version); + // STEP 4: Get previous version + var data_1 = await _.Contents.GetDataAsync(content.Id, content.Version - 1); - Assert.Equal(2, data_2.Number); + Assert.Equal(1, data_1.Number); + await Verify(data_1); + } - // STEP 4: Get previous version - var data_1 = await _.Contents.GetDataAsync(content.Id, content.Version - 1); + [Fact] + public async Task Should_update_content_in_parallel() + { + // STEP 1: Create a new item. + var content = await _.Contents.CreateAsync(new TestEntityData + { + Number = 2 + }, ContentCreateOptions.AsPublish); - Assert.Equal(1, data_1.Number); - await Verify(data_1); - } - finally + // STEP 3: Make parallel updates. + await Parallel.ForEachAsync(Enumerable.Range(0, 20), async (i, ct) => { - if (content != null) + try { - await _.Contents.DeleteAsync(content.Id); + await _.Contents.UpdateAsync(content.Id, new TestEntityData + { + Number = i + }); } - } + catch (SquidexException ex) when (ex.StatusCode is 409 or 412) + { + return; + } + }); + + + // STEP 3: Make an normal update to ensure nothing is corrupt. + await _.Contents.UpdateAsync(content.Id, new TestEntityData + { + Number = 2 + }); + + var updated = await _.Contents.GetAsync(content.Id); + + Assert.Equal(2, content.Data.Number); } [Fact] - public async Task Should_create_content_with_custom_id_and_delete_it() + public async Task Should_upsert_content_in_parallel() { - var id = "author-12345–fragment-text"; - - // STEP 1: Create a new item with a custom id. - var options = new ContentCreateOptions { Id = id, Publish = true }; - + // STEP 1: Create a new item. var content = await _.Contents.CreateAsync(new TestEntityData { - Number = 1 - }, options); - - Assert.Equal(id, content.Id); + Number = 2 + }, ContentCreateOptions.AsPublish); - // STEP 2: Delete with bulk update. - await _.Contents.BulkUpdateAsync(new BulkUpdate + // STEP 3: Make parallel upserts. + await Parallel.ForEachAsync(Enumerable.Range(0, 20), async (i, ct) => { - Jobs = new List + try { - new BulkUpdateJob + await _.Contents.UpsertAsync(content.Id, new TestEntityData { - Type = BulkUpdateType.Delete, - Id = id - } + Number = i + }); + } + catch (SquidexException ex) when (ex.StatusCode is 409 or 412) + { + return; } }); - // STEP 3: Retrieve all items and ensure that the deleted item does not exist. - var updated = await _.Contents.GetAsync(); + // STEP 3: Make an normal update to ensure nothing is corrupt. + await _.Contents.UpdateAsync(content.Id, new TestEntityData + { + Number = 2 + }); - Assert.DoesNotContain(updated.Items, x => x.Id == id); + var updated = await _.Contents.GetAsync(content.Id); + + Assert.Equal(2, content.Data.Number); } } diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_json_with_dot.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_json_with_dot.verified.txt index 39c0a7898..21fb1f5fc 100644 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_json_with_dot.verified.txt +++ b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_json_with_dot.verified.txt @@ -4,7 +4,9 @@ iv: null } }, + NewStatus: , Status: Published, + EditingStatus: Published, Id: Guid_1, Version: 1, _links: { diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_non_published_content.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_non_published_content.verified.txt index 99805a447..27e13c882 100644 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_non_published_content.verified.txt +++ b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_non_published_content.verified.txt @@ -4,7 +4,9 @@ iv: 1 } }, + NewStatus: , Status: Draft, + EditingStatus: Draft, Id: Guid_1, _links: { delete: { diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_null_text.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_null_localized_text.verified.txt similarity index 88% rename from tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_null_text.verified.txt rename to tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_null_localized_text.verified.txt index 40bdd6296..edb2ce207 100644 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_null_text.verified.txt +++ b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_null_localized_text.verified.txt @@ -6,7 +6,9 @@ en: null } }, + NewStatus: , Status: Published, + EditingStatus: Published, Id: Guid_1, Version: 1, _links: { diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_published_content.verified.txt similarity index 78% rename from tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content.verified.txt rename to tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_published_content.verified.txt index 731ee0573..adcf205a9 100644 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content.verified.txt +++ b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_published_content.verified.txt @@ -1,15 +1,14 @@ { Data: { Number: { - iv: 2 - }, - String: { - iv: test + iv: 1 } }, + NewStatus: , Status: Published, + EditingStatus: Published, Id: Guid_1, - Version: 3, + Version: 1, _links: { delete: { Method: DELETE diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_strange_text.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_strange_text.verified.txt index 477834e4c..fd7ac4f27 100644 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_strange_text.verified.txt +++ b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_create_strange_text.verified.txt @@ -4,7 +4,9 @@ iv: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36 } }, + NewStatus: , Status: Published, + EditingStatus: Published, Id: Guid_1, Version: 1, _links: { diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_to_null.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_to_null.verified.txt deleted file mode 100644 index 8ff50a7e3..000000000 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_to_null.verified.txt +++ /dev/null @@ -1,20 +0,0 @@ -{ - Data: {}, - Status: Published, - Id: Guid_1, - Version: 2, - _links: { - delete: { - Method: DELETE - }, - draft/create: { - Method: POST - }, - previous: { - Method: GET - }, - self: { - Method: GET - } - } -} \ No newline at end of file diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_with_bulk.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_with_bulk.verified.txt deleted file mode 100644 index 731ee0573..000000000 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_with_bulk.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - Data: { - Number: { - iv: 2 - }, - String: { - iv: test - } - }, - Status: Published, - Id: Guid_1, - Version: 3, - _links: { - delete: { - Method: DELETE - }, - draft/create: { - Method: POST - }, - previous: { - Method: GET - }, - self: { - Method: GET - } - } -} \ No newline at end of file diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_with_upsert.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_with_upsert.verified.txt deleted file mode 100644 index 731ee0573..000000000 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_content_with_upsert.verified.txt +++ /dev/null @@ -1,27 +0,0 @@ -{ - Data: { - Number: { - iv: 2 - }, - String: { - iv: test - } - }, - Status: Published, - Id: Guid_1, - Version: 3, - _links: { - delete: { - Method: DELETE - }, - draft/create: { - Method: POST - }, - previous: { - Method: GET - }, - self: { - Method: GET - } - } -} \ No newline at end of file diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_id_data_value.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_id_data_value.verified.txt deleted file mode 100644 index c4e6633e5..000000000 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_patch_id_data_value.verified.txt +++ /dev/null @@ -1,24 +0,0 @@ -{ - Data: { - Id: { - iv: id2 - } - }, - Status: Published, - Id: Guid_1, - Version: 2, - _links: { - delete: { - Method: DELETE - }, - draft/create: { - Method: POST - }, - previous: { - Method: GET - }, - self: { - Method: GET - } - } -} \ No newline at end of file diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_recreate_deleted_content.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_recreate_deleted_content.verified.txt deleted file mode 100644 index d695292ef..000000000 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_recreate_deleted_content.verified.txt +++ /dev/null @@ -1,40 +0,0 @@ -{ - Items: [ - { - Data: { - Number: { - iv: 2 - } - }, - Status: Published, - Id: Guid_1, - Version: 4, - _links: { - delete: { - Method: DELETE - }, - draft/create: { - Method: POST - }, - previous: { - Method: GET - }, - self: { - Method: GET - } - } - } - ], - Total: 1, - _links: { - create: { - Method: POST - }, - create/publish: { - Method: POST - }, - self: { - Method: GET - } - } -} \ No newline at end of file diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_recreate_deleted_content_with_upsert.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_recreate_deleted_content_with_upsert.verified.txt deleted file mode 100644 index 695bca6cf..000000000 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_recreate_deleted_content_with_upsert.verified.txt +++ /dev/null @@ -1,40 +0,0 @@ -{ - Items: [ - { - Data: { - Number: { - iv: 2 - } - }, - Status: Published, - Id: Guid_1, - Version: 1, - _links: { - delete: { - Method: DELETE - }, - draft/create: { - Method: POST - }, - previous: { - Method: GET - }, - self: { - Method: GET - } - } - } - ], - Total: 1, - _links: { - create: { - Method: POST - }, - create/publish: { - Method: POST - }, - self: { - Method: GET - } - } -} \ No newline at end of file diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_update_singleton_content_with_special_id.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_update_singleton_content_with_special_id.verified.txt index 3570a7089..e9f5be42d 100644 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_update_singleton_content_with_special_id.verified.txt +++ b/tools/TestSuite/TestSuite.ApiTests/Verify/ContentUpdateTests.Should_update_singleton_content_with_special_id.verified.txt @@ -4,7 +4,9 @@ iv: singleton } }, + NewStatus: , Status: Published, + EditingStatus: Published, Id: Guid_1, Version: 2, _links: { diff --git a/tools/TestSuite/TestSuite.Shared/Strategies.cs b/tools/TestSuite/TestSuite.Shared/Strategies.cs new file mode 100644 index 000000000..7ac23fc96 --- /dev/null +++ b/tools/TestSuite/TestSuite.Shared/Strategies.cs @@ -0,0 +1,233 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.ClientLibrary; + +namespace TestSuite; + +public static class Strategies +{ + public enum Deletion + { + SingleSoft, + SinglePermanent, + BulkSoft, + BulkPermanent + } + + public static Task DeleteAsync(this ISquidexClientManager clientManager, ContentBase content, Deletion strategy) + { + IContentsClient GetClient() + { + return clientManager.CreateContentsClient(content.SchemaName); + } + + switch (strategy) + { + case Deletion.SingleSoft: + return GetClient().DeleteAsync(content.Id); + case Deletion.SinglePermanent: + return GetClient().DeleteAsync(content.Id, new ContentDeleteOptions { Permanent = true }); + case Deletion.BulkSoft: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Type = BulkUpdateType.Delete, + Id = content.Id, + Permanent = false, + } + } + }); + case Deletion.BulkPermanent: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Type = BulkUpdateType.Delete, + Id = content.Id, + Permanent = true, + } + } + }); + default: + return Task.CompletedTask; + } + } + + public enum Update + { + Normal, + Upsert, + UpsertBulk, + Bulk, + BulkWithSchema, + BulkShared + } + + public static Task UpdateAsync(this ISquidexClientManager clientManager, ContentBase content, object data, Update strategy) + { + IContentsClient GetClient() + { + return clientManager.CreateContentsClient(content.SchemaName); + } + + switch (strategy) + { + case Update.Normal: + return GetClient().UpdateAsync(content.Id, data); + case Update.Upsert: + return GetClient().UpsertAsync(content.Id, data); + case Update.UpsertBulk: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Type = BulkUpdateType.Upsert + } + } + }); + case Update.Bulk: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Type = BulkUpdateType.Update, + } + } + }); + case Update.BulkWithSchema: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Type = BulkUpdateType.Update, + Schema = content.SchemaName + } + } + }); + case Update.BulkShared: + return clientManager.CreateSharedContentsClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Type = BulkUpdateType.Update, + Schema = content.SchemaName + } + } + }); + default: + return Task.CompletedTask; + } + } + + public enum Patch + { + Normal, + Upsert, + Bulk, + BulkWithSchema, + BulkShared, + UpsertBulk + } + + public static Task PatchAsync(this ISquidexClientManager clientManager, ContentBase content, object data, Patch strategy) + { + IContentsClient GetClient() + { + return clientManager.CreateContentsClient(content.SchemaName); + } + + switch (strategy) + { + case Patch.Normal: + return GetClient().PatchAsync(content.Id, data); + case Patch.Upsert: + return GetClient().UpsertAsync(content.Id, data, ContentUpsertOptions.AsPatch); + case Patch.UpsertBulk: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Patch = true, + } + } + }); + case Patch.Bulk: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Type = BulkUpdateType.Patch + } + } + }); + case Patch.BulkWithSchema: + return GetClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Type = BulkUpdateType.Patch, + Schema = content.SchemaName + } + } + }); + case Patch.BulkShared: + return clientManager.CreateSharedContentsClient().BulkUpdateAsync(new BulkUpdate + { + Jobs = new List + { + new BulkUpdateJob + { + Id = content.Id, + Data = data, + Type = BulkUpdateType.Patch, + Schema = content.SchemaName + } + } + }); + default: + return Task.CompletedTask; + } + } + + private class MyContent : Content + { + } +} diff --git a/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj b/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj index 5b5597052..00480534c 100644 --- a/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj +++ b/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj @@ -16,8 +16,8 @@ - - + +