From eef11abdfae8bbbf96de546fe42302da8eee8de2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 26 Apr 2018 20:30:41 +0200 Subject: [PATCH 1/4] Got rid of archive --- .../Contents/MongoContentRepository.cs | 35 ------- .../MongoContentRepository_SnapshotStore.cs | 2 - .../Contents/ContentQueryService.cs | 7 +- .../Contents/ContentVersionLoader.cs | 95 +++++++++++++++++++ .../Contents/IContentQueryService.cs | 2 +- .../Contents/IContentVersionLoader.cs | 18 ++++ .../Repositories/IContentRepository.cs | 2 - .../DomainObjectState.cs | 2 +- .../Controllers/Content/ContentsController.cs | 5 +- .../Contents/GraphQL/GraphQLQueriesTests.cs | 8 +- 10 files changed, 125 insertions(+), 51 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs index 1d4cc508f..50d725d35 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs @@ -26,12 +26,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public partial class MongoContentRepository : MongoRepositoryBase, IContentRepository { private readonly IAppProvider appProvider; - private readonly IMongoCollection archiveCollection; - - protected IMongoCollection ArchiveCollection - { - get { return archiveCollection; } - } public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider) : base(database) @@ -39,8 +33,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents Guard.NotNull(appProvider, nameof(appProvider)); this.appProvider = appProvider; - - archiveCollection = database.GetCollection("States_Contents_Archive"); } protected override string CollectionName() @@ -52,15 +44,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { await collection.Indexes.TryDropOneAsync("si_1_st_1_dl_1_dt_text"); - await archiveCollection.Indexes.CreateOneAsync( - Index - .Ascending(x => x.ScheduledTo)); - - await archiveCollection.Indexes.CreateOneAsync( - Index - .Ascending(x => x.Id) - .Ascending(x => x.Version)); - await collection.Indexes.CreateOneAsync( Index .Text(x => x.DataText) @@ -150,17 +133,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents return ids.Except(contentEntities.Select(x => Guid.Parse(x["id"].AsString))).ToList(); } - public async Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id, long version) - { - var contentEntity = - await ArchiveCollection.Find(x => x.Id == id && x.Version >= version).SortBy(x => x.Version) - .FirstOrDefaultAsync(); - - contentEntity?.ParseData(schema.SchemaDef); - - return contentEntity; - } - public async Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id) { var contentEntity = @@ -180,12 +152,5 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents callback(c); }); } - - public override async Task ClearAsync() - { - await Database.DropCollectionAsync("States_Contents_Archive"); - - await base.ClearAsync(); - } } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs index 54e236e4a..4bd67fd96 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs @@ -88,8 +88,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents } document.DocumentId = $"{key}_{newVersion}"; - - await ArchiveCollection.ReplaceOneAsync(x => x.DocumentId == document.DocumentId, document, Upsert); } private async Task GetSchemaAsync(Guid appId, Guid schemaId) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs index ebd65e858..e93f09281 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs @@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Contents this.modelBuilder = modelBuilder; } - public async Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id, long version = -1) + public async Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id) { Guard.NotNull(app, nameof(app)); Guard.NotNull(user, nameof(user)); @@ -58,10 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Contents var schema = await FindSchemaAsync(app, schemaIdOrName); - var content = - version > EtagVersion.Empty ? - await contentRepository.FindContentAsync(app, schema, id, version) : - await contentRepository.FindContentAsync(app, schema, id); + var content = await contentRepository.FindContentAsync(app, schema, id); if (content == null || (content.Status != Status.Published && !isFrontendClient)) { diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs new file mode 100644 index 000000000..04f02b1e0 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs @@ -0,0 +1,95 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using NodaTime; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Contents.State; +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.State; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Contents +{ + public sealed class ContentVersionLoader : IContentVersionLoader + { + private readonly IStore store; + private readonly FieldRegistry registry; + + public ContentVersionLoader(IStore store, FieldRegistry registry) + { + Guard.NotNull(store, nameof(store)); + Guard.NotNull(registry, nameof(registry)); + + this.store = store; + + this.registry = registry; + } + + public async Task<(ISchemaEntity Schema, IContentEntity Content)> LoadAsync(Guid id, int version) + { + var content = new ContentState(); + + var persistence = store.WithEventSourcing(id, e => + { + if (content.Version < version) + { + content = content.Apply(e); + } + }); + + await persistence.ReadAsync(); + + if (content.Version != version) + { + throw new DomainObjectNotFoundException(id.ToString(), typeof(IContentEntity)); + } + + var (now, then) = await ReadSchema(content.SchemaId.Id, content.LastModified); + + foreach (var key in content.Data.Keys) + { + if (IsFieldRemovedOrChanged(then.SchemaDef, now.SchemaDef, key)) + { + content.Data.Remove(key); + } + } + + return (then, content); + } + + private static bool IsFieldRemovedOrChanged(Schema schemaThen, Schema schemaNow, string key) + { + return + !schemaThen.FieldsByName.TryGetValue(key, out var fieldThen) || + !schemaNow.FieldsByName.TryGetValue(key, out var fieldNow) || + fieldThen.GetType() != fieldNow.GetType(); + } + + private async Task<(ISchemaEntity, ISchemaEntity)> ReadSchema(Guid schemaId, Instant lastUpdate) + { + var state = new SchemaState(); + var stateAtVersion = (SchemaState)null; + + var persistence = store.WithEventSourcing(schemaId, e => + { + state = state.Apply(e, registry); + + if (state.LastModified < lastUpdate) + { + stateAtVersion = state; + } + }); + + await persistence.ReadAsync(); + + return (state, stateAtVersion); + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs index f14a7d4cd..c2581135f 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs @@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Contents Task<(ISchemaEntity Schema, IResultList Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query); - Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id, long version = EtagVersion.Any); + Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id); Task FindSchemaAsync(IAppEntity app, string schemaIdOrName); } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs new file mode 100644 index 000000000..54ae6a28b --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using Squidex.Domain.Apps.Entities.Schemas; + +namespace Squidex.Domain.Apps.Entities.Contents +{ + public interface IContentVersionLoader + { + Task<(ISchemaEntity Schema, IContentEntity Content)> LoadAsync(Guid id, int version); + } +} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs b/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs index b9aba61be..0c2e687b7 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs @@ -27,8 +27,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.Repositories Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id); - Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id, long version); - Task QueryScheduledWithoutDataAsync(Instant now, Func callback); } } diff --git a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs index 210617ff6..13ce32acc 100644 --- a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs +++ b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs @@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities public Instant LastModified { get; set; } [JsonProperty] - public long Version { get; set; } + public long Version { get; set; } = EtagVersion.Empty; public T Clone() { diff --git a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs index 9ea5f1d54..673beb849 100644 --- a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs @@ -32,14 +32,17 @@ namespace Squidex.Areas.Api.Controllers.Contents public sealed class ContentsController : ApiController { private readonly IContentQueryService contentQuery; + private readonly IContentVersionLoader contentVersionLoader; private readonly IGraphQLService graphQl; public ContentsController(ICommandBus commandBus, IContentQueryService contentQuery, + IContentVersionLoader contentVersionLoader, IGraphQLService graphQl) : base(commandBus) { this.contentQuery = contentQuery; + this.contentVersionLoader = contentVersionLoader; this.graphQl = graphQl; } @@ -195,7 +198,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task GetContentVersion(string app, string name, Guid id, int version) { - var content = await contentQuery.FindContentAsync(App, name, User, id, version); + var content = await contentVersionLoader.LoadAsync(id, version); var response = SimpleMapper.Map(content.Content, new ContentDto()); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index 669ea6d2f..ae5bc90fc 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs @@ -517,7 +517,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }} }}"; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) .Returns((schema, content)); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); @@ -610,7 +610,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var refContents = new List { contentRef }; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) .Returns((schema, content)); A.CallTo(() => contentQuery.QueryAsync(app, schema.Id.ToString(), user, false, A>.That.Matches(x => x.Contains(contentRefId)))) @@ -670,7 +670,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var refAssets = new List { assetRef }; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) .Returns((schema, content)); A.CallTo(() => assetRepository.QueryAsync(app.Id, A>.That.Matches(x => x.Contains(assetRefId)))) @@ -729,7 +729,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }} }}"; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) .Returns((schema, content)); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); From 67adf346bed99a7778a52bdee1e37a9141d777ad Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 28 Apr 2018 10:52:47 +0200 Subject: [PATCH 2/4] Query service improved. --- .../ConvertContent/ContentConverter.cs | 29 ++++- .../ValidateContent/JsonValueConverter.cs | 2 +- .../Contents/ContentQueryService.cs | 108 ++++++++++++------ .../Contents/ContentVersionLoader.cs | 45 +------- .../Contents/IContentQueryService.cs | 9 +- .../Contents/IContentVersionLoader.cs | 3 +- .../Contents/QueryContext.cs | 8 +- .../DomainObjectState.cs | 2 +- .../Grains/EventConsumerGrain.cs | 2 +- .../Controllers/Content/ContentsController.cs | 63 +++------- .../Controllers/Schemas/Models/AddFieldDto.cs | 2 +- src/Squidex/Config/ServiceExtensions.cs | 2 +- .../ConvertContent/ContentConversionTests.cs | 23 ++++ .../Contents/ContentQueryServiceTests.cs | 54 ++++++--- .../Contents/GraphQL/GraphQLQueriesTests.cs | 22 ++-- .../Caching/HttpRequestCacheTests.cs | 2 +- .../Commands/DomainObjectGrainTests.cs | 2 +- .../Orleans/BootstrapTests.cs | 2 +- .../Tasks/AsyncLockPoolTests.cs | 2 +- .../Tasks/AsyncLockTests.cs | 2 +- .../ThreadingUsageTrackerTests.cs | 2 +- 21 files changed, 211 insertions(+), 175 deletions(-) diff --git a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs index 5bc9a1834..f6c2e0c47 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs @@ -13,6 +13,7 @@ using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Infrastructure; using Squidex.Infrastructure.Json; @@ -106,7 +107,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent return result; } - public static NamedContentData ToApiModel(this NamedContentData content, Schema schema, LanguagesConfig languagesConfig, bool excludeHidden = true) + public static NamedContentData ToApiModel(this NamedContentData content, Schema schema, LanguagesConfig languagesConfig, bool excludeHidden = true, bool checkTypeCompatibility = false) { Guard.NotNull(schema, nameof(schema)); Guard.NotNull(languagesConfig, nameof(languagesConfig)); @@ -123,6 +124,32 @@ namespace Squidex.Domain.Apps.Core.ConvertContent continue; } + if (checkTypeCompatibility) + { + var isValid = true; + + foreach (var value in fieldValue.Value.Values) + { + try + { + if (!value.IsNull()) + { + JsonValueConverter.ConvertValue(field, value); + } + } + catch + { + isValid = false; + break; + } + } + + if (!isValid) + { + continue; + } + } + var fieldResult = new ContentFieldData(); var fieldValues = fieldValue.Value; diff --git a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs index 9095331b3..54475f433 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent private JsonValueConverter(JToken value) { - this.Value = value; + Value = value; } public static object ConvertValue(Field field, JToken json) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs index e93f09281..fc8d77f94 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using Microsoft.OData; using Microsoft.OData.UriParser; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Contents.Edm; @@ -27,110 +28,130 @@ namespace Squidex.Domain.Apps.Entities.Contents public sealed class ContentQueryService : IContentQueryService { private readonly IContentRepository contentRepository; + private readonly IContentVersionLoader contentVersionLoader; private readonly IAppProvider appProvider; private readonly IScriptEngine scriptEngine; private readonly EdmModelBuilder modelBuilder; public ContentQueryService( IContentRepository contentRepository, + IContentVersionLoader contentVersionLoader, IAppProvider appProvider, IScriptEngine scriptEngine, EdmModelBuilder modelBuilder) { Guard.NotNull(contentRepository, nameof(contentRepository)); + Guard.NotNull(contentVersionLoader, nameof(contentVersionLoader)); Guard.NotNull(scriptEngine, nameof(scriptEngine)); Guard.NotNull(modelBuilder, nameof(modelBuilder)); Guard.NotNull(appProvider, nameof(appProvider)); this.contentRepository = contentRepository; + this.contentVersionLoader = contentVersionLoader; this.appProvider = appProvider; this.scriptEngine = scriptEngine; this.modelBuilder = modelBuilder; } - public async Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id) + public Task ThrowIfSchemaNotExistsAsync(IAppEntity app, string schemaIdOrName) + { + return GetSchemaAsync(app, schemaIdOrName); + } + + public async Task FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id, long version = -1) { Guard.NotNull(app, nameof(app)); Guard.NotNull(user, nameof(user)); + Guard.NotEmpty(id, nameof(id)); Guard.NotNullOrEmpty(schemaIdOrName, nameof(schemaIdOrName)); - var isFrontendClient = user.IsInClient("squidex-frontend"); + var schema = await GetSchemaAsync(app, schemaIdOrName); - var schema = await FindSchemaAsync(app, schemaIdOrName); + var isFrontendClient = IsFrontendClient(user); + var isVersioned = version > EtagVersion.Empty; - var content = await contentRepository.FindContentAsync(app, schema, id); + var content = + isVersioned ? + await FindContentByVersionAsync(id, version) : + await FindContentAsync(app, id, schema); - if (content == null || (content.Status != Status.Published && !isFrontendClient)) + if (content == null || (content.Status != Status.Published && !isFrontendClient) || content.SchemaId.Id != schema.Id) { throw new DomainObjectNotFoundException(id.ToString(), typeof(ISchemaEntity)); } - content = TransformContent(user, schema, Enumerable.Repeat(content, 1)).FirstOrDefault(); + content = TransformContent(app, schema, user, Enumerable.Repeat(content, 1), isVersioned, isFrontendClient).FirstOrDefault(); - return (schema, content); + return content; } - public async Task<(ISchemaEntity Schema, IResultList Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query) + public async Task> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query) { Guard.NotNull(app, nameof(app)); Guard.NotNull(user, nameof(user)); Guard.NotNullOrEmpty(schemaIdOrName, nameof(schemaIdOrName)); - var schema = await FindSchemaAsync(app, schemaIdOrName); + var schema = await GetSchemaAsync(app, schemaIdOrName); + + var isFrontendClient = IsFrontendClient(user); var parsedQuery = ParseQuery(app, query, schema); - var parsedStatus = ParseStatus(user, archived); + var parsedStatus = ParseStatus(isFrontendClient, archived); var contents = await contentRepository.QueryAsync(app, schema, parsedStatus.ToArray(), parsedQuery); - return TransformContents(user, schema, contents); + return TransformContents(app, schema, user, contents, false, isFrontendClient); } - public async Task<(ISchemaEntity Schema, IResultList Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet ids) + public async Task> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet ids) { Guard.NotNull(ids, nameof(ids)); Guard.NotNull(app, nameof(app)); Guard.NotNull(user, nameof(user)); Guard.NotNullOrEmpty(schemaIdOrName, nameof(schemaIdOrName)); - var schema = await FindSchemaAsync(app, schemaIdOrName); + var schema = await GetSchemaAsync(app, schemaIdOrName); + + var isFrontendClient = IsFrontendClient(user); - var parsedStatus = ParseStatus(user, archived); + var parsedStatus = ParseStatus(isFrontendClient, archived); var contents = await contentRepository.QueryAsync(app, schema, parsedStatus.ToArray(), ids); - return TransformContents(user, schema, contents); + return TransformContents(app, schema, user, contents, false, isFrontendClient); } - private (ISchemaEntity Schema, IResultList Contents) TransformContents(ClaimsPrincipal user, ISchemaEntity schema, IResultList contents) + private IResultList TransformContents(IAppEntity app, ISchemaEntity schema, ClaimsPrincipal user, + IResultList contents, + bool isTypeChecking, + bool isFrontendClient) { - var transformed = TransformContent(user, schema, contents); + var transformed = TransformContent(app, schema, user, contents, isTypeChecking, isFrontendClient); - return (schema, ResultList.Create(transformed, contents.Total)); + return ResultList.Create(transformed, contents.Total); } - private IEnumerable TransformContent(ClaimsPrincipal user, ISchemaEntity schema, IEnumerable contents) + private IEnumerable TransformContent(IAppEntity app, ISchemaEntity schema, ClaimsPrincipal user, + IEnumerable contents, + bool isTypeChecking, + bool isFrontendClient) { var scriptText = schema.ScriptQuery; - if (!string.IsNullOrWhiteSpace(scriptText)) - { - foreach (var content in contents) - { - var contentData = scriptEngine.Transform(new ScriptContext { User = user, Data = content.Data, ContentId = content.Id }, scriptText); - var contentResult = SimpleMapper.Map(content, new ContentEntity()); - - contentResult.Data = contentData; + var isScripting = !string.IsNullOrWhiteSpace(scriptText); - yield return contentResult; - } - } - else + foreach (var content in contents) { - foreach (var content in contents) + var result = SimpleMapper.Map(content, new ContentEntity()); + + if (!isFrontendClient && isScripting) { - yield return content; + result.Data = scriptEngine.Transform(new ScriptContext { User = user, Data = content.Data, ContentId = content.Id }, scriptText); } + + result.Data = result.Data.ToApiModel(schema.SchemaDef, app.LanguagesConfig, isFrontendClient, isTypeChecking); + + yield return result; } } @@ -148,7 +169,7 @@ namespace Squidex.Domain.Apps.Entities.Contents } } - public async Task FindSchemaAsync(IAppEntity app, string schemaIdOrName) + public async Task GetSchemaAsync(IAppEntity app, string schemaIdOrName) { Guard.NotNull(app, nameof(app)); @@ -172,11 +193,11 @@ namespace Squidex.Domain.Apps.Entities.Contents return schema; } - private static List ParseStatus(ClaimsPrincipal user, bool archived) + private static List ParseStatus(bool isFrontendClient, bool archived) { var status = new List(); - if (user.IsInClient("squidex-frontend")) + if (isFrontendClient) { if (archived) { @@ -195,5 +216,20 @@ namespace Squidex.Domain.Apps.Entities.Contents return status; } + + private Task FindContentByVersionAsync(Guid id, long version) + { + return contentVersionLoader.LoadAsync(id, version); + } + + private Task FindContentAsync(IAppEntity app, Guid id, ISchemaEntity schema) + { + return contentRepository.FindContentAsync(app, schema, id); + } + + private static bool IsFrontendClient(ClaimsPrincipal user) + { + return user.IsInClient("squidex-frontend"); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs index 04f02b1e0..75b4bb248 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs @@ -7,11 +7,8 @@ using System; using System.Threading.Tasks; -using NodaTime; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Contents.State; -using Squidex.Domain.Apps.Entities.Schemas; -using Squidex.Domain.Apps.Entities.Schemas.State; using Squidex.Infrastructure; using Squidex.Infrastructure.States; @@ -32,7 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents this.registry = registry; } - public async Task<(ISchemaEntity Schema, IContentEntity Content)> LoadAsync(Guid id, int version) + public async Task LoadAsync(Guid id, long version) { var content = new ContentState(); @@ -51,45 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Contents throw new DomainObjectNotFoundException(id.ToString(), typeof(IContentEntity)); } - var (now, then) = await ReadSchema(content.SchemaId.Id, content.LastModified); - - foreach (var key in content.Data.Keys) - { - if (IsFieldRemovedOrChanged(then.SchemaDef, now.SchemaDef, key)) - { - content.Data.Remove(key); - } - } - - return (then, content); - } - - private static bool IsFieldRemovedOrChanged(Schema schemaThen, Schema schemaNow, string key) - { - return - !schemaThen.FieldsByName.TryGetValue(key, out var fieldThen) || - !schemaNow.FieldsByName.TryGetValue(key, out var fieldNow) || - fieldThen.GetType() != fieldNow.GetType(); - } - - private async Task<(ISchemaEntity, ISchemaEntity)> ReadSchema(Guid schemaId, Instant lastUpdate) - { - var state = new SchemaState(); - var stateAtVersion = (SchemaState)null; - - var persistence = store.WithEventSourcing(schemaId, e => - { - state = state.Apply(e, registry); - - if (state.LastModified < lastUpdate) - { - stateAtVersion = state; - } - }); - - await persistence.ReadAsync(); - - return (state, stateAtVersion); + return content; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs index c2581135f..205e3652c 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs @@ -10,19 +10,18 @@ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Apps; -using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Contents { public interface IContentQueryService { - Task<(ISchemaEntity Schema, IResultList Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet ids); + Task> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet ids); - Task<(ISchemaEntity Schema, IResultList Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query); + Task> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query); - Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id); + Task FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id, long version = EtagVersion.Any); - Task FindSchemaAsync(IAppEntity app, string schemaIdOrName); + Task ThrowIfSchemaNotExistsAsync(IAppEntity app, string schemaIdOrName); } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs index 54ae6a28b..e55988d52 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs @@ -7,12 +7,11 @@ using System; using System.Threading.Tasks; -using Squidex.Domain.Apps.Entities.Schemas; namespace Squidex.Domain.Apps.Entities.Contents { public interface IContentVersionLoader { - Task<(ISchemaEntity Schema, IContentEntity Content)> LoadAsync(Guid id, int version); + Task LoadAsync(Guid id, long version); } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs index bf5dbd7ab..daf4709f0 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs @@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (content == null) { - content = (await contentQuery.FindContentAsync(app, schemaId.ToString(), user, id)).Content; + content = await contentQuery.FindContentAsync(app, schemaId.ToString(), user, id); if (content != null) { @@ -96,12 +96,12 @@ namespace Squidex.Domain.Apps.Entities.Contents { var result = await contentQuery.QueryAsync(app, schemaIdOrName, user, false, query); - foreach (var content in result.Contents) + foreach (var content in result) { cachedContents[content.Id] = content; } - return result.Contents; + return result; } public async Task> GetReferencedAssetsAsync(ICollection ids) @@ -133,7 +133,7 @@ namespace Squidex.Domain.Apps.Entities.Contents { var result = await contentQuery.QueryAsync(app, schemaId.ToString(), user, false, notLoadedContents); - foreach (var content in result.Contents) + foreach (var content in result) { cachedContents[content.Id] = content; } diff --git a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs index 13ce32acc..210617ff6 100644 --- a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs +++ b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs @@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities public Instant LastModified { get; set; } [JsonProperty] - public long Version { get; set; } = EtagVersion.Empty; + public long Version { get; set; } public T Clone() { diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs index 93580c7f0..433cf5caa 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs +++ b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs @@ -63,7 +63,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains public Task> GetStateAsync() { - return Task.FromResult(state.ToInfo(this.eventConsumer.Name).AsImmutable()); + return Task.FromResult(state.ToInfo(eventConsumer.Name).AsImmutable()); } public Task OnEventAsync(Immutable subscription, Immutable storedEvent) diff --git a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs index 673beb849..ff5dd942c 100644 --- a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs @@ -15,7 +15,6 @@ using NodaTime.Text; using NSwag.Annotations; using Squidex.Areas.Api.Controllers.Contents.Models; using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.GraphQL; @@ -32,17 +31,14 @@ namespace Squidex.Areas.Api.Controllers.Contents public sealed class ContentsController : ApiController { private readonly IContentQueryService contentQuery; - private readonly IContentVersionLoader contentVersionLoader; private readonly IGraphQLService graphQl; public ContentsController(ICommandBus commandBus, IContentQueryService contentQuery, - IContentVersionLoader contentVersionLoader, IGraphQLService graphQl) : base(commandBus) { this.contentQuery = contentQuery; - this.contentVersionLoader = contentVersionLoader; this.graphQl = graphQl; } @@ -122,18 +118,8 @@ namespace Squidex.Areas.Api.Controllers.Contents var response = new ContentsDto { - Total = result.Contents.Total, - Items = result.Contents.Take(200).Select(item => - { - var itemModel = SimpleMapper.Map(item, new ContentDto()); - - if (item.Data != null) - { - itemModel.Data = item.Data.ToApiModel(result.Schema.SchemaDef, App.LanguagesConfig, !isFrontendClient); - } - - return itemModel; - }).ToArray() + Total = result.Total, + Items = result.Take(200).Select(item => SimpleMapper.Map(item, new ContentDto())).ToArray() }; Response.Headers["Surrogate-Key"] = string.Join(" ", response.Items.Select(x => x.Id)); @@ -160,19 +146,12 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task GetContent(string app, string name, Guid id) { - var (schema, entity) = await contentQuery.FindContentAsync(App, name, User, id); + var content = await contentQuery.FindContentAsync(App, name, User, id); - var response = SimpleMapper.Map(entity, new ContentDto()); + var response = SimpleMapper.Map(content, new ContentDto()); - if (entity.Data != null) - { - var isFrontendClient = User.IsFrontendClient(); - - response.Data = entity.Data.ToApiModel(schema.SchemaDef, App.LanguagesConfig, !isFrontendClient); - } - - Response.Headers["ETag"] = entity.Version.ToString(); - Response.Headers["Surrogate-Key"] = entity.Id.ToString(); + Response.Headers["ETag"] = content.Version.ToString(); + Response.Headers["Surrogate-Key"] = content.Id.ToString(); return Ok(response); } @@ -198,18 +177,12 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task GetContentVersion(string app, string name, Guid id, int version) { - var content = await contentVersionLoader.LoadAsync(id, version); - - var response = SimpleMapper.Map(content.Content, new ContentDto()); + var content = await contentQuery.FindContentAsync(App, name, User, id, version); - if (content.Content.Data != null) - { - var isFrontendClient = User.IsFrontendClient(); - - response.Data = content.Content.Data.ToApiModel(content.Schema.SchemaDef, App.LanguagesConfig, !isFrontendClient); - } + var response = SimpleMapper.Map(content, new ContentDto()); - Response.Headers["ETag"] = version.ToString(); + Response.Headers["ETag"] = content.Version.ToString(); + Response.Headers["Surrogate-Key"] = content.Id.ToString(); return Ok(response.Data); } @@ -235,7 +208,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task PostContent(string app, string name, [FromBody] NamedContentData request, [FromQuery] bool publish = false) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = new CreateContent { ContentId = Guid.NewGuid(), Data = request.ToCleaned(), Publish = publish }; @@ -268,7 +241,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task PutContent(string app, string name, Guid id, [FromBody] NamedContentData request) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = new UpdateContent { ContentId = id, Data = request.ToCleaned() }; var context = await CommandBus.PublishAsync(command); @@ -300,7 +273,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task PatchContent(string app, string name, Guid id, [FromBody] NamedContentData request) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = new PatchContent { ContentId = id, Data = request.ToCleaned() }; var context = await CommandBus.PublishAsync(command); @@ -332,7 +305,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task PublishContent(string app, string name, Guid id, string dueTime = null) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = CreateCommand(id, Status.Published, dueTime); @@ -362,7 +335,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task UnpublishContent(string app, string name, Guid id, string dueTime = null) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = CreateCommand(id, Status.Draft, dueTime); @@ -392,7 +365,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task ArchiveContent(string app, string name, Guid id, string dueTime = null) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = CreateCommand(id, Status.Archived, dueTime); @@ -422,7 +395,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task RestoreContent(string app, string name, Guid id, string dueTime = null) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = CreateCommand(id, Status.Draft, dueTime); @@ -450,7 +423,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(1)] public async Task DeleteContent(string app, string name, Guid id) { - await contentQuery.FindSchemaAsync(App, name); + await contentQuery.ThrowIfSchemaNotExistsAsync(App, name); var command = new DeleteContent { ContentId = id }; diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs index 515c2eaa3..8ce6cd996 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs @@ -33,7 +33,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models public AddField ToCommand() { - return SimpleMapper.Map(this, new AddField { Properties = this.Properties.ToProperties() }); + return SimpleMapper.Map(this, new AddField { Properties = Properties.ToProperties() }); } } } \ No newline at end of file diff --git a/src/Squidex/Config/ServiceExtensions.cs b/src/Squidex/Config/ServiceExtensions.cs index c9ed0ab12..0d5838820 100644 --- a/src/Squidex/Config/ServiceExtensions.cs +++ b/src/Squidex/Config/ServiceExtensions.cs @@ -33,7 +33,7 @@ namespace Squidex.Config { if (typeof(TInterface) != typeof(T)) { - this.services.AddSingleton(typeof(TInterface), c => + services.AddSingleton(typeof(TInterface), c => { return c.GetRequiredService(); }); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs index 3d95e6342..6b0ff52ab 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs @@ -315,6 +315,29 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent Assert.Equal(expected, actual); } + [Fact] + public void Should_not_include_invalid_field_types() + { + var input = + new NamedContentData() + .AddField("field1", + new ContentFieldData() + .AddValue("iv", "INVALID")) + .AddField("field2", + new ContentFieldData() + .AddValue("iv", 2)); + + var actual = input.ToApiModel(schema, languagesConfig, checkTypeCompatibility: true); + + var expected = + new NamedContentData() + .AddField("field2", + new ContentFieldData() + .AddValue("iv", 2)); + + Assert.Equal(expected, actual); + } + [Fact] public void Should_return_original_when_no_language_preferences_defined() { diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs index 38a364b07..4cca28da4 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs @@ -12,7 +12,9 @@ using System.Security.Claims; using System.Threading.Tasks; using FakeItEasy; using Microsoft.OData.UriParser; +using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Contents.Edm; @@ -27,6 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents public class ContentQueryServiceTests { private readonly IContentRepository contentRepository = A.Fake(); + private readonly IContentVersionLoader contentVersionLoader = A.Fake(); private readonly IScriptEngine scriptEngine = A.Fake(); private readonly ISchemaEntity schema = A.Fake(); private readonly IContentEntity content = A.Fake(); @@ -49,12 +52,15 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => app.Id).Returns(appId); A.CallTo(() => app.Name).Returns(appName); + A.CallTo(() => app.LanguagesConfig).Returns(LanguagesConfig.English); A.CallTo(() => content.Id).Returns(contentId); A.CallTo(() => content.Data).Returns(contentData); A.CallTo(() => content.Status).Returns(Status.Published); - sut = new ContentQueryService(contentRepository, appProvider, scriptEngine, modelBuilder); + A.CallTo(() => schema.SchemaDef).Returns(new Schema("my-schema")); + + sut = new ContentQueryService(contentRepository, contentVersionLoader, appProvider, scriptEngine, modelBuilder); } [Fact] @@ -63,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)) .Returns(schema); - var result = await sut.FindSchemaAsync(app, schemaId.ToString()); + var result = await sut.GetSchemaAsync(app, schemaId.ToString()); Assert.Equal(schema, result); } @@ -74,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema")) .Returns(schema); - var result = await sut.FindSchemaAsync(app, "my-schema"); + var result = await sut.GetSchemaAsync(app, "my-schema"); Assert.Equal(schema, result); } @@ -85,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema")) .Returns((ISchemaEntity)null); - await Assert.ThrowsAsync(() => sut.FindSchemaAsync(app, "my-schema")); + await Assert.ThrowsAsync(() => sut.GetSchemaAsync(app, "my-schema")); } [Fact] @@ -104,10 +110,28 @@ namespace Squidex.Domain.Apps.Entities.Contents var result = await sut.FindContentAsync(app, schemaId.ToString(), user, contentId); - Assert.Equal(schema, result.Schema); + Assert.Equal(contentTransformed, result.Data); + Assert.Equal(content.Id, result.Id); + } - Assert.Equal(contentTransformed, result.Content.Data); - Assert.Equal(content.Id, result.Content.Id); + [Fact] + public async Task Should_return_versioned_content_from_repository_and_transform() + { + A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)) + .Returns(schema); + A.CallTo(() => contentVersionLoader.LoadAsync(contentId, 10)) + .Returns(content); + + A.CallTo(() => schema.ScriptQuery) + .Returns(""); + + A.CallTo(() => scriptEngine.Transform(A.That.Matches(x => x.User == user && x.ContentId == contentId && ReferenceEquals(x.Data, contentData)), "")) + .Returns(contentTransformed); + + var result = await sut.FindContentAsync(app, schemaId.ToString(), user, contentId, 10); + + Assert.Equal(contentTransformed, result.Data); + Assert.Equal(content.Id, result.Id); } [Fact] @@ -161,12 +185,10 @@ namespace Squidex.Domain.Apps.Entities.Contents var result = await sut.QueryAsync(app, schemaId.ToString(), user, archive, string.Empty); - Assert.Equal(schema, result.Schema); + Assert.Equal(contentData, result[0].Data); + Assert.Equal(content.Id, result[0].Id); - Assert.Equal(contentData, result.Contents[0].Data); - Assert.Equal(content.Id, result.Contents[0].Id); - - Assert.Equal(123, result.Contents.Total); + Assert.Equal(123, result.Total); } private async Task TestManyIdRequest(bool isFrontend, bool archive, HashSet ids, params Status[] status) @@ -178,12 +200,10 @@ namespace Squidex.Domain.Apps.Entities.Contents var result = await sut.QueryAsync(app, schemaId.ToString(), user, archive, ids); - Assert.Equal(schema, result.Schema); - - Assert.Equal(contentData, result.Contents[0].Data); - Assert.Equal(content.Id, result.Contents[0].Id); + Assert.Equal(contentData, result[0].Data); + Assert.Equal(content.Id, result[0].Id); - Assert.Equal(123, result.Contents.Total); + Assert.Equal(123, result.Total); } private void SetupClaims(bool isFrontend) diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index ae5bc90fc..1c17347ed 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs @@ -282,7 +282,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var contents = new List { content }; A.CallTo(() => contentQuery.QueryAsync(app, schema.Id.ToString(), user, false, "?$top=30&$skip=5")) - .Returns((schema, ResultList.Create(contents, 0))); + .Returns(ResultList.Create(contents, 0)); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); @@ -400,7 +400,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var contents = new List { content }; A.CallTo(() => contentQuery.QueryAsync(app, schema.Id.ToString(), user, false, "?$top=30&$skip=5")) - .Returns((schema, ResultList.Create(contents, 10))); + .Returns(ResultList.Create(contents, 10)); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); @@ -517,8 +517,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }} }}"; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) - .Returns((schema, content)); + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + .Returns(content); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); @@ -610,11 +610,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var refContents = new List { contentRef }; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) - .Returns((schema, content)); + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + .Returns(content); A.CallTo(() => contentQuery.QueryAsync(app, schema.Id.ToString(), user, false, A>.That.Matches(x => x.Contains(contentRefId)))) - .Returns((schema, ResultList.Create(refContents, 0))); + .Returns(ResultList.Create(refContents, 0)); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); @@ -670,8 +670,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var refAssets = new List { assetRef }; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) - .Returns((schema, content)); + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + .Returns(content); A.CallTo(() => assetRepository.QueryAsync(app.Id, A>.That.Matches(x => x.Contains(assetRefId)))) .Returns(ResultList.Create(refAssets, 0)); @@ -729,8 +729,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }} }}"; - A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) - .Returns((schema, content)); + A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) + .Returns(content); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); diff --git a/tests/Squidex.Infrastructure.Tests/Caching/HttpRequestCacheTests.cs b/tests/Squidex.Infrastructure.Tests/Caching/HttpRequestCacheTests.cs index 271c7b76a..c8a64dfa5 100644 --- a/tests/Squidex.Infrastructure.Tests/Caching/HttpRequestCacheTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Caching/HttpRequestCacheTests.cs @@ -13,7 +13,7 @@ using Xunit; namespace Squidex.Infrastructure.Caching { - public sealed class HttpRequestCacheTests + public class HttpRequestCacheTests { private readonly IHttpContextAccessor httpContextAccessor = A.Fake(); private readonly IRequestCache sut; diff --git a/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs b/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs index 0a28ef533..95d2976ab 100644 --- a/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs @@ -19,7 +19,7 @@ using Xunit; namespace Squidex.Infrastructure.Commands { - public sealed class DomainObjectGrainTests + public class DomainObjectGrainTests { private readonly IStore store = A.Fake>(); private readonly IPersistence persistence = A.Fake>(); diff --git a/tests/Squidex.Infrastructure.Tests/Orleans/BootstrapTests.cs b/tests/Squidex.Infrastructure.Tests/Orleans/BootstrapTests.cs index 037aab083..9db48683e 100644 --- a/tests/Squidex.Infrastructure.Tests/Orleans/BootstrapTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Orleans/BootstrapTests.cs @@ -17,7 +17,7 @@ using Xunit; namespace Squidex.Infrastructure.Orleans { - public sealed class BootstrapTests + public class BootstrapTests { private readonly IBackgroundGrain grain = A.Fake(); private readonly Bootstrap sut; diff --git a/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockPoolTests.cs b/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockPoolTests.cs index 8253783e3..a465fde93 100644 --- a/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockPoolTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockPoolTests.cs @@ -12,7 +12,7 @@ using Xunit; namespace Squidex.Infrastructure.Tasks { - public sealed class AsyncLockPoolTests + public class AsyncLockPoolTests { [Fact] public async Task Should_lock() diff --git a/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockTests.cs b/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockTests.cs index 58765fb66..99863cf13 100644 --- a/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockTests.cs @@ -12,7 +12,7 @@ using Xunit; namespace Squidex.Infrastructure.Tasks { - public sealed class AsyncLockTests + public class AsyncLockTests { [Fact] public async Task Should_lock() diff --git a/tests/Squidex.Infrastructure.Tests/UsageTracking/ThreadingUsageTrackerTests.cs b/tests/Squidex.Infrastructure.Tests/UsageTracking/ThreadingUsageTrackerTests.cs index 40a22b56a..41216ed30 100644 --- a/tests/Squidex.Infrastructure.Tests/UsageTracking/ThreadingUsageTrackerTests.cs +++ b/tests/Squidex.Infrastructure.Tests/UsageTracking/ThreadingUsageTrackerTests.cs @@ -14,7 +14,7 @@ using Xunit; namespace Squidex.Infrastructure.UsageTracking { - public sealed class ThreadingUsageTrackerTests + public class ThreadingUsageTrackerTests { private readonly MemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); private readonly IUsageTracker inner = A.Fake(); From 38c716f42b3fd3a973875b7fb366c58e1bb4a684 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 28 Apr 2018 11:54:30 +0200 Subject: [PATCH 3/4] UI fixes --- .../Areas/Api/Controllers/Content/ContentsController.cs | 6 +++--- src/Squidex/Config/Domain/EntitiesServices.cs | 3 +++ .../content/pages/content/content-page.component.ts | 2 +- src/Squidex/app/shared/state/contents.state.ts | 2 +- src/Squidex/app/theme/_bootstrap.scss | 2 +- src/Squidex/app/theme/_panels.scss | 4 +++- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs index ff5dd942c..8c6bebb8a 100644 --- a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs @@ -119,7 +119,7 @@ namespace Squidex.Areas.Api.Controllers.Contents var response = new ContentsDto { Total = result.Total, - Items = result.Take(200).Select(item => SimpleMapper.Map(item, new ContentDto())).ToArray() + Items = result.Take(200).Select(item => SimpleMapper.Map(item, new ContentDto { Data = item.Data })).ToArray() }; Response.Headers["Surrogate-Key"] = string.Join(" ", response.Items.Select(x => x.Id)); @@ -148,7 +148,7 @@ namespace Squidex.Areas.Api.Controllers.Contents { var content = await contentQuery.FindContentAsync(App, name, User, id); - var response = SimpleMapper.Map(content, new ContentDto()); + var response = SimpleMapper.Map(content, new ContentDto { Data = content.Data }); Response.Headers["ETag"] = content.Version.ToString(); Response.Headers["Surrogate-Key"] = content.Id.ToString(); @@ -179,7 +179,7 @@ namespace Squidex.Areas.Api.Controllers.Contents { var content = await contentQuery.FindContentAsync(App, name, User, id, version); - var response = SimpleMapper.Map(content, new ContentDto()); + var response = SimpleMapper.Map(content, new ContentDto { Data = content.Data }); Response.Headers["ETag"] = content.Version.ToString(); Response.Headers["Surrogate-Key"] = content.Id.ToString(); diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index ad0389c07..5ce312227 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -60,6 +60,9 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); + services.AddSingletonAs() + .As(); + services.AddSingletonAs() .As(); diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.ts b/src/Squidex/app/features/content/pages/content/content-page.component.ts index 645713ba9..1228ee551 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.ts +++ b/src/Squidex/app/features/content/pages/content/content-page.component.ts @@ -153,7 +153,7 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, if (this.content) { this.contentsState.loadVersion(this.content, version) .subscribe(dto => { - if (this.content.version.value !== version.toString()) { + if (this.content.version.value !== version.value) { this.contentVersion = version; } else { this.contentVersion = null; diff --git a/src/Squidex/app/shared/state/contents.state.ts b/src/Squidex/app/shared/state/contents.state.ts index 8b4d70759..49034cfe0 100644 --- a/src/Squidex/app/shared/state/contents.state.ts +++ b/src/Squidex/app/shared/state/contents.state.ts @@ -348,7 +348,7 @@ export abstract class ContentsStateBase extends State { } public loadVersion(content: ContentDto, version: Version): Observable> { - return this.contentsService.getVersionData(this.appName, this.schemaName, content.id, new Version(version.toString())) + return this.contentsService.getVersionData(this.appName, this.schemaName, content.id, version) .notify(this.dialogs); } diff --git a/src/Squidex/app/theme/_bootstrap.scss b/src/Squidex/app/theme/_bootstrap.scss index 69e8b5981..e1b74d32c 100644 --- a/src/Squidex/app/theme/_bootstrap.scss +++ b/src/Squidex/app/theme/_bootstrap.scss @@ -56,7 +56,7 @@ } .alert-link { - color: $color-dark-foreground; + color: $color-dark-foreground !important; font-size: inherit; font-weight: normal; text-decoration: underline; diff --git a/src/Squidex/app/theme/_panels.scss b/src/Squidex/app/theme/_panels.scss index adec1955e..3918a476b 100644 --- a/src/Squidex/app/theme/_panels.scss +++ b/src/Squidex/app/theme/_panels.scss @@ -124,7 +124,9 @@ } a { - color: $color-dark-foreground; + & { + color: $color-dark-foreground !important; + } &, &:hover { From 671d03b08c23f788c9b28e7b90e1a7dd2e22a247 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 28 Apr 2018 12:28:44 +0200 Subject: [PATCH 4/4] Migration added. --- .../Contents/MongoContentRepository.cs | 5 +++ .../Contents/ContentVersionLoader.cs | 1 + .../DomainObjectState.cs | 2 +- src/Squidex/Config/Domain/EntitiesServices.cs | 5 ++- tests/RunCoverage.ps1 | 2 +- .../Contents/ContentQueryServiceTests.cs | 42 +++++++++++++++++-- tools/Migrate_01/Migrate_01.csproj | 1 + tools/Migrate_01/MigrationPath.cs | 4 +- .../Migrations/DeleteArchiveCollection.cs | 32 ++++++++++++++ tools/Migrate_01/Migrations/RebuildAssets.cs | 2 +- .../Migrate_01/Migrations/RebuildContents.cs | 2 +- 11 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 tools/Migrate_01/Migrations/DeleteArchiveCollection.cs diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs index 50d725d35..69d51774d 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs @@ -152,5 +152,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents callback(c); }); } + + public Task DeleteArchiveAsync() + { + return Database.DropCollectionAsync("States_Contents_Archive"); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs index 75b4bb248..d7c0dd7f1 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs @@ -38,6 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (content.Version < version) { content = content.Apply(e); + content.Version++; } }); diff --git a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs index 210617ff6..13ce32acc 100644 --- a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs +++ b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs @@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities public Instant LastModified { get; set; } [JsonProperty] - public long Version { get; set; } + public long Version { get; set; } = EtagVersion.Empty; public T Clone() { diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index 5ce312227..6c6b98f40 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -161,13 +161,16 @@ namespace Squidex.Config.Domain services.AddTransientAs() .As(); + services.AddTransientAs() + .As(); + services.AddTransientAs() .As(); services.AddTransientAs() .As(); - services.AddTransientAs() + services.AddTransientAs() .As(); services.AddTransientAs() diff --git a/tests/RunCoverage.ps1 b/tests/RunCoverage.ps1 index 3b46fc1dc..27b2b5e1a 100644 --- a/tests/RunCoverage.ps1 +++ b/tests/RunCoverage.ps1 @@ -76,6 +76,6 @@ if ($all -Or $web) { -oldStyle } -&"$folderHome\.nuget\packages\ReportGenerator\3.1.1\tools\ReportGenerator.exe" ` +&"$folderHome\.nuget\packages\ReportGenerator\3.1.2\tools\ReportGenerator.exe" ` -reports:"$folderWorking\$folderReports\*.xml" ` -targetdir:"$folderWorking\$folderReports\Output" \ No newline at end of file diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs index 4cca28da4..654821aa3 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs @@ -147,25 +147,37 @@ namespace Squidex.Domain.Apps.Entities.Contents } [Fact] - public async Task Should_return_contents_with_ids_from_repository_and_transform() + public async Task Should_return_contents_with_ids_from_repository() { await TestManyIdRequest(true, false, new HashSet { Guid.NewGuid() }, Status.Draft, Status.Published); } [Fact] - public async Task Should_return_non_archived_contents_from_repository_and_transform() + public async Task Should_return_contents_with_ids_from_repository_and_transform_as_non_frontend() + { + await TestManyIdRequest(false, false, new HashSet { Guid.NewGuid() }, Status.Published); + } + + [Fact] + public async Task Should_return_non_archived_contents_from_repository() { await TestManyRequest(true, false, Status.Draft, Status.Published); } [Fact] - public async Task Should_return_archived_contents_from_repository_and_transform() + public async Task Should_return_non_archived_contents_from_repository_and_transform_as_non_frontend() + { + await TestManyRequest(false, false, Status.Published); + } + + [Fact] + public async Task Should_return_archived_contents_from_repository() { await TestManyRequest(true, true, Status.Archived); } [Fact] - public async Task Should_return_draft_contents_from_repository_and_transform() + public async Task Should_return_draft_contents_from_repository() { await TestManyRequest(false, false, Status.Published); } @@ -189,6 +201,17 @@ namespace Squidex.Domain.Apps.Entities.Contents Assert.Equal(content.Id, result[0].Id); Assert.Equal(123, result.Total); + + if (!isFrontend) + { + A.CallTo(() => scriptEngine.Transform(A.Ignored, A.Ignored)) + .MustHaveHappened(Repeated.Exactly.Times(result.Count)); + } + else + { + A.CallTo(() => scriptEngine.Transform(A.Ignored, A.Ignored)) + .MustNotHaveHappened(); + } } private async Task TestManyIdRequest(bool isFrontend, bool archive, HashSet ids, params Status[] status) @@ -204,6 +227,17 @@ namespace Squidex.Domain.Apps.Entities.Contents Assert.Equal(content.Id, result[0].Id); Assert.Equal(123, result.Total); + + if (!isFrontend) + { + A.CallTo(() => scriptEngine.Transform(A.Ignored, A.Ignored)) + .MustHaveHappened(Repeated.Exactly.Times(result.Count)); + } + else + { + A.CallTo(() => scriptEngine.Transform(A.Ignored, A.Ignored)) + .MustNotHaveHappened(); + } } private void SetupClaims(bool isFrontend) diff --git a/tools/Migrate_01/Migrate_01.csproj b/tools/Migrate_01/Migrate_01.csproj index 9db8eb6c0..27d9fd97a 100644 --- a/tools/Migrate_01/Migrate_01.csproj +++ b/tools/Migrate_01/Migrate_01.csproj @@ -4,6 +4,7 @@ + diff --git a/tools/Migrate_01/MigrationPath.cs b/tools/Migrate_01/MigrationPath.cs index ff7efb52d..81eb97d0d 100644 --- a/tools/Migrate_01/MigrationPath.cs +++ b/tools/Migrate_01/MigrationPath.cs @@ -15,7 +15,7 @@ namespace Migrate_01 { public sealed class MigrationPath : IMigrationPath { - private const int CurrentVersion = 7; + private const int CurrentVersion = 8; private readonly IServiceProvider serviceProvider; public MigrationPath(IServiceProvider serviceProvider) @@ -56,6 +56,8 @@ namespace Migrate_01 migrations.Add(serviceProvider.GetRequiredService()); } + migrations.Add(serviceProvider.GetRequiredService()); + return (CurrentVersion, migrations); } } diff --git a/tools/Migrate_01/Migrations/DeleteArchiveCollection.cs b/tools/Migrate_01/Migrations/DeleteArchiveCollection.cs new file mode 100644 index 000000000..0ae22145e --- /dev/null +++ b/tools/Migrate_01/Migrations/DeleteArchiveCollection.cs @@ -0,0 +1,32 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.MongoDb.Contents; +using Squidex.Infrastructure.Migrations; + +namespace Migrate_01.Migrations +{ + public sealed class DeleteArchiveCollection : IMigration + { + private readonly IContentRepository contentRepository; + + public DeleteArchiveCollection(IContentRepository contentRepository) + { + this.contentRepository = contentRepository; + } + + public async Task UpdateAsync() + { + if (contentRepository is MongoContentRepository mongoContentRepository) + { + await mongoContentRepository.DeleteArchiveAsync(); + } + } + } +} diff --git a/tools/Migrate_01/Migrations/RebuildAssets.cs b/tools/Migrate_01/Migrations/RebuildAssets.cs index 2714c1269..db9ad1bd6 100644 --- a/tools/Migrate_01/Migrations/RebuildAssets.cs +++ b/tools/Migrate_01/Migrations/RebuildAssets.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.Migrations; namespace Migrate_01 { - public class RebuildAssets : IMigration + public sealed class RebuildAssets : IMigration { private readonly Rebuilder rebuilder; diff --git a/tools/Migrate_01/Migrations/RebuildContents.cs b/tools/Migrate_01/Migrations/RebuildContents.cs index d7d11a7a7..53cd96961 100644 --- a/tools/Migrate_01/Migrations/RebuildContents.cs +++ b/tools/Migrate_01/Migrations/RebuildContents.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.Migrations; namespace Migrate_01.Migrations { - public class RebuildContents : IMigration + public sealed class RebuildContents : IMigration { private readonly Rebuilder rebuilder;