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.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs index 1d4cc508f..69d51774d 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 = @@ -181,11 +153,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents }); } - public override async Task ClearAsync() + public Task DeleteArchiveAsync() { - await Database.DropCollectionAsync("States_Contents_Archive"); - - await base.ClearAsync(); + return Database.DropCollectionAsync("States_Contents_Archive"); } } } 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..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,113 +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, long version = -1) + 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 = - version > EtagVersion.Empty ? - await contentRepository.FindContentAsync(app, schema, id, version) : - await contentRepository.FindContentAsync(app, schema, id); + 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; } } @@ -151,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)); @@ -175,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) { @@ -198,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 new file mode 100644 index 000000000..d7c0dd7f1 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs @@ -0,0 +1,55 @@ +// ========================================================================== +// 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.Core.Schemas; +using Squidex.Domain.Apps.Entities.Contents.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 LoadAsync(Guid id, long version) + { + var content = new ContentState(); + + var persistence = store.WithEventSourcing(id, e => + { + if (content.Version < version) + { + content = content.Apply(e); + content.Version++; + } + }); + + await persistence.ReadAsync(); + + if (content.Version != version) + { + throw new DomainObjectNotFoundException(id.ToString(), typeof(IContentEntity)); + } + + return content; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs index f14a7d4cd..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, long version = EtagVersion.Any); + 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 new file mode 100644 index 000000000..e55988d52 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs @@ -0,0 +1,17 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; + +namespace Squidex.Domain.Apps.Entities.Contents +{ + public interface IContentVersionLoader + { + 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/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.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 9ea5f1d54..8c6bebb8a 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; @@ -119,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 { Data = item.Data })).ToArray() }; Response.Headers["Surrogate-Key"] = string.Join(" ", response.Items.Select(x => x.Id)); @@ -157,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 { Data = content.Data }); - 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); } @@ -197,16 +179,10 @@ namespace Squidex.Areas.Api.Controllers.Contents { var content = await contentQuery.FindContentAsync(App, name, User, id, version); - var response = SimpleMapper.Map(content.Content, new ContentDto()); - - 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 { Data = content.Data }); - Response.Headers["ETag"] = version.ToString(); + Response.Headers["ETag"] = content.Version.ToString(); + Response.Headers["Surrogate-Key"] = content.Id.ToString(); return Ok(response.Data); } @@ -232,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 }; @@ -265,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); @@ -297,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); @@ -329,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); @@ -359,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); @@ -389,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); @@ -419,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); @@ -447,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/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index ad0389c07..6c6b98f40 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(); @@ -158,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/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/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 { 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.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..654821aa3 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); + } + + [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); - Assert.Equal(contentTransformed, result.Content.Data); - Assert.Equal(content.Id, result.Content.Id); + var result = await sut.FindContentAsync(app, schemaId.ToString(), user, contentId, 10); + + Assert.Equal(contentTransformed, result.Data); + Assert.Equal(content.Id, result.Id); } [Fact] @@ -123,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); } @@ -161,12 +197,21 @@ 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.Total); - Assert.Equal(123, result.Contents.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) @@ -178,12 +223,21 @@ 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[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.Total); - Assert.Equal(123, result.Contents.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/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index 669ea6d2f..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 }); @@ -518,7 +518,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }}"; A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) - .Returns((schema, content)); + .Returns(content); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); @@ -611,10 +611,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var refContents = new List { contentRef }; A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) - .Returns((schema, content)); + .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 }); @@ -671,7 +671,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)) - .Returns((schema, content)); + .Returns(content); A.CallTo(() => assetRepository.QueryAsync(app.Id, A>.That.Matches(x => x.Contains(assetRefId)))) .Returns(ResultList.Create(refAssets, 0)); @@ -730,7 +730,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }}"; A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId, EtagVersion.Any)) - .Returns((schema, content)); + .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(); 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;