From e2e0d65a3f68d8eafc0a2792e55cd767195e28ce Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 25 Sep 2017 19:02:40 +0200 Subject: [PATCH] Closes #119 Closes #120 --- Squidex.sln.DotSettings | 93 ++++++++++++++++++- .../Contents/MongoContentRepository.cs | 41 ++++++-- .../Contents/Visitors/FindExtensions.cs | 11 +-- .../Utils/MongoCollectionExtensions.cs | 8 +- .../Webhooks/MongoWebhookEventRepository.cs | 12 ++- .../Webhooks/MongoWebhookRepository.cs | 8 +- .../MongoWebhookRepository_EventHandling.cs | 4 +- .../Contents/ContentQueryService.cs | 70 +++++++++----- .../Contents/GraphQL/QueryContext.cs | 4 +- .../Contents/IContentQueryService.cs | 6 +- .../Repositories/IContentRepository.cs | 8 +- .../CQRS/Events/GetEventStoreSubscription.cs | 9 +- .../ContentApi/ContentsController.cs | 9 +- .../shared/references-editor.component.ts | 2 +- .../schemas/pages/schema/field.component.html | 2 +- .../pages/schema/schema-page.component.html | 5 +- .../angular/autocomplete.component.html | 2 +- .../framework/angular/dropdown.component.html | 2 +- .../Contents/ContentQueryServiceTests.cs | 63 +++++++++++-- .../Contents/GraphQLTests.cs | 4 +- 20 files changed, 290 insertions(+), 73 deletions(-) diff --git a/Squidex.sln.DotSettings b/Squidex.sln.DotSettings index 0f875a4d0..7563aa71c 100644 --- a/Squidex.sln.DotSettings +++ b/Squidex.sln.DotSettings @@ -1,12 +1,103 @@  + True + False + True + False + True + False + True + True + True + True + True + True + True + True + True + False + Truev + False + True + True + True + True + True + True + DO_NOT_SHOW + + DO_NOT_SHOW + WARNING + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + WARNING + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + WARNING + + ExplicitlyExcluded + TypeScript16 <?xml version="1.0" encoding="utf-16"?><Profile name="Header"><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile> <?xml version="1.0" encoding="utf-16"?><Profile name="Namespaces"><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile> <?xml version="1.0" encoding="utf-16"?><Profile name="Typescript"><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs></Profile> + + SingleQuoted + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + C:\Users\mail2\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v08_85ffde88\SolutionCaches ========================================================================== $FILENAME$ Squidex Headless CMS ========================================================================== Copyright (c) Squidex Group All rights reserved. -========================================================================== +========================================================================== + \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs index 453a879f2..9960878b8 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs @@ -72,7 +72,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents this.database = database; } - public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids, ODataUriParser odataQuery) + public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery) { var collection = GetCollection(app.Id); @@ -81,7 +81,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents { cursor = collection - .Find(odataQuery, ids, schema.Id, schema.SchemaDef, status) + .Find(odataQuery, schema.Id, schema.SchemaDef, status) .Take(odataQuery) .Skip(odataQuery) .Sort(odataQuery, schema.SchemaDef); @@ -95,24 +95,24 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents throw new ValidationException("This odata operation is not supported"); } - var entities = await cursor.ToListAsync(); + var contentEntities = await cursor.ToListAsync(); - foreach (var entity in entities) + foreach (var entity in contentEntities) { entity.ParseData(schema.SchemaDef); } - return entities; + return contentEntities; } - public Task CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids, ODataUriParser odataQuery) + public Task CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery) { var collection = GetCollection(app.Id); IFindFluent cursor; try { - cursor = collection.Find(odataQuery, ids, schema.Id, schema.SchemaDef, status); + cursor = collection.Find(odataQuery, schema.Id, schema.SchemaDef, status); } catch (NotSupportedException) { @@ -126,6 +126,33 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents return cursor.CountAsync(); } + public async Task CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids) + { + var collection = GetCollection(app.Id); + + var contentsCount = + await collection.Find(x => ids.Contains(x.Id)) + .CountAsync(); + + return contentsCount; + } + + public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids) + { + var collection = GetCollection(app.Id); + + var contentEntities = + await collection.Find(x => ids.Contains(x.Id)) + .ToListAsync(); + + foreach (var entity in contentEntities) + { + entity.ParseData(schema.SchemaDef); + } + + return contentEntities.OfType().ToList(); + } + public async Task> QueryNotFoundAsync(Guid appId, Guid schemaId, IList contentIds) { var collection = GetCollection(appId); diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/FindExtensions.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/FindExtensions.cs index 6152d7624..1abdb16a0 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/FindExtensions.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/FindExtensions.cs @@ -57,14 +57,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors return cursor; } - public static IFindFluent Find(this IMongoCollection cursor, ODataUriParser query, HashSet ids, Guid schemaId, Schema schema, Status[] status) + public static IFindFluent Find(this IMongoCollection cursor, ODataUriParser query, Guid schemaId, Schema schema, Status[] status) { - var filter = BuildQuery(query, ids, schemaId, schema, status); + var filter = BuildQuery(query, schemaId, schema, status); return cursor.Find(filter); } - public static FilterDefinition BuildQuery(ODataUriParser query, HashSet ids, Guid schemaId, Schema schema, Status[] status) + public static FilterDefinition BuildQuery(ODataUriParser query, Guid schemaId, Schema schema, Status[] status) { var filters = new List> { @@ -72,11 +72,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors Filter.In(x => x.Status, status) }; - if (ids != null && ids.Count > 0) - { - filters.Add(Filter.In(x => x.Id, ids)); - } - var filter = FilterBuilder.Build(query, schema); if (filter != null) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Utils/MongoCollectionExtensions.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Utils/MongoCollectionExtensions.cs index 320ff8d4c..78cd17175 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Utils/MongoCollectionExtensions.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Utils/MongoCollectionExtensions.cs @@ -38,7 +38,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Utils public static async Task UpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IMongoEntity, new() { - var entity = await collection.Find(t => t.Id == headers.AggregateId()).FirstOrDefaultAsync(); + var entity = + await collection.Find(t => t.Id == headers.AggregateId()) + .FirstOrDefaultAsync(); if (entity == null) { @@ -50,7 +52,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Utils public static async Task TryUpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IMongoEntity, new() { - var entity = await collection.Find(t => t.Id == headers.AggregateId()).FirstOrDefaultAsync(); + var entity = + await collection.Find(t => t.Id == headers.AggregateId()) + .FirstOrDefaultAsync(); if (entity != null) { diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventRepository.cs index db04caab3..d6a4325fc 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventRepository.cs @@ -54,16 +54,20 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks public async Task> QueryByAppAsync(Guid appId, int skip = 0, int take = 20) { - var entities = await Collection.Find(x => x.AppId == appId).Skip(skip).Limit(take).SortByDescending(x => x.Created).ToListAsync(); + var webhookEventEntities = + await Collection.Find(x => x.AppId == appId).Skip(skip).Limit(take).SortByDescending(x => x.Created) + .ToListAsync(); - return entities; + return webhookEventEntities; } public async Task FindAsync(Guid id) { - var entity = await Collection.Find(x => x.Id == id).FirstOrDefaultAsync(); + var webhookEventEntity = + await Collection.Find(x => x.Id == id) + .FirstOrDefaultAsync(); - return entity; + return webhookEventEntity; } public async Task CountByAppAsync(Guid appId) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository.cs index cb416dc07..d4dc24ac9 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository.cs @@ -46,7 +46,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks public async Task> QueryByAppAsync(Guid appId) { - var entities = await Collection.Find(x => x.AppId == appId).ToListAsync(); + var entities = + await Collection.Find(x => x.AppId == appId) + .ToListAsync(); return entities.OfType().ToList(); } @@ -97,7 +99,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks { inMemoryWebhooks = new Dictionary>(); - var webhooks = await Collection.Find(new BsonDocument()).ToListAsync(); + var webhooks = + await Collection.Find(new BsonDocument()) + .ToListAsync(); foreach (var webhook in webhooks) { diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository_EventHandling.cs index 192e57524..9646e8d62 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository_EventHandling.cs @@ -70,7 +70,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks { await EnsureWebooksLoadedAsync(); - var webhooks = await Collection.Find(t => t.SchemaIds.Contains(@event.SchemaId.Id)).ToListAsync(); + var webhooks = + await Collection.Find(t => t.SchemaIds.Contains(@event.SchemaId.Id)) + .ToListAsync(); foreach (var webhook in webhooks) { diff --git a/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs index 19d70ae8d..f2a8263c3 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs @@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Read.Contents return (schema, content); } - public async Task<(ISchemaEntity Schema, long Total, IReadOnlyList Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet ids, string query) + public async Task<(ISchemaEntity Schema, long Total, IReadOnlyList Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query) { Guard.NotNull(app, nameof(app)); Guard.NotNull(user, nameof(user)); @@ -83,27 +83,31 @@ namespace Squidex.Domain.Apps.Read.Contents var parsedQuery = ParseQuery(app, query, schema); - var status = new List(); + var status = ParseStatus(user, archived); - if (user.IsInClient("squidex-frontend")) - { - if (archived) - { - status.Add(Status.Archived); - } - else - { - status.Add(Status.Draft); - status.Add(Status.Published); - } - } - else - { - status.Add(Status.Published); - } + var taskForItems = contentRepository.QueryAsync(app, schema, status.ToArray(), parsedQuery); + var taskForCount = contentRepository.CountAsync(app, schema, status.ToArray(), parsedQuery); + + await Task.WhenAll(taskForItems, taskForCount); + + var list = TransformContent(user, schema, taskForItems.Result.ToList()); + + return (schema, taskForCount.Result, list); + } + + public async Task<(ISchemaEntity Schema, long Total, IReadOnlyList Items)> QueryWithCountAsync(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 taskForItems = contentRepository.QueryAsync(app, schema, status.ToArray(), ids, parsedQuery); - var taskForCount = contentRepository.CountAsync(app, schema, status.ToArray(), ids, parsedQuery); + var status = ParseStatus(user, archived); + + var taskForItems = contentRepository.QueryAsync(app, schema, status.ToArray(), ids); + var taskForCount = contentRepository.CountAsync(app, schema, status.ToArray(), ids); await Task.WhenAll(taskForItems, taskForCount); @@ -144,7 +148,7 @@ namespace Squidex.Domain.Apps.Read.Contents } } - public async Task FindSchemaAsync(IEntity app, string schemaIdOrName) + public async Task FindSchemaAsync(IAppEntity app, string schemaIdOrName) { Guard.NotNull(app, nameof(app)); @@ -168,6 +172,30 @@ namespace Squidex.Domain.Apps.Read.Contents return schema; } + private static List ParseStatus(ClaimsPrincipal user, bool archived) + { + var status = new List(); + + if (user.IsInClient("squidex-frontend")) + { + if (archived) + { + status.Add(Status.Archived); + } + else + { + status.Add(Status.Draft); + status.Add(Status.Published); + } + } + else + { + status.Add(Status.Published); + } + + return status; + } + private sealed class Content : IContentEntity { public Guid Id { get; set; } diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs index 6d5795755..f1e5f29c1 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs @@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL public async Task> QueryContentsAsync(Guid schemaId, string query) { - var contents = (await contentQuery.QueryWithCountAsync(app, schemaId.ToString(), user, false, null, query).ConfigureAwait(false)).Items; + var contents = (await contentQuery.QueryWithCountAsync(app, schemaId.ToString(), user, false, query).ConfigureAwait(false)).Items; foreach (var content in contents) { @@ -156,7 +156,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL if (notLoadedContents.Count > 0) { - var contents = (await contentQuery.QueryWithCountAsync(app, schemaId.ToString(), user, false, notLoadedContents, null).ConfigureAwait(false)).Items; + var contents = (await contentQuery.QueryWithCountAsync(app, schemaId.ToString(), user, false, notLoadedContents).ConfigureAwait(false)).Items; foreach (var content in contents) { diff --git a/src/Squidex.Domain.Apps.Read/Contents/IContentQueryService.cs b/src/Squidex.Domain.Apps.Read/Contents/IContentQueryService.cs index 1ad7c8f5b..0af89adcf 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/IContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/IContentQueryService.cs @@ -17,10 +17,12 @@ namespace Squidex.Domain.Apps.Read.Contents { public interface IContentQueryService { - Task<(ISchemaEntity Schema, long Total, IReadOnlyList Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet ids, string query); + Task<(ISchemaEntity Schema, long Total, IReadOnlyList Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet ids); + + Task<(ISchemaEntity Schema, long Total, IReadOnlyList Items)> QueryWithCountAsync(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 FindSchemaAsync(IEntity app, string schemaIdOrName); + Task FindSchemaAsync(IAppEntity app, string schemaIdOrName); } } diff --git a/src/Squidex.Domain.Apps.Read/Contents/Repositories/IContentRepository.cs b/src/Squidex.Domain.Apps.Read/Contents/Repositories/IContentRepository.cs index 9d1aa9f02..85887ce9a 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/Repositories/IContentRepository.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/Repositories/IContentRepository.cs @@ -18,11 +18,15 @@ namespace Squidex.Domain.Apps.Read.Contents.Repositories { public interface IContentRepository { - Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids, ODataUriParser odataQuery); + Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids); + + Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery); Task> QueryNotFoundAsync(Guid appId, Guid schemaId, IList contentIds); - Task CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids, ODataUriParser odataQuery); + Task CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids); + + Task CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery); Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id); } diff --git a/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs b/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs index 77b8782f6..2d5f3b843 100644 --- a/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs +++ b/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs @@ -94,6 +94,8 @@ namespace Squidex.Infrastructure.CQRS.Events await CreateProjectionAsync(); + SendAsync(new ConnectMessage()).Forget(); + break; } @@ -193,9 +195,12 @@ namespace Squidex.Infrastructure.CQRS.Events { await projectsManager.CreateContinuousAsync($"${streamName}", projectionConfig, connection.Settings.DefaultUserCredentials); } - catch (Exception ex) when (!(ex is ProjectionCommandConflictException)) + catch (Exception ex) { - throw; + if (!(ex is ProjectionCommandConflictException)) + { + throw; + } } } } diff --git a/src/Squidex/Controllers/ContentApi/ContentsController.cs b/src/Squidex/Controllers/ContentApi/ContentsController.cs index 5d415761c..46f31f5b2 100644 --- a/src/Squidex/Controllers/ContentApi/ContentsController.cs +++ b/src/Squidex/Controllers/ContentApi/ContentsController.cs @@ -71,10 +71,12 @@ namespace Squidex.Controllers.ContentApi [ApiCosts(2)] public async Task GetContents(string name, [FromQuery] bool archived = false, [FromQuery] string ids = null) { - var idsList = new HashSet(); + HashSet idsList = null; if (!string.IsNullOrWhiteSpace(ids)) { + idsList = new HashSet(); + foreach (var id in ids.Split(',')) { if (Guid.TryParse(id, out var guid)) @@ -86,7 +88,10 @@ namespace Squidex.Controllers.ContentApi var isFrontendClient = User.IsFrontendClient(); - var contents = await contentQuery.QueryWithCountAsync(App, name, User, archived, idsList, Request.QueryString.ToString()); + var contents = + idsList != null ? + await contentQuery.QueryWithCountAsync(App, name, User, archived, idsList) : + await contentQuery.QueryWithCountAsync(App, name, User, archived, Request.QueryString.ToString()); var response = new AssetsDto { diff --git a/src/Squidex/app/features/content/shared/references-editor.component.ts b/src/Squidex/app/features/content/shared/references-editor.component.ts index 4456241c8..91b6a159f 100644 --- a/src/Squidex/app/features/content/shared/references-editor.component.ts +++ b/src/Squidex/app/features/content/shared/references-editor.component.ts @@ -82,7 +82,7 @@ export class ReferencesEditorComponent extends AppComponentBase implements Contr this.appNameOnce() .switchMap(app => this.contentsService.getContents(app, this.schemaId, 10000, 0, undefined, contentIds)) .subscribe(dtos => { - this.contentItems = ImmutableArray.of(dtos.items); + this.contentItems = ImmutableArray.of(contentIds.map(id => dtos.items.find(c => c.id === id)).filter(c => !!c)); }); } } diff --git a/src/Squidex/app/features/schemas/pages/schema/field.component.html b/src/Squidex/app/features/schemas/pages/schema/field.component.html index 07029367c..5e9a1f033 100644 --- a/src/Squidex/app/features/schemas/pages/schema/field.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/field.component.html @@ -45,7 +45,7 @@ confirmText="Do you really want to lock the field? Lock fields cannot be deleted or changed."> Lock - diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html index 837c767dc..056a1f665 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html @@ -25,7 +25,10 @@ Scripts - + Delete diff --git a/src/Squidex/app/framework/angular/autocomplete.component.html b/src/Squidex/app/framework/angular/autocomplete.component.html index c2e5b9deb..9c8518dc0 100644 --- a/src/Squidex/app/framework/angular/autocomplete.component.html +++ b/src/Squidex/app/framework/angular/autocomplete.component.html @@ -5,7 +5,7 @@ autocorrect="off" autocapitalize="off"> -
+
{{item}} diff --git a/src/Squidex/app/framework/angular/dropdown.component.html b/src/Squidex/app/framework/angular/dropdown.component.html index 865e7b3c0..5e7c3ab53 100644 --- a/src/Squidex/app/framework/angular/dropdown.component.html +++ b/src/Squidex/app/framework/angular/dropdown.component.html @@ -15,7 +15,7 @@
-
+
{{item}} diff --git a/tests/Squidex.Domain.Apps.Read.Tests/Contents/ContentQueryServiceTests.cs b/tests/Squidex.Domain.Apps.Read.Tests/Contents/ContentQueryServiceTests.cs index 8b0ab9cfc..40941c948 100644 --- a/tests/Squidex.Domain.Apps.Read.Tests/Contents/ContentQueryServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Read.Tests/Contents/ContentQueryServiceTests.cs @@ -119,6 +119,12 @@ namespace Squidex.Domain.Apps.Read.Contents await Assert.ThrowsAsync(async () => await sut.FindContentAsync(app, schemaId.ToString(), user, contentId)); } + [Fact] + public async Task Should_return_contents_with_ids_from_repository_and_transform() + { + 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() { @@ -144,33 +150,70 @@ namespace Squidex.Domain.Apps.Read.Contents } private async Task TestManyRequest(bool isFrontend, bool archive, params Status[] status) + { + SetupClaims(isFrontend); + + SetupFakeWithOdataQuery(status); + SetupFakeWithScripting(); + + var result = await sut.QueryWithCountAsync(app, schemaId.ToString(), user, archive, string.Empty); + + Assert.Equal(123, result.Total); + Assert.Equal(schema, result.Schema); + Assert.Equal(data, result.Items[0].Data); + Assert.Equal(content.Id, result.Items[0].Id); + } + + private async Task TestManyIdRequest(bool isFrontend, bool archive, HashSet ids, params Status[] status) + { + SetupClaims(isFrontend); + + SetupFakeWithIdQuery(status, ids); + SetupFakeWithScripting(); + + var result = await sut.QueryWithCountAsync(app, schemaId.ToString(), user, archive, ids); + + Assert.Equal(123, result.Total); + Assert.Equal(schema, result.Schema); + Assert.Equal(data, result.Items[0].Data); + Assert.Equal(content.Id, result.Items[0].Id); + } + + private void SetupClaims(bool isFrontend) { if (isFrontend) { identity.AddClaim(new Claim(OpenIdClaims.ClientId, "squidex-frontend")); } + } - var ids = new HashSet(); + private void SetupFakeWithIdQuery(Status[] status, HashSet ids) + { + A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)) + .Returns(schema); + A.CallTo(() => contentRepository.QueryAsync(app, schema, A.That.IsSameSequenceAs(status), ids)) + .Returns(new List { content }); + A.CallTo(() => contentRepository.CountAsync(app, schema, A.That.IsSameSequenceAs(status), ids)) + .Returns(123); + } + private void SetupFakeWithOdataQuery(Status[] status) + { A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)) .Returns(schema); - A.CallTo(() => contentRepository.QueryAsync(app, schema, A.That.IsSameSequenceAs(status), ids, A.Ignored)) + A.CallTo(() => contentRepository.QueryAsync(app, schema, A.That.IsSameSequenceAs(status), A.Ignored)) .Returns(new List { content }); - A.CallTo(() => contentRepository.CountAsync(app, schema, A.That.IsSameSequenceAs(status), ids, A.Ignored)) + A.CallTo(() => contentRepository.CountAsync(app, schema, A.That.IsSameSequenceAs(status), A.Ignored)) .Returns(123); + } + private void SetupFakeWithScripting() + { A.CallTo(() => schema.ScriptQuery) .Returns(""); A.CallTo(() => scriptEngine.Transform(A.That.Matches(x => x.User == user && x.ContentId == contentId && ReferenceEquals(x.Data, data)), "")) .Returns(transformedData); - - var result = await sut.QueryWithCountAsync(app, schemaId.ToString(), user, archive, ids, null); - - Assert.Equal(123, result.Total); - Assert.Equal(schema, result.Schema); - Assert.Equal(data, result.Items[0].Data); - Assert.Equal(content.Id, result.Items[0].Id); } } } diff --git a/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs b/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs index ac5273792..460f983c0 100644 --- a/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs +++ b/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs @@ -272,7 +272,7 @@ namespace Squidex.Domain.Apps.Read.Contents var contents = new List { content }; - A.CallTo(() => contentQuery.QueryWithCountAsync(app, schema.Id.ToString(), user, false, null, "?$top=30&$skip=5")) + A.CallTo(() => contentQuery.QueryWithCountAsync(app, schema.Id.ToString(), user, false, "?$top=30&$skip=5")) .Returns((schema, 0L, (IReadOnlyList)contents)); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query }); @@ -460,7 +460,7 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => contentQuery.FindContentAsync(app, schema.Id.ToString(), user, contentId)) .Returns((schema, content)); - A.CallTo(() => contentQuery.QueryWithCountAsync(app, schema.Id.ToString(), user, false, A>.That.Matches(x => x.Contains(contentRefId)), null)) + A.CallTo(() => contentQuery.QueryWithCountAsync(app, schema.Id.ToString(), user, false, A>.That.Matches(x => x.Contains(contentRefId)))) .Returns((schema, 0L, (IReadOnlyList)refContents)); var result = await sut.QueryAsync(app, user, new GraphQLQuery { Query = query });