Browse Source

Query service improved.

pull/282/head
Sebastian 8 years ago
parent
commit
67adf346be
  1. 29
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs
  2. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs
  3. 108
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  4. 45
      src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs
  5. 9
      src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs
  6. 3
      src/Squidex.Domain.Apps.Entities/Contents/IContentVersionLoader.cs
  7. 8
      src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs
  8. 2
      src/Squidex.Domain.Apps.Entities/DomainObjectState.cs
  9. 2
      src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs
  10. 63
      src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs
  11. 2
      src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs
  12. 2
      src/Squidex/Config/ServiceExtensions.cs
  13. 23
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs
  14. 54
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs
  15. 22
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  16. 2
      tests/Squidex.Infrastructure.Tests/Caching/HttpRequestCacheTests.cs
  17. 2
      tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs
  18. 2
      tests/Squidex.Infrastructure.Tests/Orleans/BootstrapTests.cs
  19. 2
      tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockPoolTests.cs
  20. 2
      tests/Squidex.Infrastructure.Tests/Tasks/AsyncLockTests.cs
  21. 2
      tests/Squidex.Infrastructure.Tests/UsageTracking/ThreadingUsageTrackerTests.cs

29
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;

2
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)

108
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<IContentEntity> 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<IContentEntity> Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query)
public async Task<IResultList<IContentEntity>> 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<IContentEntity> Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet<Guid> ids)
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet<Guid> 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<IContentEntity> Contents) TransformContents(ClaimsPrincipal user, ISchemaEntity schema, IResultList<IContentEntity> contents)
private IResultList<IContentEntity> TransformContents(IAppEntity app, ISchemaEntity schema, ClaimsPrincipal user,
IResultList<IContentEntity> 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<IContentEntity> TransformContent(ClaimsPrincipal user, ISchemaEntity schema, IEnumerable<IContentEntity> contents)
private IEnumerable<IContentEntity> TransformContent(IAppEntity app, ISchemaEntity schema, ClaimsPrincipal user,
IEnumerable<IContentEntity> 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<ISchemaEntity> FindSchemaAsync(IAppEntity app, string schemaIdOrName)
public async Task<ISchemaEntity> 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<Status> ParseStatus(ClaimsPrincipal user, bool archived)
private static List<Status> ParseStatus(bool isFrontendClient, bool archived)
{
var status = new List<Status>();
if (user.IsInClient("squidex-frontend"))
if (isFrontendClient)
{
if (archived)
{
@ -195,5 +216,20 @@ namespace Squidex.Domain.Apps.Entities.Contents
return status;
}
private Task<IContentEntity> FindContentByVersionAsync(Guid id, long version)
{
return contentVersionLoader.LoadAsync(id, version);
}
private Task<IContentEntity> FindContentAsync(IAppEntity app, Guid id, ISchemaEntity schema)
{
return contentRepository.FindContentAsync(app, schema, id);
}
private static bool IsFrontendClient(ClaimsPrincipal user)
{
return user.IsInClient("squidex-frontend");
}
}
}

45
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<IContentEntity> 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<SchemaGrain, Guid>(schemaId, e =>
{
state = state.Apply(e, registry);
if (state.LastModified < lastUpdate)
{
stateAtVersion = state;
}
});
await persistence.ReadAsync();
return (state, stateAtVersion);
return content;
}
}
}

9
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<IContentEntity> Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet<Guid> ids);
Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet<Guid> ids);
Task<(ISchemaEntity Schema, IResultList<IContentEntity> Contents)> QueryAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query);
Task<IResultList<IContentEntity>> 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<IContentEntity> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id, long version = EtagVersion.Any);
Task<ISchemaEntity> FindSchemaAsync(IAppEntity app, string schemaIdOrName);
Task ThrowIfSchemaNotExistsAsync(IAppEntity app, string schemaIdOrName);
}
}

3
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<IContentEntity> LoadAsync(Guid id, long version);
}
}

8
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<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> 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;
}

2
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()
{

2
src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs

@ -63,7 +63,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
public Task<Immutable<EventConsumerInfo>> GetStateAsync()
{
return Task.FromResult(state.ToInfo(this.eventConsumer.Name).AsImmutable());
return Task.FromResult(state.ToInfo(eventConsumer.Name).AsImmutable());
}
public Task OnEventAsync(Immutable<IEventSubscription> subscription, Immutable<StoredEvent> storedEvent)

63
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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> DeleteContent(string app, string name, Guid id)
{
await contentQuery.FindSchemaAsync(App, name);
await contentQuery.ThrowIfSchemaNotExistsAsync(App, name);
var command = new DeleteContent { ContentId = id };

2
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() });
}
}
}

2
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<T>();
});

23
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()
{

54
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<IContentRepository>();
private readonly IContentVersionLoader contentVersionLoader = A.Fake<IContentVersionLoader>();
private readonly IScriptEngine scriptEngine = A.Fake<IScriptEngine>();
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>();
private readonly IContentEntity content = A.Fake<IContentEntity>();
@ -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<DomainObjectNotFoundException>(() => sut.FindSchemaAsync(app, "my-schema"));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => 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("<script-query>");
A.CallTo(() => scriptEngine.Transform(A<ScriptContext>.That.Matches(x => x.User == user && x.ContentId == contentId && ReferenceEquals(x.Data, contentData)), "<query-script>"))
.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<Guid> 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)

22
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<IContentEntity> { 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<IContentEntity> { 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<IContentEntity> { 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<HashSet<Guid>>.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<IAssetEntity> { 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<HashSet<Guid>>.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 });

2
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<IHttpContextAccessor>();
private readonly IRequestCache sut;

2
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<Guid> store = A.Fake<IStore<Guid>>();
private readonly IPersistence<MyDomainState> persistence = A.Fake<IPersistence<MyDomainState>>();

2
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<IBackgroundGrain>();
private readonly Bootstrap<IBackgroundGrain> sut;

2
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()

2
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()

2
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<IUsageTracker>();

Loading…
Cancel
Save