mirror of https://github.com/Squidex/squidex.git
74 changed files with 762 additions and 1514 deletions
@ -1,53 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoAppEntity.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Apps |
|||
{ |
|||
public sealed class MongoAppEntity : MongoEntity, IAppEntity |
|||
{ |
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public string Name { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public long Version { get; set; } |
|||
|
|||
[BsonIgnoreIfDefault] |
|||
[BsonElement] |
|||
public string PlanId { get; set; } |
|||
|
|||
[BsonIgnoreIfDefault] |
|||
[BsonElement] |
|||
public string PlanOwner { get; set; } |
|||
|
|||
[BsonIgnoreIfDefault] |
|||
[BsonElement] |
|||
public string[] ContributorIds { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
[BsonJson] |
|||
public AppClients Clients { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
[BsonJson] |
|||
public AppContributors Contributors { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
[BsonJson] |
|||
public LanguagesConfig LanguagesConfig { get; set; } |
|||
} |
|||
} |
|||
@ -1,70 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoAppRepository.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using Squidex.Domain.Apps.Read.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Repositories; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Apps |
|||
{ |
|||
public partial class MongoAppRepository : MongoRepositoryBase<MongoAppEntity>, IAppRepository, IAppEventConsumer |
|||
{ |
|||
public MongoAppRepository(IMongoDatabase database) |
|||
: base(database) |
|||
{ |
|||
} |
|||
|
|||
protected override string CollectionName() |
|||
{ |
|||
return "Projections_Apps"; |
|||
} |
|||
|
|||
protected override async Task SetupCollectionAsync(IMongoCollection<MongoAppEntity> collection) |
|||
{ |
|||
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name)); |
|||
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.ContributorIds)); |
|||
} |
|||
|
|||
protected override MongoCollectionSettings CollectionSettings() |
|||
{ |
|||
return new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority }; |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<IAppEntity>> QueryAllAsync(string subjectId) |
|||
{ |
|||
var appEntities = |
|||
await Collection.Find(s => s.ContributorIds.Contains(subjectId)) |
|||
.ToListAsync(); |
|||
|
|||
return appEntities; |
|||
} |
|||
|
|||
public async Task<IAppEntity> FindAppAsync(Guid id) |
|||
{ |
|||
var appEntity = |
|||
await Collection.Find(s => s.Id == id) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
return appEntity; |
|||
} |
|||
|
|||
public async Task<IAppEntity> FindAppAsync(string name) |
|||
{ |
|||
var appEntity = |
|||
await Collection.Find(s => s.Name == name) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
return appEntity; |
|||
} |
|||
} |
|||
} |
|||
@ -1,143 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoAppRepository_EventHandling.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Domain.Apps.Events.Apps.Utils; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using Squidex.Infrastructure.Dispatching; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Apps |
|||
{ |
|||
public partial class MongoAppRepository |
|||
{ |
|||
public string Name |
|||
{ |
|||
get { return GetType().Name; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return "^app-"; } |
|||
} |
|||
|
|||
public Task On(Envelope<IEvent> @event) |
|||
{ |
|||
return this.DispatchActionAsync(@event.Payload, @event.Headers); |
|||
} |
|||
|
|||
protected Task On(AppCreated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.CreateAsync(@event, headers, a => |
|||
{ |
|||
SimpleMapper.Map(@event, a); |
|||
|
|||
a.Clients = new AppClients(); |
|||
a.Contributors = new AppContributors(); |
|||
|
|||
a.LanguagesConfig = LanguagesConfig.Build(Language.EN); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppPlanChanged @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
SimpleMapper.Map(@event, a); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppClientAttached @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.Clients.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppClientRevoked @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.Clients.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppClientRenamed @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.Clients.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppClientUpdated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.Clients.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppContributorRemoved @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.Contributors.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppContributorAssigned @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.Contributors.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppLanguageAdded @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.LanguagesConfig.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppLanguageRemoved @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.LanguagesConfig.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AppLanguageUpdated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateAppAsync(@event, headers, a => |
|||
{ |
|||
a.LanguagesConfig.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
private async Task UpdateAppAsync(AppEvent @event, EnvelopeHeaders headers, Action<MongoAppEntity> updater) |
|||
{ |
|||
await Collection.UpdateAsync(@event, headers, a => |
|||
{ |
|||
updater(a); |
|||
|
|||
a.ContributorIds = a.Contributors.Keys.ToArray(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,74 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoSchemaEntity.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Read.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas |
|||
{ |
|||
public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity |
|||
{ |
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public string Name { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public long Version { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public Guid AppId { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public RefToken CreatedBy { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public RefToken LastModifiedBy { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public bool IsPublished { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public bool IsDeleted { get; set; } |
|||
|
|||
[BsonIgnoreIfNull] |
|||
[BsonElement] |
|||
public string ScriptQuery { get; set; } |
|||
|
|||
[BsonIgnoreIfNull] |
|||
[BsonElement] |
|||
public string ScriptCreate { get; set; } |
|||
|
|||
[BsonIgnoreIfNull] |
|||
[BsonElement] |
|||
public string ScriptUpdate { get; set; } |
|||
|
|||
[BsonIgnoreIfNull] |
|||
[BsonElement] |
|||
public string ScriptDelete { get; set; } |
|||
|
|||
[BsonIgnoreIfNull] |
|||
[BsonElement] |
|||
public string ScriptChange { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
[BsonJson] |
|||
public Schema SchemaDef { get; set; } |
|||
} |
|||
} |
|||
@ -1,73 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoSchemaRepository.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Read.Schemas; |
|||
using Squidex.Domain.Apps.Read.Schemas.Repositories; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas |
|||
{ |
|||
public partial class MongoSchemaRepository : MongoRepositoryBase<MongoSchemaEntity>, ISchemaRepository, ISchemaEventConsumer |
|||
{ |
|||
private readonly FieldRegistry registry; |
|||
|
|||
public MongoSchemaRepository(IMongoDatabase database, FieldRegistry registry) |
|||
: base(database) |
|||
{ |
|||
Guard.NotNull(registry, nameof(registry)); |
|||
|
|||
this.registry = registry; |
|||
} |
|||
|
|||
protected override string CollectionName() |
|||
{ |
|||
return "Projections_Schemas"; |
|||
} |
|||
|
|||
protected override Task SetupCollectionAsync(IMongoCollection<MongoSchemaEntity> collection) |
|||
{ |
|||
return Task.WhenAll( |
|||
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name)), |
|||
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Ascending(x => x.IsDeleted).Ascending(x => x.Name))); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<ISchemaEntity>> QueryAllAsync(Guid appId) |
|||
{ |
|||
var schemaEntities = |
|||
await Collection.Find(s => s.AppId == appId && !s.IsDeleted) |
|||
.ToListAsync(); |
|||
|
|||
return schemaEntities.OfType<ISchemaEntity>().ToList(); |
|||
} |
|||
|
|||
public async Task<ISchemaEntity> FindSchemaAsync(Guid appId, string name) |
|||
{ |
|||
var schemaEntity = |
|||
await Collection.Find(s => s.AppId == appId && !s.IsDeleted && s.Name == name) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
return schemaEntity; |
|||
} |
|||
|
|||
public async Task<ISchemaEntity> FindSchemaAsync(Guid schemaId) |
|||
{ |
|||
var schemaEntity = |
|||
await Collection.Find(s => s.Id == schemaId) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
return schemaEntity; |
|||
} |
|||
} |
|||
} |
|||
@ -1,188 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoSchemaRepository_EventHandling.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Schemas; |
|||
using Squidex.Domain.Apps.Events.Schemas.Old; |
|||
using Squidex.Domain.Apps.Events.Schemas.Utils; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using Squidex.Infrastructure.Dispatching; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
#pragma warning disable CS0612 // Type or member is obsolete
|
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas |
|||
{ |
|||
public partial class MongoSchemaRepository |
|||
{ |
|||
public string Name |
|||
{ |
|||
get { return GetType().Name; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return "^schema-"; } |
|||
} |
|||
|
|||
public Task On(Envelope<IEvent> @event) |
|||
{ |
|||
return this.DispatchActionAsync(@event.Payload, @event.Headers); |
|||
} |
|||
|
|||
protected Task On(SchemaCreated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.CreateAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef = SchemaEventDispatcher.Create(@event, registry); |
|||
|
|||
SimpleMapper.Map(@event, s); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldDeleted @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldLocked @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldHidden @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldShown @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldDisabled @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldEnabled @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldUpdated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(SchemaFieldsReordered @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(SchemaUpdated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(SchemaPublished @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(SchemaUnpublished @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(FieldAdded @event, EnvelopeHeaders headers) |
|||
{ |
|||
return UpdateSchemaAsync(@event, headers, s => |
|||
{ |
|||
s.SchemaDef.Apply(@event, registry); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(ScriptsConfigured @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.UpdateAsync(@event, headers, s => |
|||
{ |
|||
SimpleMapper.Map(@event, s); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(SchemaDeleted @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.UpdateAsync(@event, headers, s => |
|||
{ |
|||
s.IsDeleted = true; |
|||
}); |
|||
} |
|||
|
|||
protected Task On(WebhookAdded @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.UpdateAsync(@event, headers, s => |
|||
{ |
|||
/* NOOP */ |
|||
}); |
|||
} |
|||
|
|||
protected Task On(WebhookDeleted @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.UpdateAsync(@event, headers, s => |
|||
{ |
|||
/* NOOP */ |
|||
}); |
|||
} |
|||
|
|||
private Task UpdateSchemaAsync(SquidexEvent @event, EnvelopeHeaders headers, Action<MongoSchemaEntity> updater) |
|||
{ |
|||
return Collection.UpdateAsync(@event, headers, s => |
|||
{ |
|||
updater(s); |
|||
|
|||
s.IsPublished = s.SchemaDef.IsPublished; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// ==========================================================================
|
|||
// IAppRepository.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Apps.Repositories |
|||
{ |
|||
public interface IAppRepository |
|||
{ |
|||
Task<IReadOnlyList<IAppEntity>> QueryAllAsync(string subjectId); |
|||
|
|||
Task<IAppEntity> FindAppAsync(Guid appId); |
|||
|
|||
Task<IAppEntity> FindAppAsync(string name); |
|||
} |
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
// ==========================================================================
|
|||
// IAppProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Apps.Services |
|||
{ |
|||
public interface IAppProvider |
|||
{ |
|||
Task<IAppEntity> FindAppByIdAsync(Guid id); |
|||
|
|||
Task<IAppEntity> FindAppByNameAsync(string name); |
|||
} |
|||
} |
|||
@ -1,121 +0,0 @@ |
|||
// ==========================================================================
|
|||
// CachingAppProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Read.Apps.Repositories; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Caching; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations |
|||
{ |
|||
public class CachingAppProvider : CachingProviderBase, IAppProvider, IEventConsumer |
|||
{ |
|||
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30); |
|||
private readonly IAppRepository repository; |
|||
|
|||
public string Name |
|||
{ |
|||
get { return GetType().Name; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return string.Empty; } |
|||
} |
|||
|
|||
public CachingAppProvider(IMemoryCache cache, IAppRepository repository) |
|||
: base(cache) |
|||
{ |
|||
Guard.NotNull(cache, nameof(cache)); |
|||
|
|||
this.repository = repository; |
|||
} |
|||
|
|||
public async Task<IAppEntity> FindAppByIdAsync(Guid appId) |
|||
{ |
|||
var cacheKey = BuildIdCacheKey(appId); |
|||
|
|||
if (!Cache.TryGetValue(cacheKey, out IAppEntity result)) |
|||
{ |
|||
result = await repository.FindAppAsync(appId); |
|||
|
|||
Cache.Set(cacheKey, result, CacheDuration); |
|||
|
|||
if (result != null) |
|||
{ |
|||
Cache.Set(BuildNameCacheKey(result.Name), result, CacheDuration); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<IAppEntity> FindAppByNameAsync(string name) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
|
|||
var cacheKey = BuildNameCacheKey(name); |
|||
|
|||
if (!Cache.TryGetValue(cacheKey, out IAppEntity result)) |
|||
{ |
|||
result = await repository.FindAppAsync(name); |
|||
|
|||
Cache.Set(cacheKey, result, CacheDuration); |
|||
|
|||
if (result != null) |
|||
{ |
|||
Cache.Set(BuildIdCacheKey(result.Id), result, CacheDuration); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public Task On(Envelope<IEvent> @event) |
|||
{ |
|||
void Remove(NamedId<Guid> id) |
|||
{ |
|||
var cacheKeyById = BuildIdCacheKey(id.Id); |
|||
var cacheKeyByName = BuildNameCacheKey(id.Name); |
|||
|
|||
Cache.Remove(cacheKeyById); |
|||
Cache.Remove(cacheKeyByName); |
|||
|
|||
Cache.Invalidate(cacheKeyById); |
|||
Cache.Invalidate(cacheKeyByName); |
|||
} |
|||
|
|||
if (@event.Payload is AppEvent appEvent) |
|||
{ |
|||
Remove(appEvent.AppId); |
|||
} |
|||
|
|||
return TaskHelper.Done; |
|||
} |
|||
|
|||
private static string BuildNameCacheKey(string name) |
|||
{ |
|||
return $"App_Ids_{name}"; |
|||
} |
|||
|
|||
private static string BuildIdCacheKey(Guid schemaId) |
|||
{ |
|||
return $"App_Names_{schemaId}"; |
|||
} |
|||
|
|||
public Task ClearAsync() |
|||
{ |
|||
return TaskHelper.Done; |
|||
} |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
// ==========================================================================
|
|||
// ISchemaEventConsumer.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Schemas |
|||
{ |
|||
public interface ISchemaEventConsumer : IEventConsumer |
|||
{ |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// ==========================================================================
|
|||
// ISchemaRepository.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Schemas.Repositories |
|||
{ |
|||
public interface ISchemaRepository |
|||
{ |
|||
Task<IReadOnlyList<ISchemaEntity>> QueryAllAsync(Guid appId); |
|||
|
|||
Task<ISchemaEntity> FindSchemaAsync(Guid appId, string name); |
|||
|
|||
Task<ISchemaEntity> FindSchemaAsync(Guid schemaId); |
|||
} |
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
// ==========================================================================
|
|||
// ISchemaProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Schemas.Services |
|||
{ |
|||
public interface ISchemaProvider |
|||
{ |
|||
Task<ISchemaEntity> FindSchemaByIdAsync(Guid id, bool provideDeleted = false); |
|||
|
|||
Task<ISchemaEntity> FindSchemaByNameAsync(Guid appId, string name); |
|||
} |
|||
} |
|||
@ -1,131 +0,0 @@ |
|||
// ==========================================================================
|
|||
// CachingSchemaProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Read.Schemas.Repositories; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Caching; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations |
|||
{ |
|||
public class CachingSchemaProvider : CachingProviderBase, ISchemaProvider, IEventConsumer |
|||
{ |
|||
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10); |
|||
private readonly ISchemaRepository repository; |
|||
|
|||
public string Name |
|||
{ |
|||
get { return GetType().Name; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return string.Empty; } |
|||
} |
|||
|
|||
public CachingSchemaProvider(IMemoryCache cache, ISchemaRepository repository) |
|||
: base(cache) |
|||
{ |
|||
Guard.NotNull(repository, nameof(repository)); |
|||
|
|||
this.repository = repository; |
|||
} |
|||
|
|||
public async Task<ISchemaEntity> FindSchemaByIdAsync(Guid id, bool provideDeleted = false) |
|||
{ |
|||
var cacheKey = BuildIdCacheKey(id); |
|||
|
|||
if (!Cache.TryGetValue(cacheKey, out ISchemaEntity result)) |
|||
{ |
|||
result = await repository.FindSchemaAsync(id); |
|||
|
|||
Cache.Set(cacheKey, result, CacheDuration); |
|||
|
|||
if (result != null) |
|||
{ |
|||
Cache.Set(BuildNameCacheKey(result.AppId, result.Name), result, CacheDuration); |
|||
} |
|||
} |
|||
|
|||
if (result != null && result.IsDeleted && !provideDeleted) |
|||
{ |
|||
result = null; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<ISchemaEntity> FindSchemaByNameAsync(Guid appId, string name) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
|
|||
var cacheKey = BuildNameCacheKey(appId, name); |
|||
|
|||
if (!Cache.TryGetValue(cacheKey, out ISchemaEntity result)) |
|||
{ |
|||
result = await repository.FindSchemaAsync(appId, name); |
|||
|
|||
Cache.Set(cacheKey, result, CacheDuration); |
|||
|
|||
if (result != null) |
|||
{ |
|||
Cache.Set(BuildIdCacheKey(result.Id), result, CacheDuration); |
|||
} |
|||
} |
|||
|
|||
if (result != null && result.IsDeleted) |
|||
{ |
|||
result = null; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public Task On(Envelope<IEvent> @event) |
|||
{ |
|||
void Remove(NamedId<Guid> appId, NamedId<Guid> schemaId) |
|||
{ |
|||
var cacheKeyById = BuildIdCacheKey(schemaId.Id); |
|||
var cacheKeyByName = BuildNameCacheKey(appId.Id, schemaId.Name); |
|||
|
|||
Cache.Remove(cacheKeyById); |
|||
Cache.Remove(cacheKeyByName); |
|||
|
|||
Cache.Invalidate(cacheKeyById); |
|||
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) |
|||
{ |
|||
return $"Schema_Ids_{appId}_{name}"; |
|||
} |
|||
|
|||
private static string BuildIdCacheKey(Guid schemaId) |
|||
{ |
|||
return $"Schema_Names_{schemaId}"; |
|||
} |
|||
|
|||
public Task ClearAsync() |
|||
{ |
|||
return TaskHelper.Done; |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +1,19 @@ |
|||
// ==========================================================================
|
|||
// EventMessage.cs
|
|||
// AppUserGrainState.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Orleans.Concurrency; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using System.Collections.Generic; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains |
|||
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
|||
{ |
|||
[Immutable] |
|||
public sealed class EventMessage |
|||
public sealed class AppUserGrainState |
|||
{ |
|||
public Envelope<IEvent> Event { get; set; } |
|||
[JsonProperty] |
|||
public HashSet<string> AppNames { get; set; } = new HashSet<string>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
// ==========================================================================
|
|||
// OrleansApps.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Orleans; |
|||
using Squidex.Domain.Apps.Read.Apps; |
|||
using Squidex.Domain.Apps.Read.Rules; |
|||
using Squidex.Domain.Apps.Read.Schemas; |
|||
using Squidex.Domain.Apps.Read.State.Orleans.Grains; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.State.Orleans |
|||
{ |
|||
public sealed class OrleansAppProvider : IAppProvider |
|||
{ |
|||
private readonly IGrainFactory factory; |
|||
|
|||
public OrleansAppProvider(IGrainFactory factory) |
|||
{ |
|||
Guard.NotNull(factory, nameof(factory)); |
|||
|
|||
this.factory = factory; |
|||
} |
|||
|
|||
public async Task<IAppEntity> GetAppAsync(string appName) |
|||
{ |
|||
var result = await factory.GetGrain<IAppStateGrain>(appName).GetAppAsync(); |
|||
|
|||
return result.Value; |
|||
} |
|||
|
|||
public async Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(string appName, Guid id) |
|||
{ |
|||
var result = await factory.GetGrain<IAppStateGrain>(appName).GetAppWithSchemaAsync(id); |
|||
|
|||
return result.Value; |
|||
} |
|||
|
|||
public async Task<List<IRuleEntity>> GetRulesAsync(string appName) |
|||
{ |
|||
var result = await factory.GetGrain<IAppStateGrain>(appName).GetRulesAsync(); |
|||
|
|||
return result.Value; |
|||
} |
|||
|
|||
public async Task<ISchemaEntity> GetSchemaAsync(string appName, Guid id, bool provideDeleted = false) |
|||
{ |
|||
var result = await factory.GetGrain<IAppStateGrain>(appName).GetSchemaAsync(id, provideDeleted); |
|||
|
|||
return result.Value; |
|||
} |
|||
|
|||
public async Task<ISchemaEntity> GetSchemaAsync(string appName, string name, bool provideDeleted = false) |
|||
{ |
|||
var result = await factory.GetGrain<IAppStateGrain>(appName).GetSchemaAsync(name, provideDeleted); |
|||
|
|||
return result.Value; |
|||
} |
|||
|
|||
public async Task<List<ISchemaEntity>> GetSchemasAsync(string appName) |
|||
{ |
|||
var result = await factory.GetGrain<IAppStateGrain>(appName).GetSchemasAsync(); |
|||
|
|||
return result.Value; |
|||
} |
|||
|
|||
public async Task<List<IAppEntity>> GetUserApps(string userId) |
|||
{ |
|||
var schemaIds = await factory.GetGrain<IAppUserGrain>(userId).GetSchemaNamesAsync(); |
|||
|
|||
var tasks = |
|||
schemaIds |
|||
.Select(x => factory.GetGrain<IAppStateGrain>(x)) |
|||
.Select(x => x.GetAppAsync()); |
|||
|
|||
var apps = await Task.WhenAll(tasks); |
|||
|
|||
return apps.Select(a => a.Value).Where(a => a != null).ToList(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,71 +0,0 @@ |
|||
// ==========================================================================
|
|||
// OrleansAppState.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Orleans; |
|||
using Squidex.Domain.Apps.Read.Apps; |
|||
using Squidex.Domain.Apps.Read.Rules; |
|||
using Squidex.Domain.Apps.Read.Schemas; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains |
|||
{ |
|||
public sealed class OrleansAppState : IAppState |
|||
{ |
|||
private readonly IGrainFactory factory; |
|||
|
|||
public OrleansAppState(IGrainFactory factory) |
|||
{ |
|||
Guard.NotNull(factory, nameof(factory)); |
|||
|
|||
this.factory = factory; |
|||
} |
|||
|
|||
public Task<IAppEntity> GetAppAsync(Guid appId) |
|||
{ |
|||
return factory.GetGrain<IAppStateGrain>(appId).GetAppAsync(); |
|||
} |
|||
|
|||
public Task<List<IRuleEntity>> GetRulesAsync(Guid appId) |
|||
{ |
|||
return factory.GetGrain<IAppStateGrain>(appId).GetRulesAsync(); |
|||
} |
|||
|
|||
public Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false) |
|||
{ |
|||
return factory.GetGrain<IAppStateGrain>(appId).GetSchemaAsync(id, provideDeleted); |
|||
} |
|||
|
|||
public Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name, bool provideDeleted = false) |
|||
{ |
|||
return factory.GetGrain<IAppStateGrain>(appId).GetSchemaAsync(name, provideDeleted); |
|||
} |
|||
|
|||
public Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId) |
|||
{ |
|||
return factory.GetGrain<IAppStateGrain>(appId).GetSchemasAsync(); |
|||
} |
|||
|
|||
public async Task<List<IAppEntity>> GetUserApps(string userId) |
|||
{ |
|||
var schemaIds = await factory.GetGrain<IAppUserGrain>(userId).GetSchemaIdsAsync(); |
|||
|
|||
var tasks = |
|||
schemaIds |
|||
.Select(x => factory.GetGrain<IAppStateGrain>(x)) |
|||
.Select(x => x.GetAppAsync()); |
|||
|
|||
var apps = await Task.WhenAll(tasks); |
|||
|
|||
return apps.Where(a => a != null).ToList(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,16 +1,15 @@ |
|||
// ==========================================================================
|
|||
// IAppEventConsumer.cs
|
|||
// IJsonValue.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Apps |
|||
namespace Squidex.Infrastructure.Json.Orleans |
|||
{ |
|||
public interface IAppEventConsumer : IEventConsumer |
|||
public interface IJsonValue |
|||
{ |
|||
object Value { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// ==========================================================================
|
|||
// J.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Squidex.Infrastructure.Json.Orleans |
|||
{ |
|||
public struct J<T> : IJsonValue |
|||
{ |
|||
private readonly T value; |
|||
|
|||
public T Value |
|||
{ |
|||
get { return value; } |
|||
} |
|||
|
|||
object IJsonValue.Value |
|||
{ |
|||
get { return Value; } |
|||
} |
|||
|
|||
[JsonConstructor] |
|||
public J(T value) |
|||
{ |
|||
this.value = value; |
|||
} |
|||
|
|||
public static implicit operator T(J<T> value) |
|||
{ |
|||
return value.Value; |
|||
} |
|||
|
|||
public static implicit operator J<T>(T d) |
|||
{ |
|||
return new J<T>(d); |
|||
} |
|||
|
|||
public static Task<J<T>> AsTask(T value) |
|||
{ |
|||
return Task.FromResult<J<T>>(value); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
// ==========================================================================
|
|||
// JsonExternalSerializer.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Newtonsoft.Json; |
|||
using Orleans.Runtime; |
|||
using Orleans.Serialization; |
|||
|
|||
namespace Squidex.Infrastructure.Json.Orleans |
|||
{ |
|||
public class JsonExternalSerializer : IExternalSerializer |
|||
{ |
|||
private readonly JsonSerializer jsonSerializer; |
|||
|
|||
public JsonExternalSerializer(JsonSerializer jsonSerializer) |
|||
{ |
|||
Guard.NotNull(jsonSerializer, nameof(jsonSerializer)); |
|||
|
|||
this.jsonSerializer = jsonSerializer; |
|||
} |
|||
|
|||
public void Initialize(Logger logger) |
|||
{ |
|||
} |
|||
|
|||
public bool IsSupportedType(Type itemType) |
|||
{ |
|||
return itemType.GetInterfaces().Contains(typeof(IJsonValue)); |
|||
} |
|||
|
|||
public object DeepCopy(object source, ICopyContext context) |
|||
{ |
|||
return source; |
|||
} |
|||
|
|||
public object Deserialize(Type expectedType, IDeserializationContext context) |
|||
{ |
|||
var outLength = context.StreamReader.ReadInt(); |
|||
var outBytes = context.StreamReader.ReadBytes(outLength); |
|||
|
|||
var json = Encoding.Default.GetString(outBytes); |
|||
|
|||
var stream = new MemoryStream(outBytes); |
|||
|
|||
using (var reader = new JsonTextReader(new StreamReader(stream))) |
|||
{ |
|||
return jsonSerializer.Deserialize(reader, expectedType); |
|||
} |
|||
} |
|||
|
|||
public void Serialize(object item, ISerializationContext context, Type expectedType) |
|||
{ |
|||
var stream = new MemoryStream(); |
|||
|
|||
using (var writer = new JsonTextWriter(new StreamWriter(stream))) |
|||
{ |
|||
jsonSerializer.Serialize(writer, item); |
|||
|
|||
writer.Flush(); |
|||
} |
|||
|
|||
var outBytes = stream.ToArray(); |
|||
|
|||
var json = Encoding.Default.GetString(outBytes, 0, outBytes.Length); |
|||
|
|||
context.StreamWriter.Write(outBytes.Length); |
|||
context.StreamWriter.Write(outBytes); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// ==========================================================================
|
|||
// JsonState.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Linq; |
|||
|
|||
namespace Squidex.Infrastructure.Json.Orleans |
|||
{ |
|||
public sealed class JsonState<TValue> where TValue : class, new() |
|||
{ |
|||
private JsonSerializer serializer = JsonSerializer.CreateDefault(); |
|||
|
|||
private TValue value; |
|||
|
|||
[JsonProperty] |
|||
public JToken Json { get; set; } |
|||
|
|||
[JsonIgnore] |
|||
public TValue Value |
|||
{ |
|||
get |
|||
{ |
|||
if (value == null) |
|||
{ |
|||
value = ReadState(); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
set |
|||
{ |
|||
this.value = value; |
|||
} |
|||
} |
|||
|
|||
public void SetSerializer(JsonSerializer serializer) |
|||
{ |
|||
if (serializer != null) |
|||
{ |
|||
this.serializer = serializer; |
|||
} |
|||
} |
|||
|
|||
public JsonState<TValue> Update(Action<TValue> updater) |
|||
{ |
|||
var value = ReadState(); |
|||
|
|||
updater(value); |
|||
|
|||
var json = JObject.FromObject(value, serializer); |
|||
|
|||
return new JsonState<TValue> { serializer = serializer, Json = json, Value = value }; |
|||
} |
|||
|
|||
private TValue ReadState() |
|||
{ |
|||
if (Json != null) |
|||
{ |
|||
return Json.ToObject<TValue>(serializer); |
|||
} |
|||
else |
|||
{ |
|||
return new TValue(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// ==========================================================================
|
|||
// SimpleCopier.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
#pragma warning disable RECS0108 // Warns about static fields in generic types
|
|||
|
|||
namespace Squidex.Infrastructure.Reflection |
|||
{ |
|||
public static class SimpleCopier |
|||
{ |
|||
private struct PropertyMapper |
|||
{ |
|||
private readonly IPropertyAccessor accessor; |
|||
private readonly Func<object, object> converter; |
|||
|
|||
public PropertyMapper(IPropertyAccessor accessor, Func<object, object> converter) |
|||
{ |
|||
this.accessor = accessor; |
|||
this.converter = converter; |
|||
} |
|||
|
|||
public void MapProperty(object source, object target) |
|||
{ |
|||
var value = converter(accessor.Get(source)); |
|||
|
|||
accessor.Set(target, value); |
|||
} |
|||
} |
|||
|
|||
private static class ClassCopier<T> where T : class, new() |
|||
{ |
|||
private static readonly List<PropertyMapper> Mappers = new List<PropertyMapper>(); |
|||
|
|||
static ClassCopier() |
|||
{ |
|||
var type = typeof(T); |
|||
|
|||
foreach (var property in type.GetPublicProperties()) |
|||
{ |
|||
if (!property.CanWrite || !property.CanRead) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var accessor = new PropertyAccessor(type, property); |
|||
|
|||
if (property.PropertyType.Implements<ICloneable>()) |
|||
{ |
|||
Mappers.Add(new PropertyMapper(accessor, x => x == null ? x : ((ICloneable)x).Clone())); |
|||
} |
|||
else |
|||
{ |
|||
Mappers.Add(new PropertyMapper(accessor, x => x)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static T Copy(T source) |
|||
{ |
|||
var destination = new T(); |
|||
|
|||
foreach (var mapper in Mappers) |
|||
{ |
|||
mapper.MapProperty(source, destination); |
|||
} |
|||
|
|||
return destination; |
|||
} |
|||
} |
|||
|
|||
public static T Clone<T>(T source) where T : class, new() |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
return ClassCopier<T>.Copy(source); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// ==========================================================================
|
|||
// CustomJsonSerializer.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json; |
|||
using Squidex.Config.Domain; |
|||
using Squidex.Infrastructure.Json.Orleans; |
|||
|
|||
namespace Squidex.Config.Orleans |
|||
{ |
|||
public class CustomJsonSerializer : JsonExternalSerializer |
|||
{ |
|||
public CustomJsonSerializer() |
|||
: base(JsonSerializer.Create(SerializationServices.DefaultJsonSettings)) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
// ==========================================================================
|
|||
// CustomMongoDbStorageProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json; |
|||
using Orleans.Providers; |
|||
using Orleans.Providers.MongoDB.StorageProviders; |
|||
using Squidex.Config.Domain; |
|||
|
|||
namespace Squidex.Config.Orleans |
|||
{ |
|||
public sealed class CustomMongoDbStorageProvider : MongoStorageProvider |
|||
{ |
|||
protected override JsonSerializerSettings ReturnSerializerSettings(IProviderRuntime providerRuntime, IProviderConfiguration config) |
|||
{ |
|||
return SerializationServices.DefaultJsonSettings; |
|||
} |
|||
} |
|||
} |
|||
@ -1,131 +0,0 @@ |
|||
// ==========================================================================
|
|||
// CachingAppProviderTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Microsoft.Extensions.Options; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Domain.Apps.Read.Apps.Repositories; |
|||
using Squidex.Domain.Apps.Read.Apps.Services.Implementations; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.CQRS.Events; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.Apps |
|||
{ |
|||
public class CachingAppProviderTests |
|||
{ |
|||
private readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); |
|||
private readonly IAppRepository repository = A.Fake<IAppRepository>(); |
|||
private readonly CachingAppProvider sut; |
|||
private readonly IAppEntity appV1 = A.Dummy<IAppEntity>(); |
|||
private readonly IAppEntity appV2 = A.Dummy<IAppEntity>(); |
|||
private readonly NamedId<Guid> appId = new NamedId<Guid>(Guid.NewGuid(), "my-app"); |
|||
|
|||
public CachingAppProviderTests() |
|||
{ |
|||
A.CallTo(() => appV1.Id).Returns(appId.Id); |
|||
A.CallTo(() => appV1.Name).Returns(appId.Name); |
|||
|
|||
A.CallTo(() => appV2.Id).Returns(appId.Id); |
|||
A.CallTo(() => appV2.Name).Returns(appId.Name); |
|||
|
|||
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] |
|||
public async Task Should_also_retrieve_app_by_name_if_retrieved_by_id_before() |
|||
{ |
|||
A.CallTo(() => repository.FindAppAsync(appId.Id)) |
|||
.Returns(appV1); |
|||
|
|||
await ProvideAppByIdAsync(appV1); |
|||
await ProvideAppByNameAsync(appV1); |
|||
|
|||
A.CallTo(() => repository.FindAppAsync(appId.Id)).MustHaveHappened(); |
|||
A.CallTo(() => repository.FindAppAsync(appId.Name)).MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_also_retrieve_app_by_id_if_retrieved_by_name_before() |
|||
{ |
|||
A.CallTo(() => repository.FindAppAsync(appId.Name)) |
|||
.Returns(appV1); |
|||
|
|||
await ProvideAppByNameAsync(appV1); |
|||
await ProvideAppByIdAsync(appV1); |
|||
|
|||
A.CallTo(() => repository.FindAppAsync(appId.Id)).MustNotHaveHappened(); |
|||
A.CallTo(() => repository.FindAppAsync(appId.Name)).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_clear_cache_for_id_after_update_event() |
|||
{ |
|||
A.CallTo(() => repository.FindAppAsync(appId.Id)) |
|||
.Returns(appV2); |
|||
A.CallTo(() => repository.FindAppAsync(appId.Id)) |
|||
.Returns(appV1).Once(); |
|||
|
|||
await ProvideAppByIdAsync(appV1); |
|||
|
|||
sut.On(Envelope.Create(new AppLanguageAdded { AppId = appId }).To<IEvent>()).Wait(); |
|||
|
|||
await ProvideAppByIdAsync(appV2); |
|||
|
|||
A.CallTo(() => repository.FindAppAsync(appId.Id)).MustHaveHappened(Repeated.Exactly.Times(2)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_clear_cache_for_name_after_update_event() |
|||
{ |
|||
A.CallTo(() => repository.FindAppAsync(appId.Name)) |
|||
.Returns(appV2); |
|||
A.CallTo(() => repository.FindAppAsync(appId.Name)) |
|||
.Returns(appV1).Once(); |
|||
|
|||
await ProvideAppByNameAsync(appV1); |
|||
|
|||
sut.On(Envelope.Create(new AppLanguageAdded { AppId = appId }).To<IEvent>()).Wait(); |
|||
|
|||
await ProvideAppByNameAsync(appV2); |
|||
|
|||
A.CallTo(() => repository.FindAppAsync(appId.Name)).MustHaveHappened(Repeated.Exactly.Times(2)); |
|||
} |
|||
|
|||
private async Task ProvideAppByIdAsync(IAppEntity app) |
|||
{ |
|||
Assert.Equal(app, await sut.FindAppByIdAsync(appId.Id)); |
|||
} |
|||
|
|||
private async Task ProvideAppByNameAsync(IAppEntity app) |
|||
{ |
|||
Assert.Equal(app, await sut.FindAppByNameAsync(appId.Name)); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue