Skip to content

Commit

Permalink
Merge branch 'delete-document-feature' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
dragan committed Dec 20, 2010
2 parents 961d8bf + 4323676 commit 45ed944
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 15 deletions.
69 changes: 69 additions & 0 deletions src/SineSignal.Ottoman.Specs/CouchDocumentSessionSpecs.cs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -324,6 +324,75 @@ public void Should_return_populated_entity()
Assert.That(entity1.Login, Is.EqualTo("boblogin")); Assert.That(entity1.Login, Is.EqualTo("boblogin"));
} }
} }

public class When_deleting_an_entity : ConcernFor<CouchDocumentSession>
{
private Guid documentId;
private Employee entity1;
private Type entity1Type;
private PropertyInfo identityProperty;
private BulkDocsResult[] bulkDocsResults;
private ICouchDocumentConvention documentConvention;
private ICouchProxy couchProxy;
private ICouchDatabase couchDatabase;

protected override void Given()
{
documentId = Guid.NewGuid();
entity1Type = typeof(Employee);
identityProperty = entity1Type.GetProperty("Id");

documentConvention = Fake<ICouchDocumentConvention>();
documentConvention.GetIdentityPropertyFor(entity1Type).Returns(identityProperty);

var couchDocument = new CouchDocument();
couchDocument.Add("_id", documentId.ToString());
couchDocument.Add("_rev", "123456");
couchDocument.Add("Type", entity1Type.Name);
couchDocument.Add("Name", "Bob");
couchDocument.Add("Login", "boblogin");

bulkDocsResults = new BulkDocsResult[1];
bulkDocsResults[0] = new BulkDocsResult { Id = documentId.ToString(), Rev = "123456" };
couchProxy = Fake<ICouchProxy>();
couchProxy.Execute<CouchDocument>(Arg.Any<GetDocumentCommand>()).Returns(couchDocument);
couchProxy.Execute<BulkDocsResult[]>(Arg.Any<BulkDocsCommand>()).Returns(bulkDocsResults);

couchDatabase = Fake<ICouchDatabase>();
couchDatabase.Name.Returns("ottoman-test-database");
couchDatabase.CouchProxy.Returns(couchProxy);
couchDatabase.CouchDocumentConvention.Returns(documentConvention);
}

public override CouchDocumentSession CreateSystemUnderTest()
{
return new CouchDocumentSession(couchDatabase);
}

protected override void When()
{
entity1 = Sut.Load<Employee>(documentId.ToString());
Sut.Delete<Employee>(entity1);
Sut.SaveChanges();
}

[Test]
public void Should_call_get_identity_property()
{
documentConvention.Received().GetIdentityPropertyFor(entity1Type);
}

[Test]
public void Should_execute_bulk_docs_command_with_couch_proxy()
{
couchProxy.Received().Execute<BulkDocsResult[]>(Arg.Is<BulkDocsCommand>(c => {
var message = (BulkDocsMessage)c.Message;
return c.Route == couchDatabase.Name + "/_bulk_docs" &&
c.Operation == HttpMethod.Post &&
(message.NonAtomic == false && message.AllOrNothing == false && message.Docs.Length == 1);
}));
}
}
} }


public class Employee public class Employee
Expand Down
34 changes: 32 additions & 2 deletions src/SineSignal.Ottoman.Specs/CouchDocumentSpecs.cs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected override void Given()


protected override void When() protected override void When()
{ {
couchDocument = CouchDocument.Dehydrate(entity1, identityProperty, String.Empty); couchDocument = CouchDocument.Dehydrate(entity1, identityProperty, String.Empty, false);
} }


[Test] [Test]
Expand Down Expand Up @@ -56,7 +56,7 @@ protected override void Given()


protected override void When() protected override void When()
{ {
couchDocument = CouchDocument.Dehydrate(entity1, identityProperty, revision); couchDocument = CouchDocument.Dehydrate(entity1, identityProperty, revision, false);
} }


[Test] [Test]
Expand All @@ -69,5 +69,35 @@ public void Should_create_couch_document_with_revision()
Assert.That(couchDocument["Login"], Is.EqualTo(entity1.Login)); Assert.That(couchDocument["Login"], Is.EqualTo(entity1.Login));
} }
} }

public class When_dehydrating_an_entity_into_a_couch_document_that_is_marked_for_deletion : StaticConcern
{
private Employee entity1;
private Type entity1Type;
private PropertyInfo identityProperty;
private string revision;
private CouchDocument couchDocument;

protected override void Given()
{
entity1 = new Employee { Id = Guid.NewGuid(), Name = "Bob", Login = "boblogin" };
entity1Type = entity1.GetType();
identityProperty = entity1Type.GetProperty("Id");
revision = "abc123";
}

protected override void When()
{
couchDocument = CouchDocument.Dehydrate(entity1, identityProperty, revision, true);
}

[Test]
public void Should_create_couch_document_with_deleted_key()
{
Assert.That(couchDocument["_id"], Is.EqualTo(entity1.Id));
Assert.That(couchDocument["_rev"], Is.EqualTo(revision));
Assert.That(couchDocument["_deleted"], Is.True);
}
}
} }
} }
31 changes: 20 additions & 11 deletions src/SineSignal.Ottoman/CouchDocument.cs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,24 +7,33 @@ namespace SineSignal.Ottoman
{ {
internal class CouchDocument : Dictionary<string, object>, IDictionary<string, object> internal class CouchDocument : Dictionary<string, object>, IDictionary<string, object>
{ {
public static CouchDocument Dehydrate(object entity, PropertyInfo identityProperty, string revision) public static CouchDocument Dehydrate(object entity, PropertyInfo identityProperty, string revision, bool delete)
{ {
var couchDocument = new CouchDocument(); var couchDocument = new CouchDocument();
var entityType = entity.GetType(); var entityType = entity.GetType();


couchDocument["Type"] = entityType.Name; if (delete)

{
if (revision != String.Empty) couchDocument["_id"] = entityType.GetProperty(identityProperty.Name).GetValue(entity, null);
couchDocument["_rev"] = revision; couchDocument["_rev"] = revision;

couchDocument["_deleted"] = true;
foreach (PropertyInfo property in entityType.GetProperties().Where(p => p.CanRead)) }
else
{ {
var key = property.Name; couchDocument["Type"] = entityType.Name;
if (key == identityProperty.Name)
key = "_id";


var propertyValue = property.GetValue(entity, null); if (revision != String.Empty)
couchDocument[key] = propertyValue; couchDocument["_rev"] = revision;

foreach (PropertyInfo property in entityType.GetProperties().Where(p => p.CanRead))
{
var key = property.Name;
if (key == identityProperty.Name)
key = "_id";

var propertyValue = property.GetValue(entity, null);
couchDocument[key] = propertyValue;
}
} }


return couchDocument; return couchDocument;
Expand Down
37 changes: 35 additions & 2 deletions src/SineSignal.Ottoman/CouchDocumentSession.cs
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


using SineSignal.Ottoman.Commands; using SineSignal.Ottoman.Commands;
using SineSignal.Ottoman.Exceptions; using SineSignal.Ottoman.Exceptions;
Expand All @@ -15,12 +18,14 @@ public class CouchDocumentSession : ICouchDocumentSession


private Dictionary<string, object> IdentityMap { get; set; } private Dictionary<string, object> IdentityMap { get; set; }
private Dictionary<object, EntityMetadata> EntityMetadataMap { get; set; } private Dictionary<object, EntityMetadata> EntityMetadataMap { get; set; }
private HashSet<object> DeletedEntities { get; set; }


public CouchDocumentSession(ICouchDatabase couchDatabase) public CouchDocumentSession(ICouchDatabase couchDatabase)
{ {
CouchDatabase = couchDatabase; CouchDatabase = couchDatabase;
IdentityMap = new Dictionary<string, object>(); IdentityMap = new Dictionary<string, object>();
EntityMetadataMap = new Dictionary<object, EntityMetadata>(); EntityMetadataMap = new Dictionary<object, EntityMetadata>();
DeletedEntities = new HashSet<object>();
} }


public void Store(object entity) public void Store(object entity)
Expand Down Expand Up @@ -70,15 +75,41 @@ public void Store(object entity)
return StalkEntity<T>(couchDocument); return StalkEntity<T>(couchDocument);
} }


public void Delete<T>(T entity)
{
if (EntityMetadataMap.ContainsKey(entity) == false)
throw new InvalidOperationException(String.Format("{0} is not associated with the current couch document session, cannot delete unknown entity.", entity));

DeletedEntities.Add(entity);
}

public void SaveChanges() public void SaveChanges()
{ {
var docs = new List<CouchDocument>(); var docs = new List<CouchDocument>();
var entities = new List<object>(); var entities = new List<object>();

foreach (var entity in DeletedEntities)
{
EntityMetadata entityMetadata;
if (EntityMetadataMap.TryGetValue(entity, out entityMetadata))
{
EntityMetadataMap.Remove(entity);
IdentityMap.Remove(entityMetadata.Key);

Type entityType = entity.GetType();
PropertyInfo identityProperty = CouchDatabase.CouchDocumentConvention.GetIdentityPropertyFor(entityType);

docs.Add(CouchDocument.Dehydrate(entity, identityProperty, entityMetadata.Revision, true));
entities.Add(entity);
}
}
DeletedEntities.Clear();

foreach (var entity in EntityMetadataMap.Where(pair => IsEntityDirty(pair.Key, pair.Value))) foreach (var entity in EntityMetadataMap.Where(pair => IsEntityDirty(pair.Key, pair.Value)))
{ {
Type entityType = entity.Key.GetType(); Type entityType = entity.Key.GetType();
PropertyInfo identityProperty = CouchDatabase.CouchDocumentConvention.GetIdentityPropertyFor(entityType); PropertyInfo identityProperty = CouchDatabase.CouchDocumentConvention.GetIdentityPropertyFor(entityType);
docs.Add(CouchDocument.Dehydrate(entity.Key, identityProperty, entity.Value.Revision)); docs.Add(CouchDocument.Dehydrate(entity.Key, identityProperty, entity.Value.Revision, false));
entities.Add(entity.Key); entities.Add(entity.Key);
} }


Expand Down Expand Up @@ -120,7 +151,9 @@ private static object GetIdentityValueFor(object entity, PropertyInfo identityPr


private bool IsEntityDirty(object entity, EntityMetadata entityMetadata) private bool IsEntityDirty(object entity, EntityMetadata entityMetadata)
{ {
return entity.Equals(entityMetadata.OriginalEntity) == false; // TODO: Need to find a better way to tell if the entity is dirty
//return entity.Equals(entityMetadata.OriginalEntity) == false;
return true;
} }


private T StalkEntity<T>(CouchDocument couchDocument) where T : new() private T StalkEntity<T>(CouchDocument couchDocument) where T : new()
Expand Down
1 change: 1 addition & 0 deletions src/SineSignal.Ottoman/ICouchDocumentSession.cs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public interface ICouchDocumentSession
{ {
void Store(object entity); void Store(object entity);
T Load<T>(string id) where T : new(); T Load<T>(string id) where T : new();
void Delete<T>(T entity);
void SaveChanges(); void SaveChanges();
} }
} }

0 comments on commit 45ed944

Please sign in to comment.