Browse Source

Simplified schema resolving.

pull/214/head
Sebastian Stehle 8 years ago
parent
commit
dcf44d303d
  1. 27
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  2. 4
      src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs
  3. 18
      src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs
  4. 3
      src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs
  5. 54
      src/Squidex.Domain.Apps.Entities/AppProvider.cs
  6. 4
      src/Squidex.Domain.Apps.Entities/IAppProvider.cs
  7. 4
      src/Squidex.Domain.Apps.Entities/Schemas/Repositories/ISchemaRepository.cs
  8. 15
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs
  9. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs
  10. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs
  11. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs
  12. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs
  13. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandMiddlewareTests.cs
  14. 25
      tools/Migrate_01/Migration01_FromCqrs.cs

27
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Entities.Contents.State;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection;
@ -28,12 +29,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
if (contentEntity != null)
{
var schema = await appProvider.GetSchemaAsync(contentEntity.AppId, contentEntity.SchemaId, true);
if (schema == null)
{
throw new InvalidOperationException($"Cannot find schema {contentEntity.SchemaId}");
}
var schema = await GetSchemaAsync(contentEntity.AppId, contentEntity.SchemaId);
contentEntity?.ParseData(schema.SchemaDef);
@ -52,12 +48,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return;
}
var schema = await appProvider.GetSchemaAsync(value.AppId, value.SchemaId, true);
if (schema == null)
{
throw new InvalidOperationException($"Cannot find schema {value.SchemaId}");
}
var schema = await GetSchemaAsync(value.AppId, value.SchemaId);
var idData = value.Data?.ToIdModel(schema.SchemaDef, true);
@ -94,5 +85,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
}
private async Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid schemaId)
{
var schema = await appProvider.GetSchemaAsync(appId, schemaId);
if (schema == null)
{
throw new DomainObjectNotFoundException(schemaId.ToString(), typeof(ISchemaEntity));
}
return schema;
}
}
}

4
src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs

@ -38,5 +38,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas
[BsonElement]
[BsonRequired]
public int Version { get; set; }
[BsonElement]
[BsonRequired]
public bool IsDeleted { get; set; }
}
}

18
src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs

@ -30,23 +30,23 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas
protected override async Task SetupCollectionAsync(IMongoCollection<MongoSchemaEntity> collection)
{
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Ascending(x => x.IsDeleted));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Ascending(x => x.Name).Ascending(x => x.IsDeleted));
}
public async Task<IReadOnlyList<Guid>> QueryAllSchemaIdsAsync(Guid appId, string name)
public async Task<Guid> FindSchemaIdAsync(Guid appId, string name)
{
var schemaEntities =
await Collection.Find(x => x.AppId == appId && x.Name == name).Only(x => x.Id).SortByDescending(x => x.Version)
.ToListAsync();
var schemaEntity =
await Collection.Find(x => x.AppId == appId && x.Name == name && !x.IsDeleted).Only(x => x.Id).SortByDescending(x => x.Version)
.FirstOrDefaultAsync();
return schemaEntities.Select(x => Guid.Parse(x["_id"].AsString)).ToList();
return schemaEntity != null ? Guid.Parse(schemaEntity["_id"].AsString) : Guid.Empty;
}
public async Task<IReadOnlyList<Guid>> QueryAllSchemaIdsAsync(Guid appId)
public async Task<IReadOnlyList<Guid>> QuerySchemaIdsAsync(Guid appId)
{
var schemaEntities =
await Collection.Find(x => x.AppId == appId).Only(x => x.Id)
await Collection.Find(x => x.AppId == appId && !x.IsDeleted).Only(x => x.Id)
.ToListAsync();
return schemaEntities.Select(x => Guid.Parse(x["_id"].AsString)).ToList();

3
src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs

@ -41,7 +41,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas
.Set(x => x.State, value)
.Set(x => x.AppId, value.AppId)
.Set(x => x.Name, value.Name)
.Set(x => x.Version, newVersion),
.Set(x => x.Version, newVersion)
.Set(x => x.IsDeleted, value.IsDeleted),
Upsert);
}
catch (MongoWriteException ex)

54
src/Squidex.Domain.Apps.Entities/AppProvider.cs

@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities
@ -49,14 +50,19 @@ namespace Squidex.Domain.Apps.Entities
{
var app = await stateFactory.GetSingleAsync<AppDomainObject>(appId);
if (IsFound(app))
if (!IsFound(app))
{
return (null, null);
}
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
return IsFound(schema, false) ? (null, null) : (app.State, schema.State);
if (!IsFound(schema) || schema.State.IsDeleted)
{
return (null, null);
}
return (app.State, schema.State);
}
public async Task<IAppEntity> GetAppAsync(string appName)
@ -68,38 +74,42 @@ namespace Squidex.Domain.Apps.Entities
return null;
}
var app = await stateFactory.GetSingleAsync<AppDomainObject>(appId);
return IsFound(app) ? app.State : null;
return (await stateFactory.GetSingleAsync<AppDomainObject>(appId)).State;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false)
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name)
{
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
var schemaId = await GetSchemaIdAsync(appId, name);
if (schemaId == Guid.Empty)
{
return null;
}
return IsFound(schema, provideDeleted) ? schema.State : null;
return (await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaId)).State;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name, bool provideDeleted = false)
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id)
{
var ids = await schemaRepository.QueryAllSchemaIdsAsync(appId, name);
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
var schemas =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<SchemaDomainObject>(id)));
if (!IsFound(schema))
{
return null;
}
return schemas.OrderByDescending(x => x.State.LastModified).FirstOrDefault(s => IsFound(s, provideDeleted))?.State;
return schema.State;
}
public async Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId)
{
var ids = await schemaRepository.QueryAllSchemaIdsAsync(appId);
var ids = await schemaRepository.QuerySchemaIdsAsync(appId);
var schemas =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<SchemaDomainObject>(id)));
return schemas.Where(s => IsFound(s)).Select(s => (ISchemaEntity)s.State).ToList();
return schemas.Where(IsFound).Select(s => (ISchemaEntity)s.State).ToList();
}
public async Task<List<IRuleEntity>> GetRulesAsync(Guid appId)
@ -110,7 +120,7 @@ namespace Squidex.Domain.Apps.Entities
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<RuleDomainObject>(id)));
return rules.Select(r => (IRuleEntity)r.State).ToList();
return rules.Where(IsFound).Select(r => (IRuleEntity)r.State).ToList();
}
public async Task<List<IAppEntity>> GetUserApps(string userId)
@ -121,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<AppDomainObject>(id)));
return apps.Select(a => (IAppEntity)a.State).ToList();
return apps.Where(IsFound).Select(a => (IAppEntity)a.State).ToList();
}
private Task<Guid> GetAppIdAsync(string name)
@ -129,14 +139,14 @@ namespace Squidex.Domain.Apps.Entities
return appRepository.FindAppIdByNameAsync(name);
}
private static bool IsFound(AppDomainObject app)
private async Task<Guid> GetSchemaIdAsync(Guid appId, string name)
{
return app.Version >= 0;
return await schemaRepository.FindSchemaIdAsync(appId, name);
}
private static bool IsFound(SchemaDomainObject schema, bool provideDeleted = false)
private static bool IsFound(IDomainObject app)
{
return schema.Version >= 0 && (!schema.State.IsDeleted || provideDeleted);
return app.Version > EtagVersion.Empty;
}
}
}

4
src/Squidex.Domain.Apps.Entities/IAppProvider.cs

@ -21,9 +21,9 @@ namespace Squidex.Domain.Apps.Entities
Task<IAppEntity> GetAppAsync(string appName);
Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false);
Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id);
Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name, bool provideDeleted = false);
Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name);
Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId);

4
src/Squidex.Domain.Apps.Entities/Schemas/Repositories/ISchemaRepository.cs

@ -14,8 +14,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Repositories
{
public interface ISchemaRepository
{
Task<IReadOnlyList<Guid>> QueryAllSchemaIdsAsync(Guid appId, string name);
Task<Guid> FindSchemaIdAsync(Guid appId, string name);
Task<IReadOnlyList<Guid>> QueryAllSchemaIdsAsync(Guid appId);
Task<IReadOnlyList<Guid>> QuerySchemaIdsAsync(Guid appId);
}
}

15
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs

@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
public async Task Should_return_schema_from_id_if_string_is_guid()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId))
.Returns(schema);
var result = await sut.FindSchemaAsync(app, schemaId.ToString());
@ -72,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
public async Task Should_return_schema_from_name_if_string_not_guid()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema", false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema"))
.Returns(schema);
var result = await sut.FindSchemaAsync(app, "my-schema");
@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
public async Task Should_throw_if_schema_not_found()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema", false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema"))
.Returns((ISchemaEntity)null);
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.FindSchemaAsync(app, "my-schema"));
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
public async Task Should_return_content_from_repository_and_transform()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId))
.Returns(schema);
A.CallTo(() => contentRepository.FindContentAsync(app, schema, contentId))
.Returns(content);
@ -114,8 +114,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
public async Task Should_throw_if_content_to_find_does_not_exist()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId))
.Returns(schema);
A.CallTo(() => contentRepository.FindContentAsync(app, schema, contentId))
.Returns((IContentEntity)null);
@ -196,7 +197,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
private void SetupFakeWithIdQuery(Status[] status, HashSet<Guid> ids)
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId))
.Returns(schema);
A.CallTo(() => contentRepository.QueryAsync(app, schema, A<Status[]>.That.IsSameSequenceAs(status), ids))
@ -205,7 +206,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
private void SetupFakeWithOdataQuery(Status[] status)
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId))
.Returns(schema);
A.CallTo(() => contentRepository.QueryAsync(app, schema, A<Status[]>.That.IsSameSequenceAs(status), A<ODataUriParser>.Ignored))

2
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs

@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
public GuardRuleTests()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<Guid>.Ignored, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<Guid>.Ignored))
.Returns(A.Fake<ISchemaEntity>());
}

4
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
[Fact]
public async Task Should_add_error_if_schemas_ids_are_not_valid()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, A<Guid>.Ignored, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, A<Guid>.Ignored))
.Returns(Task.FromResult<ISchemaEntity>(null));
var trigger = new ContentChangedTrigger
@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
[Fact]
public async Task Should_not_add_error_if_schemas_ids_are_valid()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, A<Guid>.Ignored, false))
A.CallTo(() => appProvider.GetSchemaAsync(appId, A<Guid>.Ignored))
.Returns(A.Fake<ISchemaEntity>());
var trigger = new ContentChangedTrigger

2
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs

@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
public RuleCommandMiddlewareTests()
{
A.CallTo(() => appProvider.GetSchemaAsync(A<Guid>.Ignored, A<Guid>.Ignored, false))
A.CallTo(() => appProvider.GetSchemaAsync(A<Guid>.Ignored, A<Guid>.Ignored))
.Returns(A.Fake<ISchemaEntity>());
sut = new RuleCommandMiddleware(Handler, appProvider);

4
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs

@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
.AddField(new StringField(1, "field1", Partitioning.Invariant))
.AddField(new StringField(2, "field2", Partitioning.Invariant));
A.CallTo(() => appProvider.GetSchemaAsync(A<Guid>.Ignored, "new-schema", false))
A.CallTo(() => appProvider.GetSchemaAsync(A<Guid>.Ignored, "new-schema"))
.Returns(Task.FromResult<ISchemaEntity>(null));
}
@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
[Fact]
public Task CanCreate_should_throw_exception_if_name_already_in_use()
{
A.CallTo(() => appProvider.GetSchemaAsync(A<Guid>.Ignored, "new-schema", false))
A.CallTo(() => appProvider.GetSchemaAsync(A<Guid>.Ignored, "new-schema"))
.Returns(Task.FromResult(A.Fake<ISchemaEntity>()));
var command = new CreateSchema { AppId = appId, Name = "new-schema" };

4
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandMiddlewareTests.cs

@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut = new SchemaCommandMiddleware(Handler, appProvider);
A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName, false))
A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName))
.Returns((ISchemaEntity)null);
}
@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
Assert.Equal(SchemaId, context.Result<EntityCreatedResult<Guid>>().IdOrValue);
A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName, false)).MustHaveHappened();
A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName)).MustHaveHappened();
}
[Fact]

25
tools/Migrate_01/Migration01_FromCqrs.cs

@ -56,7 +56,22 @@ namespace Migrate_01
{
var version = storedEvent.EventStreamNumber;
if (@event.Payload is AssetEvent assetEvent)
if (@event.Payload is ContentEvent contentEvent)
{
try
{
var content = await stateFactory.CreateAsync<ContentDomainObject>(contentEvent.ContentId);
content.UpdateState(content.State.Apply(@event));
await content.WriteStateAsync(version);
}
catch (DomainObjectNotFoundException)
{
// Schema has been deleted.
}
}
else if (@event.Payload is AssetEvent assetEvent)
{
var asset = await stateFactory.CreateAsync<AssetDomainObject>(assetEvent.AssetId);
@ -64,14 +79,6 @@ namespace Migrate_01
await asset.WriteStateAsync(version);
}
else if (@event.Payload is ContentEvent contentEvent)
{
var content = await stateFactory.CreateAsync<ContentDomainObject>(contentEvent.ContentId);
content.UpdateState(content.State.Apply(@event));
await content.WriteStateAsync(version);
}
else if (@event.Payload is SchemaEvent schemaEvent)
{
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaEvent.SchemaId.Id);

Loading…
Cancel
Save