Browse Source

t

pull/1/head
Sebastian Stehle 9 years ago
parent
commit
94a831dec0
  1. 12
      src/PinkParrot/Configurations/InfrastructureModule.cs
  2. 4
      src/PinkParrot/Configurations/ReadModule.cs
  3. 2
      src/PinkParrot/Configurations/WriteModule.cs
  4. 23
      src/PinkParrot/Modules/Api/BaseController.cs
  5. 20
      src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs
  6. 28
      src/PinkParrot/Modules/Api/Schemas/SchemaListModel.cs
  7. 34
      src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
  8. 6
      src/PinkParrot/Startup.cs
  9. 2
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeHeaders.cs
  10. 5
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/EventStoreFormatter.cs
  11. 35
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/MongoDb/MongoExtensions.cs
  12. 6
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/MongoDb/MongoRepositoryBase.cs
  13. 16
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/IPropertyAccessor.cs
  14. 82
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/PropertiesTypeAccessor.cs
  15. 77
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/PropertyAccessor.cs
  16. 175
      src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/SimpleMapper.cs
  17. 14
      src/pinkparrot_read/PinkParrot.Read/IEntity.cs
  18. 11
      src/pinkparrot_read/PinkParrot.Read/IModelSchemaRM.cs
  19. 21
      src/pinkparrot_read/PinkParrot.Read/Models/IEntity.cs
  20. 17
      src/pinkparrot_read/PinkParrot.Read/Models/ITenantEntity.cs
  21. 25
      src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaListRM.cs
  22. 3
      src/pinkparrot_read/PinkParrot.Read/Repositories/IModelSchemaRepository.cs
  23. 70
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/EntityMapper.cs
  24. 37
      src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/MongoModelSchemaListRepository.cs
  25. 9
      src/pinkparrot_read/PinkParrot.Read/Services/Implementations/MongoStreamPositionsStorage.cs

12
src/PinkParrot/Configurations/InfrastructureDependencies.cs → src/PinkParrot/Configurations/InfrastructureModule.cs

@ -11,6 +11,8 @@ using Autofac;
using EventStore.ClientAPI;
using EventStore.ClientAPI.SystemData;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using MongoDB.Driver;
using PinkParrot.Infrastructure.CQRS.Autofac;
using PinkParrot.Infrastructure.CQRS.Commands;
@ -19,7 +21,7 @@ using PinkParrot.Read.Services.Implementations;
namespace PinkParrot.Configurations
{
public class InfrastructureDependencies : Module
public class InfrastructureModule : Module
{
protected override void Load(ContainerBuilder builder)
{
@ -41,6 +43,14 @@ namespace PinkParrot.Configurations
.AsSelf()
.SingleInstance();
builder.RegisterType<HttpContextAccessor>()
.As<IHttpContextAccessor>()
.SingleInstance();
builder.RegisterType<ActionContextAccessor>()
.As<IActionContextAccessor>()
.SingleInstance();
builder.RegisterInstance(mongoDatabase)
.As<IMongoDatabase>()
.SingleInstance();

4
src/PinkParrot/Configurations/ReadDependencies.cs → src/PinkParrot/Configurations/ReadModule.cs

@ -15,7 +15,7 @@ using PinkParrot.Read.Services.Implementations;
namespace PinkParrot.Configurations
{
public sealed class ReadDependencies : Module
public sealed class ReadModule : Module
{
protected override void Load(ContainerBuilder builder)
{
@ -27,7 +27,7 @@ namespace PinkParrot.Configurations
.As<IModelSchemaProvider>()
.SingleInstance();
builder.RegisterType<MongoModelSchemaRepository>()
builder.RegisterType<MongoModelSchemaListRepository>()
.As<IModelSchemaRepository>()
.As<ICatchEventConsumer>()
.SingleInstance();

2
src/PinkParrot/Configurations/WriteDependencies.cs → src/PinkParrot/Configurations/WriteModule.cs

@ -13,7 +13,7 @@ using PinkParrot.Write.Schema;
namespace PinkParrot.Configurations
{
public class WriteDependencies : Module
public class WriteModule : Module
{
protected override void Load(ContainerBuilder builder)
{

23
src/PinkParrot/Modules/Api/BaseController.cs

@ -0,0 +1,23 @@
// ==========================================================================
// BaseController.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using Microsoft.AspNetCore.Mvc;
using PinkParrot.Infrastructure.CQRS.Commands;
namespace PinkParrot.Modules.Api
{
public abstract class BaseController : Controller
{
public ICommandBus CommandBus { get; }
protected BaseController(ICommandBus commandBus)
{
CommandBus = commandBus;
}
}
}

20
src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs

@ -17,13 +17,11 @@ using Swashbuckle.SwaggerGen.Annotations;
namespace PinkParrot.Modules.Api.Schemas
{
public class SchemasFieldsController : Controller
public class SchemasFieldsController : BaseController
{
private readonly ICommandBus commandBus;
public SchemasFieldsController(ICommandBus commandBus)
: base(commandBus)
{
this.commandBus = commandBus;
}
/// <summary>
@ -38,7 +36,7 @@ namespace PinkParrot.Modules.Api.Schemas
{
var command = new AddModelField { Properties = field };
return commandBus.PublishAsync(command);
return CommandBus.PublishAsync(command);
}
/// <summary>
@ -52,7 +50,7 @@ namespace PinkParrot.Modules.Api.Schemas
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Update(string name, long fieldId, [FromBody] UpdateModelField command)
{
return commandBus.PublishAsync(command);
return CommandBus.PublishAsync(command);
}
/// <summary>
@ -65,7 +63,7 @@ namespace PinkParrot.Modules.Api.Schemas
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Hide(string name, long fieldId, HideModelField command)
{
return commandBus.PublishAsync(command);
return CommandBus.PublishAsync(command);
}
/// <summary>
@ -78,7 +76,7 @@ namespace PinkParrot.Modules.Api.Schemas
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Show(string name, long fieldId, ShowModelField command)
{
return commandBus.PublishAsync(command);
return CommandBus.PublishAsync(command);
}
/// <summary>
@ -91,7 +89,7 @@ namespace PinkParrot.Modules.Api.Schemas
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Enable(string name, long fieldId, EnableModelField command)
{
return commandBus.PublishAsync(command);
return CommandBus.PublishAsync(command);
}
/// <summary>
@ -104,7 +102,7 @@ namespace PinkParrot.Modules.Api.Schemas
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Disable(string name, long fieldId, DisableModelField command)
{
return commandBus.PublishAsync(command);
return CommandBus.PublishAsync(command);
}
@ -118,7 +116,7 @@ namespace PinkParrot.Modules.Api.Schemas
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task Delete(string name, long fieldId, DeleteModelField command)
{
return commandBus.PublishAsync(command);
return CommandBus.PublishAsync(command);
}
}
}

28
src/PinkParrot/Modules/Api/Schemas/SchemaListModel.cs

@ -0,0 +1,28 @@
// ==========================================================================
// SchemaListModel.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
namespace PinkParrot.Modules.Api.Schemas
{
public class SchemaListModel
{
[Required]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public DateTime Created { get; set; }
[Required]
public DateTime LastModified { get; set; }
}
}

34
src/PinkParrot/Modules/Api/Schemas/SchemasController.cs

@ -8,11 +8,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PinkParrot.Core.Schema;
using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Read.Models;
using PinkParrot.Infrastructure.Reflection;
using PinkParrot.Read.Repositories;
using PinkParrot.Read.Services;
using PinkParrot.Write.Schema.Commands;
using Swashbuckle.SwaggerGen.Annotations;
@ -20,13 +23,17 @@ using Swashbuckle.SwaggerGen.Annotations;
namespace PinkParrot.Modules.Api.Schemas
{
public class SchemasController : Controller
public class SchemasController : BaseController
{
private readonly ICommandBus commandBus;
private readonly IModelSchemaRepository modelSchemaRepository;
private readonly ITenantProvider tenantProvider;
public SchemasController(ICommandBus commandBus)
public SchemasController(ICommandBus commandBus, ITenantProvider tenantProvider, IModelSchemaRepository modelSchemaRepository)
: base(commandBus)
{
this.commandBus = commandBus;
this.modelSchemaRepository = modelSchemaRepository;
this.tenantProvider = tenantProvider;
}
/// <summary>
@ -35,9 +42,12 @@ namespace PinkParrot.Modules.Api.Schemas
[HttpGet]
[Route("schemas/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
public Task<List<ModelSchemaRM>> Query()
public async Task<List<SchemaListModel>> Query()
{
return null;
var tenantId = await tenantProvider.ProvideTenantIdByDomainAsync(Request.Host.ToString());
var schemes = await modelSchemaRepository.QueryAllAsync(tenantId);
return schemes.Select(s => SimpleMapper.Map(s, new SchemaListModel())).ToList();
}
/// <summary>
@ -52,12 +62,12 @@ namespace PinkParrot.Modules.Api.Schemas
[HttpPost]
[Route("schemas/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(EntityCreated), 204)]
[ProducesResponseType(typeof(EntityCreated), 201)]
public async Task<ActionResult> Create([FromBody] ModelSchemaProperties schema)
{
var command = new CreateModelSchema { AggregateId = Guid.NewGuid(), Properties = schema };
await commandBus.PublishAsync(command);
await CommandBus.PublishAsync(command);
return CreatedAtAction("Query", new EntityCreated { Id = command.AggregateId });
}
@ -73,11 +83,12 @@ namespace PinkParrot.Modules.Api.Schemas
[HttpPut]
[Route("schemas/{name}/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(void), 204)]
public async Task<ActionResult> Update(string name, [FromBody] ModelSchemaProperties schema)
{
var command = new UpdateModelSchema { Properties = schema };
await commandBus.PublishAsync(command);
await CommandBus.PublishAsync(command);
return NoContent();
}
@ -92,9 +103,10 @@ namespace PinkParrot.Modules.Api.Schemas
[HttpDelete]
[Route("schemas/{name}/")]
[SwaggerOperation(Tags = new[] { "Schemas" })]
[ProducesResponseType(typeof(void), 204)]
public async Task<ActionResult> Delete(string name)
{
await commandBus.PublishAsync(new DeleteModelSchema());
await CommandBus.PublishAsync(new DeleteModelSchema());
return NoContent();
}

6
src/PinkParrot/Startup.cs

@ -30,9 +30,9 @@ namespace PinkParrot
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule<WriteDependencies>();
builder.RegisterModule<InfrastructureDependencies>();
builder.RegisterModule<ReadDependencies>();
builder.RegisterModule<InfrastructureModule>();
builder.RegisterModule<ReadModule>();
builder.RegisterModule<WriteModule>();
return new AutofacServiceProvider(builder.Build());
}

2
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeHeaders.cs

@ -22,7 +22,7 @@ namespace PinkParrot.Infrastructure.CQRS
foreach (var property in bag.Properties)
{
Set(property.Key, property.Value);
Set(property.Key, property.Value.RawValue);
}
}

5
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EventStore/EventStoreFormatter.cs

@ -10,6 +10,7 @@ using System;
using System.Text;
using EventStore.ClientAPI;
using Newtonsoft.Json;
using NodaTime;
using PinkParrot.Infrastructure.CQRS.Events;
// ReSharper disable InconsistentNaming
@ -27,14 +28,14 @@ namespace PinkParrot.Infrastructure.CQRS.EventStore
public Envelope<IEvent> Parse(ResolvedEvent @event)
{
var headers = ReadJson<EnvelopeHeaders>(@event.Event.Metadata);
var headers = ReadJson<PropertiesBag>(@event.Event.Metadata);
var eventType = Type.GetType(headers.Properties[CommonHeaders.EventType].ToString());
var eventData = ReadJson<IEvent>(@event.Event.Data, eventType);
var envelope = new Envelope<IEvent>(eventData, headers);
envelope.Headers.Set(CommonHeaders.Timestamp, DateTime.SpecifyKind(@event.Event.Created, DateTimeKind.Utc));
envelope.Headers.Set(CommonHeaders.Timestamp, Instant.FromDateTimeUtc(DateTime.SpecifyKind(@event.Event.Created, DateTimeKind.Utc)));
envelope.Headers.Set(CommonHeaders.EventNumber, @event.OriginalEventNumber);
return envelope;

35
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/MongoDb/MongoExtensions.cs

@ -0,0 +1,35 @@
// ==========================================================================
// MongoExtensions.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using MongoDB.Driver;
namespace PinkParrot.Infrastructure.MongoDb
{
public static class MongoExtensions
{
public static async Task<bool> InsertOneIfExistsAsync<T>(this IMongoCollection<T> collection, T document)
{
try
{
await collection.InsertOneAsync(document);
}
catch (MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
return false;
}
throw;
}
return true;
}
}
}

6
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/MongoDb/BaseMongoDbRepository.cs → src/pinkparrot_infrastructure/PinkParrot.Infrastructure/MongoDb/MongoRepositoryBase.cs

@ -1,5 +1,5 @@
// ==========================================================================
// BaseMongoDbRepository.cs
// MongoRepositoryBase.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
@ -12,7 +12,7 @@ using MongoDB.Driver;
namespace PinkParrot.Infrastructure.MongoDb
{
public abstract class BaseMongoDbRepository<TEntity>
public abstract class MongoRepositoryBase<TEntity>
{
private const string CollectionFormat = "{0}Set";
private readonly IMongoCollection<TEntity> mongoCollection;
@ -83,7 +83,7 @@ namespace PinkParrot.Infrastructure.MongoDb
}
}
protected BaseMongoDbRepository(IMongoDatabase database)
protected MongoRepositoryBase(IMongoDatabase database)
{
Guard.NotNull(database, nameof(database));

16
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/IPropertyAccessor.cs

@ -0,0 +1,16 @@
// ==========================================================================
// IPropertyAccessor.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
namespace PinkParrot.Infrastructure.Reflection
{
public interface IPropertyAccessor
{
object Get(object target);
void Set(object target, object value);
}
}

82
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/PropertiesTypeAccessor.cs

@ -0,0 +1,82 @@
// ==========================================================================
// PropertiesTypeAccessor.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace PinkParrot.Infrastructure.Reflection
{
public sealed class PropertiesTypeAccessor
{
private static readonly ConcurrentDictionary<Type, PropertiesTypeAccessor> AccessorCache = new ConcurrentDictionary<Type, PropertiesTypeAccessor>();
private readonly Dictionary<string, IPropertyAccessor> accessors = new Dictionary<string, IPropertyAccessor>();
private readonly List<PropertyInfo> properties = new List<PropertyInfo>();
public IEnumerable<PropertyInfo> Properties
{
get
{
return properties;
}
}
private PropertiesTypeAccessor(Type type)
{
var allProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in allProperties.Where(property => property.CanRead && property.CanWrite))
{
accessors[property.Name] = new PropertyAccessor(type, property);
properties.Add(property);
}
}
public static PropertiesTypeAccessor Create(Type targetType)
{
Guard.NotNull(targetType, nameof(targetType));
return AccessorCache.GetOrAdd(targetType, x => new PropertiesTypeAccessor(x));
}
public void SetValue(object target, string propertyName, object value)
{
Guard.NotNull(target, "target");
var accessor = FindAccessor(propertyName);
accessor.Set(target, value);
}
public object GetValue(object target, string propertyName)
{
Guard.NotNull(target, nameof(target));
var accessor = FindAccessor(propertyName);
return accessor.Get(target);
}
private IPropertyAccessor FindAccessor(string propertyName)
{
Guard.NotNullOrEmpty(propertyName, nameof(propertyName));
IPropertyAccessor accessor;
if (!accessors.TryGetValue(propertyName, out accessor))
{
throw new ArgumentException("Property does not exist.", nameof(propertyName));
}
return accessor;
}
}
}

77
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/PropertyAccessor.cs

@ -0,0 +1,77 @@
// ==========================================================================
// PropertyAccessor.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Reflection;
namespace PinkParrot.Infrastructure.Reflection
{
public sealed class PropertyAccessor : IPropertyAccessor
{
private sealed class PropertyWrapper<TObject, TValue> : IPropertyAccessor
{
private readonly Func<TObject, TValue> getMethod;
private readonly Action<TObject, TValue> setMethod;
public PropertyWrapper(PropertyInfo propertyInfo)
{
if (propertyInfo.CanRead)
{
getMethod = (Func<TObject, TValue>)propertyInfo.GetGetMethod(true).CreateDelegate(typeof(Func<TObject, TValue>));
}
else
{
getMethod = x => { throw new NotSupportedException(); };
}
if (propertyInfo.CanWrite)
{
setMethod = (Action<TObject, TValue>)propertyInfo.GetSetMethod(true).CreateDelegate(typeof(Action<TObject, TValue>));
}
else
{
setMethod = (x, y) => { throw new NotSupportedException(); };
}
}
public object Get(object source)
{
return getMethod((TObject)source);
}
public void Set(object source, object value)
{
setMethod((TObject)source, (TValue)value);
}
}
private readonly IPropertyAccessor internalAccessor;
public PropertyAccessor(Type targetType, PropertyInfo propertyInfo)
{
Guard.NotNull(targetType, nameof(targetType));
Guard.NotNull(propertyInfo, nameof(propertyInfo));
internalAccessor = (IPropertyAccessor)Activator.CreateInstance(typeof(PropertyWrapper<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType), propertyInfo);
}
public object Get(object target)
{
Guard.NotNull(target, nameof(target));
return internalAccessor.Get(target);
}
public void Set(object target, object value)
{
Guard.NotNull(target, nameof(target));
internalAccessor.Set(target, value);
}
}
}

175
src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Reflection/SimpleMapper.cs

@ -0,0 +1,175 @@
// ==========================================================================
// SimpleMapper.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
// ReSharper disable StaticMemberInGenericType
namespace PinkParrot.Infrastructure.Reflection
{
public static class SimpleMapper
{
private sealed class ConversionPropertyMapper : PropertyMapper
{
private readonly Type targetType;
public ConversionPropertyMapper(IPropertyAccessor srcAccessor, IPropertyAccessor dstAccessor, Type targetType)
: base(srcAccessor, dstAccessor)
{
this.targetType = targetType;
}
public override void Map(object source, object destination, CultureInfo culture)
{
var value = GetValue(source);
if (value == null)
{
return;
}
object converted;
try
{
converted = Convert.ChangeType(value, targetType, culture);
SetValue(destination, converted);
}
catch (InvalidCastException)
{
if (targetType == typeof(string))
{
converted = value.ToString();
SetValue(destination, converted);
}
}
}
}
private class PropertyMapper
{
private readonly IPropertyAccessor srcAccessor;
private readonly IPropertyAccessor dstAccessor;
public PropertyMapper(IPropertyAccessor srcAccessor, IPropertyAccessor dstAccessor)
{
this.srcAccessor = srcAccessor;
this.dstAccessor = dstAccessor;
}
public virtual void Map(object source, object destination, CultureInfo culture)
{
var value = GetValue(source);
SetValue(destination, value);
}
protected void SetValue(object destination, object value)
{
dstAccessor.Set(destination, value);
}
protected object GetValue(object source)
{
return srcAccessor.Get(source);
}
}
private static class ClassMapper<TSource, TDestination>
where TSource : class
where TDestination : class
{
private static readonly PropertyMapper[] Mappers;
private static readonly Type[] Convertibles =
{
typeof(bool),
typeof(byte),
typeof(char),
typeof(decimal),
typeof(float),
typeof(double),
typeof(short),
typeof(int),
typeof(long),
typeof(string),
typeof(DateTime)
};
static ClassMapper()
{
var dstType = typeof(TDestination);
var srcType = typeof(TSource);
var destinationProperties = dstType.GetProperties();
var newMappers = new List<PropertyMapper>();
foreach (var srcProperty in srcType.GetProperties().Where(x => x.CanRead))
{
var dstProperty = destinationProperties.FirstOrDefault(x => x.Name == srcProperty.Name);
if (dstProperty == null || !dstProperty.CanWrite)
{
continue;
}
var srcPropertyType = srcProperty.PropertyType;
var dstPropertyType = dstProperty.PropertyType;
if (srcPropertyType == dstPropertyType)
{
newMappers.Add(new PropertyMapper(new PropertyAccessor(srcType, srcProperty), new PropertyAccessor(dstType, dstProperty)));
}
else
{
if (Convertibles.Contains(dstPropertyType))
{
newMappers.Add(new ConversionPropertyMapper(new PropertyAccessor(srcType, srcProperty), new PropertyAccessor(dstType, dstProperty), dstPropertyType));
}
}
}
Mappers = newMappers.ToArray();
}
public static TDestination Map(TSource source, TDestination destination, CultureInfo culture)
{
foreach (var mapper in Mappers)
{
mapper.Map(source, destination, culture);
}
return destination;
}
}
public static TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
where TSource : class
where TDestination : class
{
return Map(source, destination, CultureInfo.CurrentCulture);
}
public static TDestination Map<TSource, TDestination>(TSource source, TDestination destination, CultureInfo culture)
where TSource : class
where TDestination : class
{
Guard.NotNull(source, nameof(source));
Guard.NotNull(culture, nameof(culture));
Guard.NotNull(destination, nameof(destination));
return ClassMapper<TSource, TDestination>.Map(source, destination, culture);
}
}
}

14
src/pinkparrot_read/PinkParrot.Read/IEntity.cs

@ -0,0 +1,14 @@
using System;
namespace PinkParrot.Read.Models
{
public interface IModelSchemaRM1
{
DateTime Created { get; set; }
string Hints { get; set; }
string Label { get; set; }
DateTime Modified { get; set; }
string Name { get; set; }
Guid SchemaId { get; set; }
}
}

11
src/pinkparrot_read/PinkParrot.Read/IModelSchemaRM.cs

@ -0,0 +1,11 @@
using System;
namespace PinkParrot.Read.Models
{
public interface IModelSchemaRM
{
DateTime Created { get; set; }
DateTime Modified { get; set; }
Guid SchemaId { get; set; }
}
}

21
src/pinkparrot_read/PinkParrot.Read/Models/IEntity.cs

@ -0,0 +1,21 @@
// ==========================================================================
// IEntity.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Read.Models
{
public interface IEntity
{
Guid Id { get; set; }
DateTime Created { get; set; }
DateTime LastModified { get; set; }
}
}

17
src/pinkparrot_read/PinkParrot.Read/Models/ITenantEntity.cs

@ -0,0 +1,17 @@
// ==========================================================================
// ITenantEntity.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
namespace PinkParrot.Read.Models
{
public interface ITenantEntity
{
Guid TenantId { get; set; }
}
}

25
src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaRM.cs → src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaListRM.cs

@ -7,38 +7,37 @@
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using PinkParrot.Infrastructure;
namespace PinkParrot.Read.Models
{
public sealed class ModelSchemaRM
public sealed class ModelSchemaListRM : IEntity, ITenantEntity
{
[Hide]
[BsonId]
public string Id { get; set; }
[Required]
[BsonElement]
public Guid SchemaId { get; set; }
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
[Required]
[BsonRequired]
[BsonElement]
public string Name { get; set; }
[Required]
[BsonRequired]
[BsonElement]
public DateTime Created { get; set; }
[Required]
[BsonRequired]
[BsonElement]
public DateTime Modified { get; set; }
public DateTime LastModified { get; set; }
[BsonRequired]
[BsonElement]
public string Label { get; set; }
public Guid TenantId { get; set; }
[BsonRequired]
[BsonElement]
public string Hints { get; set; }
public bool IsDeleted { get; set; }
}
}

3
src/pinkparrot_read/PinkParrot.Read/Repositories/IModelSchemaRepository.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PinkParrot.Read.Models;
@ -14,6 +15,6 @@ namespace PinkParrot.Read.Repositories
{
public interface IModelSchemaRepository
{
Task<List<ModelSchemaRM>> QueryAllAsync();
Task<List<ModelSchemaListRM>> QueryAllAsync(Guid tenantId);
}
}

70
src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/EntityMapper.cs

@ -0,0 +1,70 @@
// ==========================================================================
// EntityMapper.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
using MongoDB.Driver;
using PinkParrot.Infrastructure.CQRS;
using PinkParrot.Infrastructure.MongoDb;
using PinkParrot.Read.Models;
namespace PinkParrot.Read.Repositories.Implementations
{
public static class EntityMapper
{
public static T Create<T>(EnvelopeHeaders headers) where T : IEntity, new()
{
var timestamp = headers.Timestamp().ToDateTimeUtc();
var entity = new T { Id = headers.AggregateId(), Created = timestamp };
var tenantEntity = entity as ITenantEntity;
if (tenantEntity != null)
{
tenantEntity.TenantId = headers.TenantId();
}
return Update(entity, headers);
}
public static T Update<T>(T entity, EnvelopeHeaders headers) where T : IEntity
{
var timestamp = headers.Timestamp().ToDateTimeUtc();
entity.LastModified = timestamp;
return entity;
}
public static Task CreateAsync<T>(this IMongoCollection<T> collection, EnvelopeHeaders headers, Action<T> updater) where T : class, IEntity, new()
{
var entity = Create<T>(headers);
updater(entity);
return collection.InsertOneIfExistsAsync(entity);
}
public static async Task UpdateAsync<T>(this IMongoCollection<T> collection, EnvelopeHeaders headers, Action<T> updater) where T : class, IEntity
{
var entity = await collection.Find(t => t.Id == headers.AggregateId()).FirstOrDefaultAsync();
if (entity == null)
{
return;
}
Update(entity, headers);
updater(entity);
await collection.ReplaceOneAsync(t => t.Id == entity.Id, entity);
}
}
}

37
src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/MongoModelSchemaRepository.cs → src/pinkparrot_read/PinkParrot.Read/Repositories/Implementations/MongoModelSchemaListRepository.cs

@ -16,47 +16,46 @@ using PinkParrot.Infrastructure.CQRS;
using PinkParrot.Infrastructure.CQRS.Events;
using PinkParrot.Infrastructure.Dispatching;
using PinkParrot.Infrastructure.MongoDb;
using PinkParrot.Infrastructure.Tasks;
using PinkParrot.Read.Models;
namespace PinkParrot.Read.Repositories.Implementations
{
public sealed class MongoModelSchemaRepository : BaseMongoDbRepository<ModelSchemaRM>, IModelSchemaRepository, ICatchEventConsumer
public sealed class MongoModelSchemaListRepository : MongoRepositoryBase<ModelSchemaListRM>, IModelSchemaRepository, ICatchEventConsumer
{
public MongoModelSchemaRepository(IMongoDatabase database)
public MongoModelSchemaListRepository(IMongoDatabase database)
: base(database)
{
}
protected override Task SetupCollectionAsync(IMongoCollection<ModelSchemaRM> collection)
protected override Task SetupCollectionAsync(IMongoCollection<ModelSchemaListRM> collection)
{
return Collection.Indexes.CreateOneAsync(IndexKeys.Ascending(x => x.SchemaId));
return collection.Indexes.CreateOneAsync(IndexKeys.Ascending(x => x.Id));
}
public IQueryable<ModelSchemaRM> QuerySchemas()
public IQueryable<ModelSchemaListRM> QuerySchemas()
{
return Collection.AsQueryable();
}
public Task<List<ModelSchemaRM>> QueryAllAsync()
public Task<List<ModelSchemaListRM>> QueryAllAsync(Guid tenantId)
{
return Collection.Find(s => true).ToListAsync();
return Collection.Find(s => s.TenantId == tenantId && s.IsDeleted == false).ToListAsync();
}
public async void On(ModelSchemaCreated @event, EnvelopeHeaders headers)
public void On(ModelSchemaUpdated @event, EnvelopeHeaders headers)
{
var now = DateTime.UtcNow;
Collection.UpdateAsync(headers, e => e.Name = @event.Properties.Name).Forget();
}
var entity = new ModelSchemaRM
{
SchemaId = headers.AggregateId(),
Created = now,
Modified = now,
Name = @event.Properties.Name,
Hints = @event.Properties.Hints,
Label = @event.Properties.Label,
};
public void On(ModelSchemaDeleted @event, EnvelopeHeaders headers)
{
Collection.UpdateAsync(headers, e => e.IsDeleted = true).Forget();
}
await Collection.InsertOneAsync(entity);
public void On(ModelSchemaCreated @event, EnvelopeHeaders headers)
{
Collection.CreateAsync(headers, e => e.Name = @event.Properties.Name);
}
public void On(Envelope<IEvent> @event)

9
src/pinkparrot_read/PinkParrot.Read/Services/Implementations/MongoStreamPositionsStorage.cs

@ -8,16 +8,15 @@
using EventStore.ClientAPI;
using MongoDB.Bson;
using MongoDB.Driver;
using PinkParrot.Infrastructure.CQRS.EventStore;
using PinkParrot.Infrastructure.MongoDb;
using IFindFluentExtensions = MongoDB.Driver.IFindFluentExtensions;
using IMongoDatabase = MongoDB.Driver.IMongoDatabase;
//// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
namespace PinkParrot.Read.Services.Implementations
{
public sealed class MongoStreamPositionsStorage : BaseMongoDbRepository<MongoPosition>, IStreamPositionStorage
public sealed class MongoStreamPositionsStorage : MongoRepositoryBase<MongoPosition>, IStreamPositionStorage
{
private static readonly ObjectId Id = new ObjectId("507f1f77bcf86cd799439011");
@ -28,14 +27,14 @@ namespace PinkParrot.Read.Services.Implementations
public Position? ReadPosition()
{
var document = IFindFluentExtensions.FirstOrDefault<MongoPosition, MongoPosition>(Collection.Find(t => t.Id == Id));
var document = Collection.Find(t => t.Id == Id).FirstOrDefault<MongoPosition, MongoPosition>();
return document != null ? new Position(document.CommitPosition, document.PreparePosition) : Position.Start;
}
public void WritePosition(Position position)
{
var document = IFindFluentExtensions.FirstOrDefault<MongoPosition, MongoPosition>(Collection.Find(t => t.Id == Id));
var document = Collection.Find(t => t.Id == Id).FirstOrDefault<MongoPosition, MongoPosition>();
var isFound = document != null;

Loading…
Cancel
Save