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
|
// Squidex Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) Squidex Group
|
// Copyright (c) Squidex Group
|
||||
// All rights reserved.
|
// All rights reserved.
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
|
|
||||
using Orleans.Concurrency; |
using System.Collections.Generic; |
||||
using Squidex.Infrastructure.CQRS.Events; |
using Newtonsoft.Json; |
||||
|
|
||||
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains |
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations |
||||
{ |
{ |
||||
[Immutable] |
public sealed class AppUserGrainState |
||||
public sealed class EventMessage |
|
||||
{ |
{ |
||||
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
|
// Squidex Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) Squidex Group
|
// Copyright (c) Squidex Group
|
||||
// All rights reserved.
|
// All rights reserved.
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
|
|
||||
using Squidex.Infrastructure.CQRS.Events; |
namespace Squidex.Infrastructure.Json.Orleans |
||||
|
|
||||
namespace Squidex.Domain.Apps.Read.Apps |
|
||||
{ |
{ |
||||
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