mirror of https://github.com/Squidex/squidex.git
45 changed files with 836 additions and 489 deletions
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// MongoAppEntity.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
using Squidex.Domain.Apps.Entities.Apps.State; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Apps |
|||
{ |
|||
public sealed class MongoAppEntity |
|||
{ |
|||
[BsonId] |
|||
[BsonElement] |
|||
[BsonRepresentation(BsonType.String)] |
|||
public string Id { get; set; } |
|||
|
|||
[BsonElement] |
|||
[BsonRequired] |
|||
public AppState State { get; set; } |
|||
|
|||
[BsonElement] |
|||
[BsonRequired] |
|||
public int Version { get; set; } |
|||
|
|||
[BsonElement] |
|||
[BsonRequired] |
|||
public string[] UserIds { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// ==========================================================================
|
|||
// MongoAppRepository.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using Squidex.Domain.Apps.Entities.Apps.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Apps.State; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Apps |
|||
{ |
|||
public sealed class MongoAppRepository : MongoRepositoryBase<MongoAppEntity>, IAppRepository, ISnapshotStore<AppState> |
|||
{ |
|||
public MongoAppRepository(IMongoDatabase database) |
|||
: base(database) |
|||
{ |
|||
} |
|||
|
|||
protected override Task SetupCollectionAsync(IMongoCollection<MongoAppEntity> collection) |
|||
{ |
|||
return collection.Indexes.CreateOneAsync(Index.Ascending(x => x.UserIds)); |
|||
} |
|||
|
|||
public async Task<(AppState Value, long Version)> ReadAsync(string key) |
|||
{ |
|||
var existing = |
|||
await Collection.Find(x => x.Id == key) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
if (existing != null) |
|||
{ |
|||
return (existing.State, existing.Version); |
|||
} |
|||
|
|||
return (null, -1); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<string>> QueryUserAppNamesAsync(string userId) |
|||
{ |
|||
var appEntities = |
|||
await Collection.Find(x => x.UserIds.Contains(userId)).Project<MongoAppEntity>(Projection.Include(x => x.Id)).ToListAsync(); |
|||
|
|||
return appEntities.Select(x => x.Id).ToList(); |
|||
} |
|||
|
|||
public async Task WriteAsync(string key, AppState value, long oldVersion, long newVersion) |
|||
{ |
|||
try |
|||
{ |
|||
await Collection.UpdateOneAsync(x => x.Id == key && x.Version == oldVersion, |
|||
Update |
|||
.Set(x => x.UserIds, value.Contributors.Keys.ToArray()) |
|||
.Set(x => x.State, value) |
|||
.Set(x => x.Version, newVersion), |
|||
Upsert); |
|||
} |
|||
catch (MongoWriteException ex) |
|||
{ |
|||
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey) |
|||
{ |
|||
var existingVersion = |
|||
await Collection.Find(x => x.Id == key) |
|||
.Project<MongoAppEntity>(Projection.Exclude(x => x.Id)).FirstOrDefaultAsync(); |
|||
|
|||
if (existingVersion != null) |
|||
{ |
|||
throw new InconsistentStateException(existingVersion.Version, oldVersion, ex); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// ==========================================================================
|
|||
// MongoAssetEntity.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
using Squidex.Domain.Apps.Entities.Assets.State; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets |
|||
{ |
|||
public sealed class MongoAssetEntity |
|||
{ |
|||
[BsonId] |
|||
[BsonElement] |
|||
[BsonRepresentation(BsonType.String)] |
|||
public string Id { get; set; } |
|||
|
|||
[BsonElement] |
|||
[BsonRequired] |
|||
public AssetState State { get; set; } |
|||
|
|||
[BsonElement] |
|||
[BsonRequired] |
|||
public int Version { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,140 @@ |
|||
// ==========================================================================
|
|||
// MongoAssetRepository.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.Bson; |
|||
using MongoDB.Driver; |
|||
using Squidex.Domain.Apps.Entities.Assets; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Assets.State; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets |
|||
{ |
|||
public sealed class MongoAssetRepository : MongoRepositoryBase<MongoAssetEntity>, IAssetRepository, ISnapshotStore<AssetState> |
|||
{ |
|||
public MongoAssetRepository(IMongoDatabase database) |
|||
: base(database) |
|||
{ |
|||
} |
|||
|
|||
protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetEntity> collection) |
|||
{ |
|||
return collection.Indexes.CreateOneAsync( |
|||
Index |
|||
.Ascending(x => x.State.AppId) |
|||
.Ascending(x => x.State.FileName) |
|||
.Ascending(x => x.State.MimeType) |
|||
.Descending(x => x.State.LastModified)); |
|||
} |
|||
|
|||
public async Task<(AssetState Value, long Version)> ReadAsync(string key) |
|||
{ |
|||
var existing = |
|||
await Collection.Find(x => x.Id == key) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
if (existing != null) |
|||
{ |
|||
return (existing.State, existing.Version); |
|||
} |
|||
|
|||
return (null, -1); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0) |
|||
{ |
|||
var filter = CreateFilter(appId, mimeTypes, ids, query); |
|||
|
|||
var assetEntities = |
|||
await Collection.Find(filter).Skip(skip).Limit(take).SortByDescending(x => x.State.LastModified) |
|||
.ToListAsync(); |
|||
|
|||
return assetEntities.OfType<IAssetEntity>().ToList(); |
|||
} |
|||
|
|||
public async Task<long> CountAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null) |
|||
{ |
|||
var filter = CreateFilter(appId, mimeTypes, ids, query); |
|||
|
|||
var assetsCount = |
|||
await Collection.Find(filter) |
|||
.CountAsync(); |
|||
|
|||
return assetsCount; |
|||
} |
|||
|
|||
public async Task<IAssetEntity> FindAssetAsync(Guid id) |
|||
{ |
|||
var (state, etag) = await ReadAsync(id.ToString()); |
|||
|
|||
return state; |
|||
} |
|||
|
|||
private static FilterDefinition<MongoAssetEntity> CreateFilter(Guid appId, ICollection<string> mimeTypes, ICollection<Guid> ids, string query) |
|||
{ |
|||
var filters = new List<FilterDefinition<MongoAssetEntity>> |
|||
{ |
|||
Filter.Eq(x => x.State.AppId, appId) |
|||
}; |
|||
|
|||
if (ids != null && ids.Count > 0) |
|||
{ |
|||
filters.Add(Filter.In(x => x.Id, ids.Select(x => x.ToString()))); |
|||
} |
|||
|
|||
if (mimeTypes != null && mimeTypes.Count > 0) |
|||
{ |
|||
filters.Add(Filter.In(x => x.State.MimeType, mimeTypes)); |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(query)) |
|||
{ |
|||
filters.Add(Filter.Regex(x => x.State.FileName, new BsonRegularExpression(query, "i"))); |
|||
} |
|||
|
|||
var filter = Filter.And(filters); |
|||
|
|||
return filter; |
|||
} |
|||
|
|||
public async Task WriteAsync(string key, AssetState value, long oldVersion, long newVersion) |
|||
{ |
|||
try |
|||
{ |
|||
await Collection.UpdateOneAsync(x => x.Id == key && x.Version == oldVersion, |
|||
Update |
|||
.Set(x => x.State, value) |
|||
.Set(x => x.Version, newVersion), |
|||
Upsert); |
|||
} |
|||
catch (MongoWriteException ex) |
|||
{ |
|||
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey) |
|||
{ |
|||
var existingVersion = |
|||
await Collection.Find(x => x.Id == key) |
|||
.Project<MongoAssetEntity>(Projection.Exclude(x => x.Id)).FirstOrDefaultAsync(); |
|||
|
|||
if (existingVersion != null) |
|||
{ |
|||
throw new InconsistentStateException(existingVersion.Version, oldVersion, ex); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// ==========================================================================
|
|||
// MongoRuleEntity.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using MongoDB.Bson; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
using Squidex.Domain.Apps.Entities.Rules.State; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules |
|||
{ |
|||
public sealed class MongoRuleEntity |
|||
{ |
|||
[BsonId] |
|||
[BsonElement] |
|||
[BsonRepresentation(BsonType.String)] |
|||
public string Id { get; set; } |
|||
|
|||
[BsonElement] |
|||
[BsonRequired] |
|||
public RuleState State { get; set; } |
|||
|
|||
[BsonElement] |
|||
[BsonRequired] |
|||
public int Version { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// ==========================================================================
|
|||
// MongoRuleRepository.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.Entities.Rules.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Rules.State; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules |
|||
{ |
|||
public sealed class MongoRuleRepository : MongoRepositoryBase<MongoRuleEntity>, IRuleRepository, ISnapshotStore<RuleState> |
|||
{ |
|||
public MongoRuleRepository(IMongoDatabase database) |
|||
: base(database) |
|||
{ |
|||
} |
|||
|
|||
protected override Task SetupCollectionAsync(IMongoCollection<MongoRuleEntity> collection) |
|||
{ |
|||
return collection.Indexes.CreateOneAsync(Index.Ascending(x => x.State.AppId)); |
|||
} |
|||
|
|||
public async Task<(RuleState Value, long Version)> ReadAsync(string key) |
|||
{ |
|||
var existing = |
|||
await Collection.Find(x => x.Id == key) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
if (existing != null) |
|||
{ |
|||
return (existing.State, existing.Version); |
|||
} |
|||
|
|||
return (null, -1); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<string>> QueryRuleIdsAsync(Guid appId) |
|||
{ |
|||
var ruleEntities = |
|||
await Collection.Find(x => x.State.AppId == appId).Project<MongoRuleEntity>(Projection.Include(x => x.Id)).ToListAsync(); |
|||
|
|||
return ruleEntities.Select(x => x.Id).ToList(); |
|||
} |
|||
|
|||
public async Task WriteAsync(string key, RuleState value, long oldVersion, long newVersion) |
|||
{ |
|||
try |
|||
{ |
|||
await Collection.UpdateOneAsync(x => x.Id == key && x.Version == oldVersion, |
|||
Update |
|||
.Set(x => x.State, value) |
|||
.Set(x => x.Version, newVersion), |
|||
Upsert); |
|||
} |
|||
catch (MongoWriteException ex) |
|||
{ |
|||
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey) |
|||
{ |
|||
var existingVersion = |
|||
await Collection.Find(x => x.Id == key) |
|||
.Project<MongoRuleEntity>(Projection.Exclude(x => x.Id)).FirstOrDefaultAsync(); |
|||
|
|||
if (existingVersion != null) |
|||
{ |
|||
throw new InconsistentStateException(existingVersion.Version, oldVersion, ex); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
// ==========================================================================
|
|||
// AppProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Domain.Apps.Entities.Rules; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities |
|||
{ |
|||
public sealed class AppProvider : IAppProvider |
|||
{ |
|||
private readonly IStateFactory factory; |
|||
|
|||
public AppProvider(IStateFactory factory) |
|||
{ |
|||
Guard.NotNull(factory, nameof(factory)); |
|||
|
|||
this.factory = factory; |
|||
} |
|||
|
|||
public Task<IAppEntity> GetAppAsync(string appName) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
public Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(string appName, Guid id) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
public Task<List<IRuleEntity>> GetRulesAsync(string appName) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
public Task<ISchemaEntity> GetSchemaAsync(string appName, Guid id, bool provideDeleted = false) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
public Task<ISchemaEntity> GetSchemaAsync(string appName, string name, bool provideDeleted = false) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
public Task<List<ISchemaEntity>> GetSchemasAsync(string appName) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
public Task<List<IAppEntity>> GetUserApps(string userId) |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// ==========================================================================
|
|||
// IAppRepository.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.Repositories |
|||
{ |
|||
public interface IAppRepository |
|||
{ |
|||
Task<IReadOnlyList<string>> QueryUserAppNamesAsync(string userId); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// ==========================================================================
|
|||
// ContentState.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.State |
|||
{ |
|||
public sealed class ContentState : DomainObjectState<ContentState> |
|||
{ |
|||
[JsonProperty] |
|||
public IdContentData Data { get; set; } |
|||
|
|||
[JsonProperty] |
|||
public string Status { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// ==========================================================================
|
|||
// IRuleRepository.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.Entities.Rules.Repositories |
|||
{ |
|||
public interface IRuleRepository |
|||
{ |
|||
Task<IReadOnlyList<string>> QueryRuleIdsAsync(Guid appId); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// ==========================================================================
|
|||
// 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.Entities.Schemas.Repositories |
|||
{ |
|||
public interface ISchemaRepository |
|||
{ |
|||
Task<string> FindSchemaNameAsync(Guid schemaId); |
|||
|
|||
Task<IReadOnlyList<string>> QuerySchemaNamesAsync(Guid appId); |
|||
} |
|||
} |
|||
@ -1,75 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoAssetEntity.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Domain.Apps.Read.Assets; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Assets |
|||
{ |
|||
public sealed class MongoAssetEntity : |
|||
MongoEntity, |
|||
IAssetEntity, |
|||
IUpdateableEntityWithVersion, |
|||
IUpdateableEntityWithCreatedBy, |
|||
IUpdateableEntityWithLastModifiedBy, |
|||
IUpdateableEntityWithAppRef |
|||
{ |
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public string MimeType { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public string FileName { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public long FileSize { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public long FileVersion { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public bool IsImage { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public long Version { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public int? PixelWidth { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public int? PixelHeight { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public Guid AppId { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public RefToken CreatedBy { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement] |
|||
public RefToken LastModifiedBy { get; set; } |
|||
|
|||
Guid IAssetInfo.AssetId |
|||
{ |
|||
get { return Id; } |
|||
} |
|||
} |
|||
} |
|||
@ -1,100 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoAssetRepository.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.Bson; |
|||
using MongoDB.Driver; |
|||
using Squidex.Domain.Apps.Read.Assets; |
|||
using Squidex.Domain.Apps.Read.Assets.Repositories; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Assets |
|||
{ |
|||
public partial class MongoAssetRepository : MongoRepositoryBase<MongoAssetEntity>, IAssetRepository, IAssetEventConsumer |
|||
{ |
|||
public MongoAssetRepository(IMongoDatabase database) |
|||
: base(database) |
|||
{ |
|||
} |
|||
|
|||
protected override string CollectionName() |
|||
{ |
|||
return "Projections_Assets"; |
|||
} |
|||
|
|||
protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetEntity> collection) |
|||
{ |
|||
return collection.Indexes.CreateOneAsync( |
|||
Index.Ascending(x => x.AppId) |
|||
.Ascending(x => x.FileName) |
|||
.Ascending(x => x.MimeType) |
|||
.Descending(x => x.LastModified)); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0) |
|||
{ |
|||
var filter = CreateFilter(appId, mimeTypes, ids, query); |
|||
|
|||
var assetEntities = |
|||
await Collection.Find(filter).Skip(skip).Limit(take).SortByDescending(x => x.LastModified) |
|||
.ToListAsync(); |
|||
|
|||
return assetEntities.OfType<IAssetEntity>().ToList(); |
|||
} |
|||
|
|||
public async Task<long> CountAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null) |
|||
{ |
|||
var filter = CreateFilter(appId, mimeTypes, ids, query); |
|||
|
|||
var assetsCount = |
|||
await Collection.Find(filter) |
|||
.CountAsync(); |
|||
|
|||
return assetsCount; |
|||
} |
|||
|
|||
public async Task<IAssetEntity> FindAssetAsync(Guid id) |
|||
{ |
|||
var assetEntity = |
|||
await Collection.Find(s => s.Id == id) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
return assetEntity; |
|||
} |
|||
|
|||
private static FilterDefinition<MongoAssetEntity> CreateFilter(Guid appId, ICollection<string> mimeTypes, ICollection<Guid> ids, string query) |
|||
{ |
|||
var filters = new List<FilterDefinition<MongoAssetEntity>> |
|||
{ |
|||
Filter.Eq(x => x.AppId, appId) |
|||
}; |
|||
|
|||
if (ids != null && ids.Count > 0) |
|||
{ |
|||
filters.Add(Filter.In(x => x.Id, ids)); |
|||
} |
|||
|
|||
if (mimeTypes != null && mimeTypes.Count > 0) |
|||
{ |
|||
filters.Add(Filter.In(x => x.MimeType, mimeTypes)); |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(query)) |
|||
{ |
|||
filters.Add(Filter.Regex(x => x.FileName, new BsonRegularExpression(query, "i"))); |
|||
} |
|||
|
|||
var filter = Filter.And(filters); |
|||
|
|||
return filter; |
|||
} |
|||
} |
|||
} |
|||
@ -1,64 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoAssetRepository_EventHandling.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using Squidex.Domain.Apps.Events.Assets; |
|||
using Squidex.Infrastructure.Dispatching; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Assets |
|||
{ |
|||
public partial class MongoAssetRepository |
|||
{ |
|||
public string Name |
|||
{ |
|||
get { return GetType().Name; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return "^asset-"; } |
|||
} |
|||
|
|||
public Task On(Envelope<IEvent> @event) |
|||
{ |
|||
return this.DispatchActionAsync(@event.Payload, @event.Headers); |
|||
} |
|||
|
|||
protected Task On(AssetCreated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.CreateAsync(@event, headers, a => |
|||
{ |
|||
SimpleMapper.Map(@event, a); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AssetUpdated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.UpdateAsync(@event, headers, a => |
|||
{ |
|||
SimpleMapper.Map(@event, a); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AssetRenamed @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.UpdateAsync(@event, headers, a => |
|||
{ |
|||
SimpleMapper.Map(@event, a); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AssetDeleted @event, EnvelopeHeaders headers) |
|||
{ |
|||
return Collection.DeleteOneAsync(x => x.Id == @event.AssetId); |
|||
} |
|||
} |
|||
} |
|||
@ -1,160 +0,0 @@ |
|||
// ==========================================================================
|
|||
// MongoContentRepository_EventHandling.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using Squidex.Domain.Apps.Core.ConvertContent; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Domain.Apps.Events.Assets; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure.Dispatching; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Domain.Apps.Read.MongoDb.Contents |
|||
{ |
|||
public partial class MongoContentRepository |
|||
{ |
|||
public string Name |
|||
{ |
|||
get { return GetType().Name; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return "^(content-)|(app-)|(asset-)"; } |
|||
} |
|||
|
|||
public async Task ClearAsync() |
|||
{ |
|||
using (var collections = await database.ListCollectionsAsync()) |
|||
{ |
|||
while (await collections.MoveNextAsync()) |
|||
{ |
|||
foreach (var collection in collections.Current) |
|||
{ |
|||
var name = collection["name"].ToString(); |
|||
|
|||
if (name.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
await database.DropCollectionAsync(name); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public Task On(Envelope<IEvent> @event) |
|||
{ |
|||
return this.DispatchActionAsync(@event.Payload, @event.Headers); |
|||
} |
|||
|
|||
protected Task On(AppCreated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return ForAppIdAsync(@event.AppId.Id, async collection => |
|||
{ |
|||
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.SchemaId).Descending(x => x.LastModified)); |
|||
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.ReferencedIds)); |
|||
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Status)); |
|||
await collection.Indexes.CreateOneAsync(Index.Text(x => x.DataText)); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(ContentCreated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return ForSchemaAsync(@event.AppId, @event.SchemaId.Id, (collection, schema) => |
|||
{ |
|||
return collection.CreateAsync(@event, headers, content => |
|||
{ |
|||
content.SchemaId = @event.SchemaId.Id; |
|||
|
|||
SimpleMapper.Map(@event, content); |
|||
|
|||
var idData = @event.Data?.ToIdModel(schema.SchemaDef, true); |
|||
|
|||
content.DataText = idData?.ToFullText(); |
|||
content.IdData = idData; |
|||
content.ReferencedIds = idData?.ToReferencedIds(schema.SchemaDef); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(ContentUpdated @event, EnvelopeHeaders headers) |
|||
{ |
|||
return ForSchemaAsync(@event.AppId, @event.SchemaId.Id, (collection, schema) => |
|||
{ |
|||
var idData = @event.Data?.ToIdModel(schema.SchemaDef, true); |
|||
|
|||
return collection.UpdateOneAsync( |
|||
Filter.Eq(x => x.Id, @event.ContentId), |
|||
Update |
|||
.Set(x => x.DataText, idData.ToFullText()) |
|||
.Set(x => x.IdData, idData) |
|||
.Set(x => x.ReferencedIds, idData.ToReferencedIds(schema.SchemaDef)) |
|||
.Set(x => x.LastModified, headers.Timestamp()) |
|||
.Set(x => x.LastModifiedBy, @event.Actor) |
|||
.Set(x => x.Version, headers.EventStreamNumber())); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(ContentStatusChanged @event, EnvelopeHeaders headers) |
|||
{ |
|||
return ForAppIdAsync(@event.AppId.Id, collection => |
|||
{ |
|||
return collection.UpdateOneAsync( |
|||
Filter.Eq(x => x.Id, @event.ContentId), |
|||
Update |
|||
.Set(x => x.Status, @event.Status) |
|||
.Set(x => x.LastModified, headers.Timestamp()) |
|||
.Set(x => x.LastModifiedBy, @event.Actor) |
|||
.Set(x => x.Version, headers.EventStreamNumber())); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AssetDeleted @event, EnvelopeHeaders headers) |
|||
{ |
|||
return ForAppIdAsync(@event.AppId.Id, collection => |
|||
{ |
|||
return collection.UpdateManyAsync( |
|||
Filter.And( |
|||
Filter.AnyEq(x => x.ReferencedIds, @event.AssetId), |
|||
Filter.AnyNe(x => x.ReferencedIdsDeleted, @event.AssetId)), |
|||
Update.AddToSet(x => x.ReferencedIdsDeleted, @event.AssetId)); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(ContentDeleted @event, EnvelopeHeaders headers) |
|||
{ |
|||
return ForAppIdAsync(@event.AppId.Id, async collection => |
|||
{ |
|||
await collection.UpdateManyAsync( |
|||
Filter.And( |
|||
Filter.AnyEq(x => x.ReferencedIds, @event.ContentId), |
|||
Filter.AnyNe(x => x.ReferencedIdsDeleted, @event.ContentId)), |
|||
Update.AddToSet(x => x.ReferencedIdsDeleted, @event.ContentId)); |
|||
|
|||
await collection.DeleteOneAsync(x => x.Id == @event.ContentId); |
|||
}); |
|||
} |
|||
|
|||
private Task ForAppIdAsync(Guid appId, Func<IMongoCollection<MongoContentEntity>, Task> action) |
|||
{ |
|||
var collection = GetCollection(appId); |
|||
|
|||
return action(collection); |
|||
} |
|||
|
|||
private IMongoCollection<MongoContentEntity> GetCollection(Guid appId) |
|||
{ |
|||
var name = $"{Prefix}{appId}"; |
|||
|
|||
return database.GetCollection<MongoContentEntity>(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
// ==========================================================================
|
|||
// AsyncLock.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
|
|||
|
|||
namespace Squidex.Infrastructure.Tasks |
|||
{ |
|||
public sealed class AsyncLock |
|||
{ |
|||
private readonly SemaphoreSlim semaphore; |
|||
|
|||
public AsyncLock() |
|||
{ |
|||
semaphore = new SemaphoreSlim(1); |
|||
} |
|||
|
|||
public Task<IDisposable> LockAsync() |
|||
{ |
|||
Task wait = semaphore.WaitAsync(); |
|||
|
|||
if (wait.IsCompleted) |
|||
{ |
|||
return Task.FromResult((IDisposable)new LockReleaser(this)); |
|||
} |
|||
else |
|||
{ |
|||
return wait.ContinueWith(x => (IDisposable)new LockReleaser(this), |
|||
CancellationToken.None, |
|||
TaskContinuationOptions.ExecuteSynchronously, |
|||
TaskScheduler.Default); |
|||
} |
|||
} |
|||
|
|||
private class LockReleaser : IDisposable |
|||
{ |
|||
private AsyncLock target; |
|||
|
|||
internal LockReleaser(AsyncLock target) |
|||
{ |
|||
this.target = target; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
AsyncLock current = target; |
|||
|
|||
if (current == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
target = null; |
|||
|
|||
try |
|||
{ |
|||
current.semaphore.Release(); |
|||
} |
|||
catch |
|||
{ |
|||
// just ignore the Exception
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
// ==========================================================================
|
|||
// AsyncLockPool.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Tasks |
|||
{ |
|||
public sealed class AsyncLockPool |
|||
{ |
|||
private readonly AsyncLock[] locks; |
|||
|
|||
public AsyncLockPool(int poolSize) |
|||
{ |
|||
Guard.GreaterThan(poolSize, 0, nameof(poolSize)); |
|||
|
|||
locks = new AsyncLock[poolSize]; |
|||
|
|||
for (var i = 0; i < poolSize; i++) |
|||
{ |
|||
locks[i] = new AsyncLock(); |
|||
} |
|||
} |
|||
|
|||
public Task<IDisposable> LockAsync(object target) |
|||
{ |
|||
Guard.NotNull(target, nameof(target)); |
|||
|
|||
return locks[Math.Abs(target.GetHashCode() % locks.Length)].LockAsync(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue