From e7103e87d23a66976873e81fdbe6b29ce9beade0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6ran=20Krampe?= Date: Fri, 23 Oct 2009 09:37:12 +0200 Subject: [PATCH 1/3] Much improved (and now correct) attachment handling, thanks to Sebastian Negomireanu. --- Tests/CouchTest.cs | 36 ++++++++++++++++++++------- src/CouchDatabase.cs | 59 +++++++++++++++++++++++++++++--------------- src/CouchRequest.cs | 35 ++++++++++++++++++++------ 3 files changed, 94 insertions(+), 36 deletions(-) diff --git a/Tests/CouchTest.cs b/Tests/CouchTest.cs index 9ac5790..d1a26b6 100644 --- a/Tests/CouchTest.cs +++ b/Tests/CouchTest.cs @@ -191,15 +191,33 @@ public void ShouldStoreGetAndDeleteAttachment() { var doc = new CouchJsonDocument("{\"CPU\": \"Intel\"}"); ICouchDocument cd = db.CreateDocument(doc); - Assert.That(db.HasAttachment(cd), Is.False); - db.WriteAttachment(cd, "jabbadabba", "text/plain"); - Assert.That(db.HasAttachment(cd), Is.True); - Assert.That(db.ReadAttachment(cd), Is.EqualTo("jabbadabba")); - db.WriteAttachment(cd, "jabbadabba-doo", "text/plain"); - Assert.That(db.HasAttachment(cd), Is.True); - Assert.That(db.ReadAttachment(cd), Is.EqualTo("jabbadabba-doo")); - db.DeleteAttachment(cd); - Assert.That(db.HasAttachment(cd), Is.False); + var attachmentName = "someAttachment.txt"; + Assert.That(db.HasAttachment(cd,attachmentName), Is.False); + db.WriteAttachment(cd, attachmentName, "jabbadabba", "text/plain"); + Assert.That(db.HasAttachment(cd,attachmentName), Is.True); + + using (var response = db.ReadAttachment(cd, attachmentName)) + { + using (var reader = new StreamReader(response.GetResponseStream())) + { + Assert.That(reader.ReadToEnd(), Is.EqualTo("jabbadabba")); + } + } + + db.WriteAttachment(cd, attachmentName, "jabbadabba-doo", "text/plain"); + Assert.That(db.HasAttachment(cd,attachmentName), Is.True); + + using (var response = db.ReadAttachment(cd, attachmentName)) + { + using (var reader = new StreamReader(response.GetResponseStream())) + { + Assert.That(reader.ReadToEnd(), Is.EqualTo("jabbadabba-doo")); + } + } + + db.DeleteAttachment(cd,attachmentName); + + Assert.That(db.HasAttachment(cd,attachmentName), Is.False); } [Test, ExpectedException(typeof (CouchConflictException))] diff --git a/src/CouchDatabase.cs b/src/CouchDatabase.cs index 0e7b3fb..04e9ad7 100644 --- a/src/CouchDatabase.cs +++ b/src/CouchDatabase.cs @@ -4,6 +4,7 @@ using System.Net; using Divan.Lucene; using Newtonsoft.Json.Linq; +using System.Text; namespace Divan { @@ -60,17 +61,17 @@ public CouchDesignDocument NewDesignDocument(string aName) return newDoc; } - /// - /// Only to be used when developing. - /// - public CouchViewDefinition NewTempView(string designDoc, string viewName, string mapText) + /// + /// Only to be used when developing. + /// + public CouchViewDefinition NewTempView(string designDoc, string viewName, string mapText) { var doc = NewDesignDocument(designDoc); var view = doc.AddView(viewName, "function (doc) {" + mapText + "}"); doc.Synch(); return view; } - + /// /// Currently the logic is that the code is always the master. /// And we also do not remove design documents in the database that @@ -243,11 +244,27 @@ public ICouchDocument WriteDocument(ICouchDocument document, bool batch) /// Add an attachment to an existing ICouchDocument, it may already exist in db and will then be overwritten. /// /// Couch document - /// Binary data as string + /// Name of the attachment. + /// The attachment data. + /// The MIME type for the attachment. + /// The document. + /// This relies on the document to already have an id. + public ICouchDocument WriteAttachment(ICouchDocument document, string attachmentName, string attachmentData, string mimeType) + { + var byteData = Encoding.UTF8.GetBytes(attachmentData); + return WriteAttachment(document, attachmentName, byteData, mimeType); + } + + /// + /// Add an attachment to an existing ICouchDocument, it may already exist in db and will then be overwritten. + /// + /// Couch document + /// Name of the attachment. + /// The attachment data. /// The MIME type for the attachment. /// The document. /// This relies on the document to already have an id. - public ICouchDocument WriteAttachment(ICouchDocument document, string attachment, string mimeType) + public ICouchDocument WriteAttachment(ICouchDocument document, string attachmentName, byte[] attachmentData, string mimeType) { if (document.Id == null) { @@ -256,7 +273,7 @@ public ICouchDocument WriteAttachment(ICouchDocument document, string attachment } JObject result = - Request(document.Id + "/attachment").Query("?rev=" + document.Rev).Data(attachment).MimeType(mimeType).Put().Check("Failed to write attachment") + Request(document.Id + "/" + attachmentName).Query("?rev=" + document.Rev).Data(attachmentData).MimeType(mimeType).Put().Check("Failed to write attachment") .Result(); document.Id = result["id"].Value(); // Not really neeed document.Rev = result["rev"].Value(); @@ -277,9 +294,11 @@ public void ReadDocument(ICouchDocument document) /// Read the attachment for an ICouchDocument. /// /// Document to read. - public string ReadAttachment(ICouchDocument document) + /// Name of the attachment. + /// + public WebResponse ReadAttachment(ICouchDocument document, string attachmentName) { - return ReadAttachment(document.Id); + return ReadAttachment(document.Id, attachmentName); } /// @@ -348,11 +367,11 @@ public string ReadDocumentString(string documentId) /// /// Document identifier /// Document attachment - public string ReadAttachment(string documentId) + public WebResponse ReadAttachment(string documentId, string attachmentName) { try { - return Request(documentId + "/attachment").String(); + return Request(documentId + "/" + attachmentName).Response(); } catch (WebException e) { @@ -368,7 +387,7 @@ public string ReadAttachment(string documentId) /// POST which may be problematic in some environments. public CouchJsonDocument CreateDocument(string json) { - return (CouchJsonDocument) CreateDocument(new CouchJsonDocument(json)); + return (CouchJsonDocument)CreateDocument(new CouchJsonDocument(json)); } /// @@ -511,7 +530,7 @@ public IList GetDocuments(string[] documentIds) public T GetDocument(string documentId) where T : ICouchDocument, new() { - var doc = new T {Id = documentId}; + var doc = new T { Id = documentId }; try { ReadDocument(doc); @@ -576,9 +595,9 @@ public void DeleteDocument(ICouchDocument document) DeleteDocument(document.Id, document.Rev); } - public ICouchDocument DeleteAttachment(ICouchDocument document) + public ICouchDocument DeleteAttachment(ICouchDocument document, string attachmentName) { - JObject result = Request(document.Id + "/attachment").Query("?rev=" + document.Rev).Delete().Check("Failed to delete attachment").Result(); + JObject result = Request(document.Id + "/" + attachmentName).Query("?rev=" + document.Rev).Delete().Check("Failed to delete attachment").Result(); document.Id = result["id"].Value(); // Not really neeed document.Rev = result["rev"].Value(); return document; @@ -654,9 +673,9 @@ public bool HasDocument(ICouchDocument document) return HasDocument(document.Id); } - public bool HasAttachment(ICouchDocument document) + public bool HasAttachment(ICouchDocument document, string attachmentName) { - return HasAttachment(document.Id); + return HasAttachment(document.Id, attachmentName); } public bool HasDocumentChanged(ICouchDocument document) @@ -682,11 +701,11 @@ public bool HasDocument(string documentId) } } - public bool HasAttachment(string documentId) + public bool HasAttachment(string documentId, string attachmentName) { try { - Request(documentId + "/attachment").Head().Send(); + Request(documentId + "/" + attachmentName).Head().Send(); return true; } catch (WebException) diff --git a/src/CouchRequest.cs b/src/CouchRequest.cs index f0b1de8..f4281db 100644 --- a/src/CouchRequest.cs +++ b/src/CouchRequest.cs @@ -25,7 +25,7 @@ public class CouchRequest public string method = "GET"; // PUT, DELETE, POST, HEAD public string mimeType; public string path; - public string postData; + public byte[] postData; public string query; public JToken result; @@ -125,6 +125,12 @@ public CouchRequest Delete() } public CouchRequest Data(string data) + { + postData = Encoding.UTF8.GetBytes(data); + return this; + } + + public CouchRequest Data(byte[] data) { postData = data; return this; @@ -144,12 +150,12 @@ public CouchRequest MimeTypeJson() public JObject Result() { - return (JObject) result; + return (JObject)result; } public T Result() where T : JToken { - return (T) result; + return (T)result; } public string Etag() @@ -195,11 +201,11 @@ private HttpWebRequest GetRequest() if (postData != null) { - byte[] bytes = Encoding.UTF8.GetBytes(postData); - request.ContentLength = bytes.Length; + //byte[] bytes = Encoding.UTF8.GetBytes(postData); + request.ContentLength = postData.Length; using (Stream ps = request.GetRequestStream()) { - ps.Write(bytes, 0, bytes.Length); + ps.Write(postData, 0, postData.Length); ps.Close(); } } @@ -240,7 +246,7 @@ public T Parse() where T : JToken } //timer.Stop(); //Trace.WriteLine("Time for Couch HTTP & JSON PARSE: " + timer.ElapsedMilliseconds); - return (T) result; + return (T)result; } private void PickETag(WebResponse response) @@ -274,6 +280,21 @@ public string String() } } + public WebResponse Response() + { + WebResponse response = GetResponse(); + + PickETag(response); + if (etagToCheck != null) + { + if (IsETagValid()) + { + return null; + } + } + return response; + } + private WebResponse GetResponse() { return GetRequest().GetResponse(); From 2a8087616f3ebf31595f9b631d030fb40ce1a009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6ran=20Krampe?= Date: Fri, 23 Oct 2009 09:58:12 +0200 Subject: [PATCH 2/3] Better comments and added missing using directive. --- Tests/CouchTest.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Tests/CouchTest.cs b/Tests/CouchTest.cs index d1a26b6..4d69315 100644 --- a/Tests/CouchTest.cs +++ b/Tests/CouchTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Configuration; using System.Linq; +using System.IO; using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; using NUnit.Framework; @@ -11,8 +12,12 @@ namespace Divan.Test { /// /// Unit tests for Divan. Operates in a separate CouchDB database called divan_unit_tests. - /// If you are not running a CouchDB on localhost:5984 you will need to edit - /// the Tests/App.config file. + /// If you are not running a CouchDB on localhost:5984 you will need to first edit + /// the Tests/App.config file to point somewhere else. + /// + /// NOTE: App.config is copied to Tests/bin/Debug/Divan.Test.dll.config as a + /// Custom Command in the Makefile! if someone can tell me how to handle this better on + /// Mono/Monodevelop I am all ears. /// /// Run from command line using something like: /// nunit-console2 --labels -run=Divan.Test.CouchTest Tests/bin/Debug/Tests.dll From 25fb7b2707247cd2ee8ddf9c704d10cfbbc32a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6ran=20Krampe?= Date: Fri, 23 Oct 2009 10:00:00 +0200 Subject: [PATCH 3/3] Updates to README.rdoc and Makefile etc. --- README.rdoc | 6 ++---- Tests/Divan.Test.csproj | 4 +--- Tests/Makefile | 1 + 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/README.rdoc b/README.rdoc index d74143f..899adcd 100644 --- a/README.rdoc +++ b/README.rdoc @@ -39,11 +39,9 @@ in any mono release after 2.4.2.3 (I guess). Then we will remove that check. == What about documentation? At the moment documentation is... this file! :) But there are also unit tests in the separate Tests project: -* CouchTest.cs that runs against a CouchDB server. +* CouchTest.cs that runs against a CouchDB server, see the class comment for some instructions. * Lucene/CouchLuceneTest.cs that tests the CouchDB-Lucene integration if you have it, see article below. -When running the tests they will expect CouchDB running at localhost:5984, unless you copy the App.config.template to App.config and edit it. - There is also one sample project "Trivial" showing basic usage. More samples with more advanced usage is coming soon. * One blog article[http://goran.krampe.se/blog/Divan/divan-plus-couchdb-lucene.rdoc] on how to install Couchdb-Lucene and test it with Divan. @@ -68,4 +66,4 @@ Apart from polishing and fixing bugs the following pieces are on the todolist: * Writing a tutorial and a sample project showing the advanced mechanisms. == Contributing -...is simple. All contributions should be under the MIT license. Just fork and make pull requests and we will try to integrate. Feel free to bring up anything on the mailinglist. We haven't started using any issue tracker yet, but we will if it gets more popular and we when we do a proper release. \ No newline at end of file +...is simple. All contributions should be under the MIT license. Just fork and make pull requests and we will try to integrate. Feel free to bring up anything on the mailinglist. We haven't started using any issue tracker yet, but we will if it gets more popular and we when we do a proper release. diff --git a/Tests/Divan.Test.csproj b/Tests/Divan.Test.csproj index 96045da..1b55939 100644 --- a/Tests/Divan.Test.csproj +++ b/Tests/Divan.Test.csproj @@ -57,6 +57,7 @@ + @@ -69,9 +70,6 @@ Divan - - -