Browse Source

Closes #145

Closes #144
pull/147/merge
Sebastian Stehle 9 years ago
parent
commit
765d3894c5
  1. 12
      src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs
  2. 9
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs
  3. 5
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs
  4. 14
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs
  5. 4
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs
  6. 39
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs
  7. 12
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs
  8. 42
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs
  9. 1
      src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs
  10. 3
      src/Squidex.Domain.Apps.Read/Apps/Repositories/IAppRepository.cs
  11. 3
      src/Squidex.Domain.Apps.Read/Apps/Services/IAppProvider.cs
  12. 45
      src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/CachingAppProvider.cs
  13. 3
      src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs
  14. 3
      src/Squidex.Domain.Apps.Read/Schemas/Services/ISchemaProvider.cs
  15. 45
      src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs
  16. 2
      src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs
  17. 2
      src/Squidex/Config/Domain/InfrastructureModule.cs
  18. 2
      src/Squidex/Config/Domain/Serializers.cs
  19. 22
      src/Squidex/Config/Domain/StoreMongoDbModule.cs
  20. 20
      src/Squidex/Config/Domain/Usages.cs
  21. 1
      src/Squidex/Config/Identity/LazyClientStore.cs
  22. 28
      tests/Squidex.Domain.Apps.Read.Tests/Apps/CachingAppProviderTests.cs
  23. 26
      tests/Squidex.Domain.Apps.Read.Tests/Schemas/CachingSchemaProviderTests.cs
  24. 2
      tests/Squidex.Infrastructure.Tests/Json/ConverterContractResolverTests.cs

12
src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs

@ -22,8 +22,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{ {
public partial class MongoAppRepository public partial class MongoAppRepository
{ {
private readonly List<Action<NamedId<Guid>>> subscribers = new List<Action<NamedId<Guid>>>();
public string Name public string Name
{ {
get { return GetType().Name; } get { return GetType().Name; }
@ -34,11 +32,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
get { return "^app-"; } get { return "^app-"; }
} }
public void SubscribeOnChanged(Action<NamedId<Guid>> subscriber)
{
subscribers.Add(subscriber);
}
public Task On(Envelope<IEvent> @event) public Task On(Envelope<IEvent> @event)
{ {
return this.DispatchActionAsync(@event.Payload, @event.Headers); return this.DispatchActionAsync(@event.Payload, @event.Headers);
@ -144,11 +137,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
a.ContributorIds = a.Contributors.Keys.ToList(); a.ContributorIds = a.Contributors.Keys.ToList();
}); });
foreach (var subscriber in subscribers)
{
subscriber(@event.AppId);
}
} }
} }
} }

9
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs

@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MongoDB.Bson; using MongoDB.Bson;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
@ -22,9 +23,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
{ {
private const int MaxLength = 1024 * 1024; private const int MaxLength = 1024 * 1024;
public static BsonDocument ToBsonDocument(this IdContentData data) public static BsonDocument ToBsonDocument(this IdContentData data, JsonSerializer jsonSerializer)
{ {
return (BsonDocument)JToken.FromObject(data).ToBson(); return (BsonDocument)JToken.FromObject(data, jsonSerializer).ToBson();
} }
public static List<Guid> ToReferencedIds(this IdContentData data, Schema schema) public static List<Guid> ToReferencedIds(this IdContentData data, Schema schema)
@ -32,11 +33,11 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
return data.GetReferencedIds(schema).ToList(); return data.GetReferencedIds(schema).ToList();
} }
public static NamedContentData ToData(this BsonDocument document, Schema schema, List<Guid> deletedIds) public static NamedContentData ToData(this BsonDocument document, Schema schema, List<Guid> deletedIds, JsonSerializer jsonSerializer)
{ {
return document return document
.ToJson() .ToJson()
.ToObject<IdContentData>() .ToObject<IdContentData>(jsonSerializer)
.ToCleanedReferences(schema, new HashSet<Guid>(deletedIds ?? new List<Guid>())) .ToCleanedReferences(schema, new HashSet<Guid>(deletedIds ?? new List<Guid>()))
.ToNameModel(schema, true); .ToNameModel(schema, true);
} }

5
src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs

@ -10,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
@ -82,9 +83,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
get { return data; } get { return data; }
} }
public void ParseData(Schema schema) public void ParseData(Schema schema, JsonSerializer serializer)
{ {
data = DataDocument.ToData(schema, ReferencedIdsDeleted); data = DataDocument.ToData(schema, ReferencedIdsDeleted, serializer);
} }
} }
} }

14
src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using Microsoft.OData.UriParser; using Microsoft.OData.UriParser;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Contents; using Squidex.Domain.Apps.Read.Contents;
@ -30,6 +31,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
private const string Prefix = "Projections_Content_"; private const string Prefix = "Projections_Content_";
private readonly IMongoDatabase database; private readonly IMongoDatabase database;
private readonly ISchemaProvider schemas; private readonly ISchemaProvider schemas;
private readonly JsonSerializer serializer;
protected static FilterDefinitionBuilder<MongoContentEntity> Filter protected static FilterDefinitionBuilder<MongoContentEntity> Filter
{ {
@ -63,13 +65,15 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
} }
} }
public MongoContentRepository(IMongoDatabase database, ISchemaProvider schemas) public MongoContentRepository(IMongoDatabase database, ISchemaProvider schemas, JsonSerializer serializer)
{ {
Guard.NotNull(database, nameof(database)); Guard.NotNull(database, nameof(database));
Guard.NotNull(schemas, nameof(schemas)); Guard.NotNull(schemas, nameof(schemas));
Guard.NotNull(serializer, nameof(serializer));
this.schemas = schemas;
this.database = database; this.database = database;
this.schemas = schemas;
this.serializer = serializer;
} }
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery) public async Task<IReadOnlyList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery)
@ -99,7 +103,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
foreach (var entity in contentEntities) foreach (var entity in contentEntities)
{ {
entity.ParseData(schema.SchemaDef); entity.ParseData(schema.SchemaDef, serializer);
} }
return contentEntities; return contentEntities;
@ -147,7 +151,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
foreach (var entity in contentEntities) foreach (var entity in contentEntities)
{ {
entity.ParseData(schema.SchemaDef); entity.ParseData(schema.SchemaDef, serializer);
} }
return contentEntities.OfType<IContentEntity>().ToList(); return contentEntities.OfType<IContentEntity>().ToList();
@ -172,7 +176,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
await collection.Find(x => x.Id == id) await collection.Find(x => x.Id == id)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef); contentEntity?.ParseData(schema.SchemaDef, serializer);
return contentEntity; return contentEntity;
} }

4
src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs

@ -79,7 +79,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
var idData = @event.Data?.ToIdModel(schema.SchemaDef, true); var idData = @event.Data?.ToIdModel(schema.SchemaDef, true);
content.DataText = idData?.ToFullText(); content.DataText = idData?.ToFullText();
content.DataDocument = idData?.ToBsonDocument(); content.DataDocument = idData?.ToBsonDocument(serializer);
content.ReferencedIds = idData?.ToReferencedIds(schema.SchemaDef); content.ReferencedIds = idData?.ToReferencedIds(schema.SchemaDef);
}); });
}); });
@ -95,7 +95,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
Filter.Eq(x => x.Id, @event.ContentId), Filter.Eq(x => x.Id, @event.ContentId),
Update Update
.Set(x => x.DataText, idData.ToFullText()) .Set(x => x.DataText, idData.ToFullText())
.Set(x => x.DataDocument, idData.ToBsonDocument()) .Set(x => x.DataDocument, idData.ToBsonDocument(serializer))
.Set(x => x.ReferencedIds, idData.ToReferencedIds(schema.SchemaDef)) .Set(x => x.ReferencedIds, idData.ToReferencedIds(schema.SchemaDef))
.Set(x => x.LastModified, headers.Timestamp()) .Set(x => x.LastModified, headers.Timestamp())
.Set(x => x.LastModifiedBy, @event.Actor) .Set(x => x.LastModifiedBy, @event.Actor)

39
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs

@ -9,6 +9,7 @@
using System; using System;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
@ -19,7 +20,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{ {
public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity
{ {
private Schema schema; private Lazy<Schema> schema;
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
@ -73,23 +74,31 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
[BsonElement] [BsonElement]
public string ScriptChange { get; set; } public string ScriptChange { get; set; }
[BsonIgnore] Schema ISchemaEntity.SchemaDef
public Schema SchemaDef
{ {
get get { return schema.Value; }
{ }
if (schema == null)
{
schema = SchemaDocument.ToJson().ToObject<Schema>();
}
return schema; public void SerializeSchema(Schema newSchema, JsonSerializer serializer)
} {
set SchemaDocument = JObject.FromObject(newSchema, serializer).ToBson();
{ schema = new Lazy<Schema>(() => newSchema);
schema = value;
IsPublished = newSchema.IsPublished;
}
SchemaDocument = (BsonDocument)JToken.FromObject(schema).ToBson(); public void UpdateSchema(JsonSerializer serializer, Func<Schema, Schema> updater)
{
DeserializeSchema(serializer);
SerializeSchema(updater(schema.Value), serializer);
}
public void DeserializeSchema(JsonSerializer serializer)
{
if (schema == null)
{
schema = new Lazy<Schema>(() => schema != null ? SchemaDocument.ToJson().ToObject<Schema>(serializer) : null);
} }
} }
} }

12
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs

@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Driver; using MongoDB.Driver;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Repositories; using Squidex.Domain.Apps.Read.Schemas.Repositories;
@ -22,14 +23,17 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{ {
public partial class MongoSchemaRepository : MongoRepositoryBase<MongoSchemaEntity>, ISchemaRepository, IEventConsumer public partial class MongoSchemaRepository : MongoRepositoryBase<MongoSchemaEntity>, ISchemaRepository, IEventConsumer
{ {
private readonly JsonSerializer serializer;
private readonly FieldRegistry registry; private readonly FieldRegistry registry;
public MongoSchemaRepository(IMongoDatabase database, FieldRegistry registry) public MongoSchemaRepository(IMongoDatabase database, JsonSerializer serializer, FieldRegistry registry)
: base(database) : base(database)
{ {
Guard.NotNull(registry, nameof(registry)); Guard.NotNull(registry, nameof(registry));
Guard.NotNull(serializer, nameof(serializer));
this.registry = registry; this.registry = registry;
this.serializer = serializer;
} }
protected override string CollectionName() protected override string CollectionName()
@ -50,6 +54,8 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
await Collection.Find(s => s.AppId == appId && !s.IsDeleted) await Collection.Find(s => s.AppId == appId && !s.IsDeleted)
.ToListAsync(); .ToListAsync();
schemaEntities.ForEach(x => x.DeserializeSchema(serializer));
return schemaEntities.OfType<ISchemaEntity>().ToList(); return schemaEntities.OfType<ISchemaEntity>().ToList();
} }
@ -59,6 +65,8 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
await Collection.Find(s => s.AppId == appId && !s.IsDeleted && s.Name == name) await Collection.Find(s => s.AppId == appId && !s.IsDeleted && s.Name == name)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
schemaEntity?.DeserializeSchema(serializer);
return schemaEntity; return schemaEntity;
} }
@ -68,6 +76,8 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
await Collection.Find(s => s.Id == schemaId) await Collection.Find(s => s.Id == schemaId)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
schemaEntity?.DeserializeSchema(serializer);
return schemaEntity; return schemaEntity;
} }
} }

42
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs

@ -7,7 +7,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
@ -15,7 +14,6 @@ using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Events.Schemas.Old; using Squidex.Domain.Apps.Events.Schemas.Old;
using Squidex.Domain.Apps.Events.Schemas.Utils; using Squidex.Domain.Apps.Events.Schemas.Utils;
using Squidex.Domain.Apps.Read.MongoDb.Utils; using Squidex.Domain.Apps.Read.MongoDb.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
@ -26,8 +24,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{ {
public partial class MongoSchemaRepository public partial class MongoSchemaRepository
{ {
private readonly List<Action<NamedId<Guid>, NamedId<Guid>>> subscribers = new List<Action<NamedId<Guid>, NamedId<Guid>>>();
public string Name public string Name
{ {
get { return GetType().Name; } get { return GetType().Name; }
@ -38,11 +34,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
get { return "^schema-"; } get { return "^schema-"; }
} }
public void SubscribeOnChanged(Action<NamedId<Guid>, NamedId<Guid>> subscriber)
{
subscribers.Add(subscriber);
}
public Task On(Envelope<IEvent> @event) public Task On(Envelope<IEvent> @event)
{ {
return this.DispatchActionAsync(@event.Payload, @event.Headers); return this.DispatchActionAsync(@event.Payload, @event.Headers);
@ -52,7 +43,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{ {
var schema = SchemaEventDispatcher.Dispatch(@event, registry); var schema = SchemaEventDispatcher.Dispatch(@event, registry);
return Collection.CreateAsync(@event, headers, s => { s.SchemaDef = schema; SimpleMapper.Map(@event, s); }); return Collection.CreateAsync(@event, headers, s => { UpdateSchema(s, schema); SimpleMapper.Map(@event, s); });
} }
protected Task On(FieldDeleted @event, EnvelopeHeaders headers) protected Task On(FieldDeleted @event, EnvelopeHeaders headers)
@ -125,30 +116,29 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true); return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true);
} }
protected Task On(WebhookAdded @event, EnvelopeHeaders headers) private Task UpdateSchema(SquidexEvent @event, EnvelopeHeaders headers, Func<Schema, Schema> updater)
{ {
return Collection.UpdateAsync(@event, headers, e => { }); return Collection.UpdateAsync(@event, headers, e => UpdateSchema(e, updater));
} }
protected Task On(WebhookDeleted @event, EnvelopeHeaders headers) private void UpdateSchema(MongoSchemaEntity entity, Func<Schema, Schema> updater)
{ {
return Collection.UpdateAsync(@event, headers, e => { }); entity.UpdateSchema(serializer, updater);
} }
private async Task UpdateSchema(SchemaEvent @event, EnvelopeHeaders headers, Func<Schema, Schema> updater = null) private void UpdateSchema(MongoSchemaEntity entity, Schema schema)
{ {
await Collection.UpdateAsync(@event, headers, e => entity.SerializeSchema(schema, serializer);
{ }
if (updater != null)
{
e.SchemaDef = updater(e.SchemaDef);
}
});
foreach (var subscriber in subscribers) protected Task On(WebhookAdded @event, EnvelopeHeaders headers)
{ {
subscriber(@event.AppId, @event.SchemaId); return Collection.UpdateAsync(@event, headers, e => { });
} }
protected Task On(WebhookDeleted @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => { });
} }
} }
} }

1
src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs

@ -8,7 +8,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Read.Apps namespace Squidex.Domain.Apps.Read.Apps
{ {

3
src/Squidex.Domain.Apps.Read/Apps/Repositories/IAppRepository.cs

@ -9,7 +9,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Apps.Repositories namespace Squidex.Domain.Apps.Read.Apps.Repositories
{ {
@ -20,7 +19,5 @@ namespace Squidex.Domain.Apps.Read.Apps.Repositories
Task<IAppEntity> FindAppAsync(Guid appId); Task<IAppEntity> FindAppAsync(Guid appId);
Task<IAppEntity> FindAppAsync(string name); Task<IAppEntity> FindAppAsync(string name);
void SubscribeOnChanged(Action<NamedId<Guid>> subscriber);
} }
} }

3
src/Squidex.Domain.Apps.Read/Apps/Services/IAppProvider.cs

@ -8,7 +8,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Apps.Services namespace Squidex.Domain.Apps.Read.Apps.Services
{ {
@ -17,7 +16,5 @@ namespace Squidex.Domain.Apps.Read.Apps.Services
Task<IAppEntity> FindAppByIdAsync(Guid id); Task<IAppEntity> FindAppByIdAsync(Guid id);
Task<IAppEntity> FindAppByNameAsync(string name); Task<IAppEntity> FindAppByNameAsync(string name);
void Invalidate(NamedId<Guid> appId);
} }
} }

45
src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/CachingAppProvider.cs

@ -9,19 +9,31 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Read.Apps.Repositories; using Squidex.Domain.Apps.Read.Apps.Repositories;
using Squidex.Domain.Apps.Read.Utils; using Squidex.Domain.Apps.Read.Utils;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Caching;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
{ {
public class CachingAppProvider : CachingProviderBase, IAppProvider public class CachingAppProvider : CachingProviderBase, IAppProvider, IEventConsumer
{ {
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30); private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30);
private readonly IAppRepository repository; private readonly IAppRepository repository;
public string Name
{
get { return GetType().Name; }
}
public string EventsFilter
{
get { return string.Empty; }
}
public CachingAppProvider(IMemoryCache cache, IAppRepository repository) public CachingAppProvider(IMemoryCache cache, IAppRepository repository)
: base(cache) : base(cache)
{ {
@ -70,16 +82,26 @@ namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
return result; return result;
} }
public void Invalidate(NamedId<Guid> appId) public Task On(Envelope<IEvent> @event)
{ {
var cacheKeyById = BuildIdCacheKey(appId.Id); void Remove(NamedId<Guid> id)
var cacheKeyByName = BuildNameCacheKey(appId.Name); {
var cacheKeyById = BuildIdCacheKey(id.Id);
var cacheKeyByName = BuildNameCacheKey(id.Name);
Cache.Remove(cacheKeyById); Cache.Remove(cacheKeyById);
Cache.Remove(cacheKeyByName); Cache.Remove(cacheKeyByName);
Cache.Invalidate(cacheKeyById); Cache.Invalidate(cacheKeyById);
Cache.Invalidate(cacheKeyByName); Cache.Invalidate(cacheKeyByName);
}
if (@event.Payload is AppEvent appEvent)
{
Remove(appEvent.AppId);
}
return TaskHelper.Done;
} }
private static string BuildNameCacheKey(string name) private static string BuildNameCacheKey(string name)
@ -91,5 +113,10 @@ namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
{ {
return $"App_Names_{schemaId}"; return $"App_Names_{schemaId}";
} }
public Task ClearAsync()
{
return TaskHelper.Done;
}
} }
} }

3
src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs

@ -9,7 +9,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Schemas.Repositories namespace Squidex.Domain.Apps.Read.Schemas.Repositories
{ {
@ -20,7 +19,5 @@ namespace Squidex.Domain.Apps.Read.Schemas.Repositories
Task<ISchemaEntity> FindSchemaAsync(Guid appId, string name); Task<ISchemaEntity> FindSchemaAsync(Guid appId, string name);
Task<ISchemaEntity> FindSchemaAsync(Guid schemaId); Task<ISchemaEntity> FindSchemaAsync(Guid schemaId);
void SubscribeOnChanged(Action<NamedId<Guid>, NamedId<Guid>> subscriber);
} }
} }

3
src/Squidex.Domain.Apps.Read/Schemas/Services/ISchemaProvider.cs

@ -8,7 +8,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Schemas.Services namespace Squidex.Domain.Apps.Read.Schemas.Services
{ {
@ -17,7 +16,5 @@ namespace Squidex.Domain.Apps.Read.Schemas.Services
Task<ISchemaEntity> FindSchemaByIdAsync(Guid id, bool provideDeleted = false); Task<ISchemaEntity> FindSchemaByIdAsync(Guid id, bool provideDeleted = false);
Task<ISchemaEntity> FindSchemaByNameAsync(Guid appId, string name); Task<ISchemaEntity> FindSchemaByNameAsync(Guid appId, string name);
void Invalidate(NamedId<Guid> appId, NamedId<Guid> schemaId);
} }
} }

45
src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs

@ -9,19 +9,31 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Read.Schemas.Repositories; using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Utils; using Squidex.Domain.Apps.Read.Utils;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Caching;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations
{ {
public class CachingSchemaProvider : CachingProviderBase, ISchemaProvider public class CachingSchemaProvider : CachingProviderBase, ISchemaProvider, IEventConsumer
{ {
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10); private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10);
private readonly ISchemaRepository repository; private readonly ISchemaRepository repository;
public string Name
{
get { return GetType().Name; }
}
public string EventsFilter
{
get { return string.Empty; }
}
public CachingSchemaProvider(IMemoryCache cache, ISchemaRepository repository) public CachingSchemaProvider(IMemoryCache cache, ISchemaRepository repository)
: base(cache) : base(cache)
{ {
@ -80,16 +92,26 @@ namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations
return result; return result;
} }
public void Invalidate(NamedId<Guid> appId, NamedId<Guid> schemaId) public Task On(Envelope<IEvent> @event)
{ {
var cacheKeyById = BuildIdCacheKey(schemaId.Id); void Remove(NamedId<Guid> appId, NamedId<Guid> schemaId)
var cacheKeyByName = BuildNameCacheKey(appId.Id, schemaId.Name); {
var cacheKeyById = BuildIdCacheKey(schemaId.Id);
var cacheKeyByName = BuildNameCacheKey(appId.Id, schemaId.Name);
Cache.Remove(cacheKeyById); Cache.Remove(cacheKeyById);
Cache.Remove(cacheKeyByName); Cache.Remove(cacheKeyByName);
Cache.Invalidate(cacheKeyById); Cache.Invalidate(cacheKeyById);
Cache.Invalidate(cacheKeyByName); Cache.Invalidate(cacheKeyByName);
}
if (@event.Payload is SchemaEvent schemaEvent)
{
Remove(schemaEvent.AppId, schemaEvent.SchemaId);
}
return TaskHelper.Done;
} }
private static string BuildNameCacheKey(Guid appId, string name) private static string BuildNameCacheKey(Guid appId, string name)
@ -101,5 +123,10 @@ namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations
{ {
return $"Schema_Names_{schemaId}"; return $"Schema_Names_{schemaId}";
} }
public Task ClearAsync()
{
return TaskHelper.Done;
}
} }
} }

2
src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs

@ -44,7 +44,7 @@ namespace Squidex.Infrastructure.CQRS.Events
connectionFactory = new ConnectionFactory { Uri = new Uri(uri, UriKind.Absolute) }; connectionFactory = new ConnectionFactory { Uri = new Uri(uri, UriKind.Absolute) };
connection = new Lazy<IConnection>(connectionFactory.CreateConnection); connection = new Lazy<IConnection>(connectionFactory.CreateConnection);
channel = new Lazy<IModel>(() => connection.Value.CreateModel()); channel = new Lazy<IModel>(connection.Value.CreateModel);
this.exchange = exchange; this.exchange = exchange;
this.eventsFilter = eventsFilter; this.eventsFilter = eventsFilter;

2
src/Squidex/Config/Domain/InfrastructureModule.cs

@ -15,8 +15,6 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Json;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Actors; using Squidex.Infrastructure.Actors;
using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.Assets;

2
src/Squidex/Config/Domain/Serializers.cs

@ -60,8 +60,6 @@ namespace Squidex.Config.Domain
TypeNameRegistry.Map(typeof(NoopEvent).GetTypeInfo().Assembly); TypeNameRegistry.Map(typeof(NoopEvent).GetTypeInfo().Assembly);
ConfigureJson(SerializerSettings, TypeNameHandling.Auto); ConfigureJson(SerializerSettings, TypeNameHandling.Auto);
JsonConvert.DefaultSettings = () => SerializerSettings;
} }
public static IServiceCollection AddMyEventFormatter(this IServiceCollection services) public static IServiceCollection AddMyEventFormatter(this IServiceCollection services)

22
src/Squidex/Config/Domain/StoreMongoDbModule.cs

@ -13,7 +13,9 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Read.Apps.Repositories; using Squidex.Domain.Apps.Read.Apps.Repositories;
using Squidex.Domain.Apps.Read.Apps.Services.Implementations;
using Squidex.Domain.Apps.Read.Assets.Repositories; using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Domain.Apps.Read.Contents.GraphQL;
using Squidex.Domain.Apps.Read.Contents.Repositories; using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Read.History.Repositories; using Squidex.Domain.Apps.Read.History.Repositories;
using Squidex.Domain.Apps.Read.MongoDb.Apps; using Squidex.Domain.Apps.Read.MongoDb.Apps;
@ -23,6 +25,7 @@ using Squidex.Domain.Apps.Read.MongoDb.History;
using Squidex.Domain.Apps.Read.MongoDb.Schemas; using Squidex.Domain.Apps.Read.MongoDb.Schemas;
using Squidex.Domain.Apps.Read.MongoDb.Webhooks; using Squidex.Domain.Apps.Read.MongoDb.Webhooks;
using Squidex.Domain.Apps.Read.Schemas.Repositories; using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Schemas.Services.Implementations;
using Squidex.Domain.Apps.Read.Webhooks.Repositories; using Squidex.Domain.Apps.Read.Webhooks.Repositories;
using Squidex.Domain.Users; using Squidex.Domain.Users;
using Squidex.Domain.Users.MongoDb; using Squidex.Domain.Users.MongoDb;
@ -144,7 +147,6 @@ namespace Squidex.Config.Domain
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration)) .WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<IAppRepository>() .As<IAppRepository>()
.As<IExternalSystem>() .As<IExternalSystem>()
.As<IEventConsumer>()
.AsSelf() .AsSelf()
.SingleInstance(); .SingleInstance();
@ -152,7 +154,6 @@ namespace Squidex.Config.Domain
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration)) .WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<ISchemaRepository>() .As<ISchemaRepository>()
.As<IExternalSystem>() .As<IExternalSystem>()
.As<IEventConsumer>()
.AsSelf() .AsSelf()
.SingleInstance(); .SingleInstance();
@ -185,6 +186,23 @@ namespace Squidex.Config.Domain
.As<IEventConsumer>() .As<IEventConsumer>()
.AsSelf() .AsSelf()
.SingleInstance(); .SingleInstance();
builder.Register(c =>
new CompoundEventConsumer(
c.Resolve<MongoSchemaRepository>(),
c.Resolve<CachingGraphQLService>(),
c.Resolve<CachingSchemaProvider>()))
.As<IEventConsumer>()
.AsSelf()
.SingleInstance();
builder.Register(c =>
new CompoundEventConsumer(
c.Resolve<MongoAppRepository>(),
c.Resolve<CachingAppProvider>()))
.As<IEventConsumer>()
.AsSelf()
.SingleInstance();
} }
} }
} }

20
src/Squidex/Config/Domain/Usages.cs

@ -9,10 +9,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Squidex.Domain.Apps.Read.Apps.Repositories;
using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Actors; using Squidex.Infrastructure.Actors;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
@ -42,22 +38,6 @@ namespace Squidex.Config.Domain
} }
} }
var appRepository = services.GetService<IAppRepository>();
var appProvider = services.GetService<IAppProvider>();
if (appProvider != null)
{
appRepository?.SubscribeOnChanged(appProvider.Invalidate);
}
var schemaRepository = services.GetService<ISchemaRepository>();
var schemaProvider = services.GetService<ISchemaProvider>();
if (schemaProvider != null)
{
schemaRepository?.SubscribeOnChanged(schemaProvider.Invalidate);
}
return app; return app;
} }

1
src/Squidex/Config/Identity/LazyClientStore.cs

@ -8,7 +8,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4; using IdentityServer4;
using IdentityServer4.Models; using IdentityServer4.Models;

28
tests/Squidex.Domain.Apps.Read.Tests/Apps/CachingAppProviderTests.cs

@ -11,9 +11,11 @@ using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Read.Apps.Repositories; using Squidex.Domain.Apps.Read.Apps.Repositories;
using Squidex.Domain.Apps.Read.Apps.Services.Implementations; using Squidex.Domain.Apps.Read.Apps.Services.Implementations;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Read.Apps namespace Squidex.Domain.Apps.Read.Apps
@ -38,6 +40,24 @@ namespace Squidex.Domain.Apps.Read.Apps
sut = new CachingAppProvider(cache, repository); sut = new CachingAppProvider(cache, repository);
} }
[Fact]
public void Should_return_empty_for_events_filter()
{
Assert.Equal(string.Empty, sut.EventsFilter);
}
[Fact]
public void Should_return_empty_for_name()
{
Assert.Equal(typeof(CachingAppProvider).Name, sut.Name);
}
[Fact]
public void Should_do_nothing_when_clearing()
{
Assert.NotNull(sut.ClearAsync());
}
[Fact] [Fact]
public async Task Should_also_retrieve_app_by_name_if_retrieved_by_id_before() public async Task Should_also_retrieve_app_by_name_if_retrieved_by_id_before()
{ {
@ -65,7 +85,7 @@ namespace Squidex.Domain.Apps.Read.Apps
} }
[Fact] [Fact]
public async Task Should_clear_cache_for_id_after_invalidating() public async Task Should_clear_cache_for_id_after_update_event()
{ {
A.CallTo(() => repository.FindAppAsync(appId.Id)) A.CallTo(() => repository.FindAppAsync(appId.Id))
.Returns(appV2); .Returns(appV2);
@ -74,7 +94,7 @@ namespace Squidex.Domain.Apps.Read.Apps
await ProvideAppByIdAsync(appV1); await ProvideAppByIdAsync(appV1);
sut.Invalidate(appId); sut.On(Envelope.Create(new AppLanguageAdded { AppId = appId }).To<IEvent>()).Wait();
await ProvideAppByIdAsync(appV2); await ProvideAppByIdAsync(appV2);
@ -82,7 +102,7 @@ namespace Squidex.Domain.Apps.Read.Apps
} }
[Fact] [Fact]
public async Task Should_clear_cache_for_name_after_invalidating() public async Task Should_clear_cache_for_name_after_update_event()
{ {
A.CallTo(() => repository.FindAppAsync(appId.Name)) A.CallTo(() => repository.FindAppAsync(appId.Name))
.Returns(appV2); .Returns(appV2);
@ -91,7 +111,7 @@ namespace Squidex.Domain.Apps.Read.Apps
await ProvideAppByNameAsync(appV1); await ProvideAppByNameAsync(appV1);
sut.Invalidate(appId); sut.On(Envelope.Create(new AppLanguageAdded { AppId = appId }).To<IEvent>()).Wait();
await ProvideAppByNameAsync(appV2); await ProvideAppByNameAsync(appV2);

26
tests/Squidex.Domain.Apps.Read.Tests/Schemas/CachingSchemaProviderTests.cs

@ -43,6 +43,24 @@ namespace Squidex.Domain.Apps.Read.Schemas
sut = new CachingSchemaProvider(cache, repository); sut = new CachingSchemaProvider(cache, repository);
} }
[Fact]
public void Should_return_empty_for_events_filter()
{
Assert.Equal(string.Empty, sut.EventsFilter);
}
[Fact]
public void Should_return_empty_for_name()
{
Assert.Equal(typeof(CachingSchemaProvider).Name, sut.Name);
}
[Fact]
public void Should_do_nothing_when_clearing()
{
Assert.NotNull(sut.ClearAsync());
}
[Fact] [Fact]
public async Task Should_also_retrieve_schema_by_name_if_retrieved_by_id_before() public async Task Should_also_retrieve_schema_by_name_if_retrieved_by_id_before()
{ {
@ -70,7 +88,7 @@ namespace Squidex.Domain.Apps.Read.Schemas
} }
[Fact] [Fact]
public async Task Should_clear_cache_for_id_after_invalidating() public async Task Should_clear_cache_for_id_after_update_event()
{ {
A.CallTo(() => repository.FindSchemaAsync(schemaId.Id)) A.CallTo(() => repository.FindSchemaAsync(schemaId.Id))
.Returns(schemaV2); .Returns(schemaV2);
@ -79,7 +97,7 @@ namespace Squidex.Domain.Apps.Read.Schemas
await ProvideSchemaById(schemaV1); await ProvideSchemaById(schemaV1);
sut.Invalidate(appId, schemaId); sut.On(Envelope.Create(new FieldAdded { AppId = appId, SchemaId = schemaId })).Wait();
await ProvideSchemaById(schemaV2); await ProvideSchemaById(schemaV2);
@ -87,7 +105,7 @@ namespace Squidex.Domain.Apps.Read.Schemas
} }
[Fact] [Fact]
public async Task Should_clear_cache_for_name_after_invalidating() public async Task Should_clear_cache_for_name_after_update_event()
{ {
A.CallTo(() => repository.FindSchemaAsync(appId.Id, schemaId.Name)) A.CallTo(() => repository.FindSchemaAsync(appId.Id, schemaId.Name))
.Returns(schemaV2); .Returns(schemaV2);
@ -96,7 +114,7 @@ namespace Squidex.Domain.Apps.Read.Schemas
await ProvideSchemaByName(schemaV1); await ProvideSchemaByName(schemaV1);
sut.Invalidate(appId, schemaId); sut.On(Envelope.Create(new SchemaUpdated { AppId = appId, SchemaId = schemaId })).Wait();
await ProvideSchemaByName(schemaV2); await ProvideSchemaByName(schemaV2);

2
tests/Squidex.Infrastructure.Tests/Json/ConverterContractResolverTests.cs

@ -31,7 +31,7 @@ namespace Squidex.Infrastructure.Json
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{ {
throw new NotImplementedException(); throw new NotSupportedException();
} }
public override bool CanConvert(Type objectType) public override bool CanConvert(Type objectType)

Loading…
Cancel
Save