From acfb937246493dacfaf5a63005ae862c33da666a Mon Sep 17 00:00:00 2001 From: Foretagsplatsen Date: Tue, 11 Aug 2009 16:30:40 +0200 Subject: [PATCH] initial commit --- .gitignore | 11 + Divan.sln | 26 + README.rdoc | 92 +++ lib/Newtonsoft.Json.dll | Bin 0 -> 225280 bytes lib/nunit.framework.dll | Bin 0 -> 77824 bytes samples/Trivial/Program.cs | 136 +++++ samples/Trivial/Properties/AssemblyInfo.cs | 36 ++ samples/Trivial/Trivial.csproj | 69 +++ src/CouchBulkDeleteDocuments.cs | 37 ++ src/CouchBulkDocuments.cs | 47 ++ src/CouchBulkKeys.cs | 55 ++ src/CouchConflictException.cs | 14 + src/CouchDatabase.cs | 615 +++++++++++++++++++++ src/CouchDocument.cs | 114 ++++ src/CouchException.cs | 62 +++ src/CouchGenericViewResult.cs | 94 ++++ src/CouchJSONDocument.cs | 86 +++ src/CouchNotFoundException.cs | 14 + src/CouchPermanentViewResult.cs | 9 + src/CouchQuery.cs | 212 +++++++ src/CouchQueryDocument.cs | 19 + src/CouchRequest.cs | 291 ++++++++++ src/CouchServer.cs | 180 ++++++ src/CouchTest.cs | 252 +++++++++ src/CouchViewDefinition.cs | 85 +++ src/CouchViewResult.cs | 36 ++ src/CustomDictionary.xml | 468 ++++++++++++++++ src/DesignCouchDocument.cs | 66 +++ src/Divan.FxCop | 307 ++++++++++ src/Divan.csproj | 80 +++ src/ICanJSON.cs | 16 + src/ICouchDocument.cs | 14 + src/Properties/AssemblyInfo.cs | 39 ++ 33 files changed, 3582 insertions(+) create mode 100644 .gitignore create mode 100644 Divan.sln create mode 100644 README.rdoc create mode 100644 lib/Newtonsoft.Json.dll create mode 100644 lib/nunit.framework.dll create mode 100644 samples/Trivial/Program.cs create mode 100644 samples/Trivial/Properties/AssemblyInfo.cs create mode 100644 samples/Trivial/Trivial.csproj create mode 100644 src/CouchBulkDeleteDocuments.cs create mode 100644 src/CouchBulkDocuments.cs create mode 100644 src/CouchBulkKeys.cs create mode 100644 src/CouchConflictException.cs create mode 100644 src/CouchDatabase.cs create mode 100644 src/CouchDocument.cs create mode 100644 src/CouchException.cs create mode 100644 src/CouchGenericViewResult.cs create mode 100644 src/CouchJSONDocument.cs create mode 100644 src/CouchNotFoundException.cs create mode 100644 src/CouchPermanentViewResult.cs create mode 100644 src/CouchQuery.cs create mode 100644 src/CouchQueryDocument.cs create mode 100644 src/CouchRequest.cs create mode 100644 src/CouchServer.cs create mode 100644 src/CouchTest.cs create mode 100644 src/CouchViewDefinition.cs create mode 100644 src/CouchViewResult.cs create mode 100644 src/CustomDictionary.xml create mode 100644 src/DesignCouchDocument.cs create mode 100644 src/Divan.FxCop create mode 100644 src/Divan.csproj create mode 100644 src/ICanJSON.cs create mode 100644 src/ICouchDocument.cs create mode 100644 src/Properties/AssemblyInfo.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d45997 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +obj +bin +_ReSharper.* +*.csproj.user +*.resharper.user +*.resharper +*.suo +*.cache +*~ +*.swp +.svn \ No newline at end of file diff --git a/Divan.sln b/Divan.sln new file mode 100644 index 0000000..25cde46 --- /dev/null +++ b/Divan.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Divan", "src\Divan.csproj", "{37AC0B66-5340-4B81-BC62-3EE80233A011}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trivial", "samples\Trivial\Trivial.csproj", "{CDCC7924-F227-46DC-B2E6-2BBE06B84AF2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {37AC0B66-5340-4B81-BC62-3EE80233A011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37AC0B66-5340-4B81-BC62-3EE80233A011}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37AC0B66-5340-4B81-BC62-3EE80233A011}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37AC0B66-5340-4B81-BC62-3EE80233A011}.Release|Any CPU.Build.0 = Release|Any CPU + {CDCC7924-F227-46DC-B2E6-2BBE06B84AF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDCC7924-F227-46DC-B2E6-2BBE06B84AF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDCC7924-F227-46DC-B2E6-2BBE06B84AF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDCC7924-F227-46DC-B2E6-2BBE06B84AF2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..d6cd275 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,92 @@ += Divan, a C# library for CouchDB + +Divan is a C# library for using CouchDB (http://www.couchdb.org). It should be more or less API complete +including bulk operations, attachments, views and design documents etc. It is quite fast and designed +to be flexible but not bloated. + +Divan has been developed in-house at Foretagsplatsen AB (www.foretagsplatsen.se) and is being used in +the new core system at Foretagsplatsen. It has unit tests (although could benefit from more) and at least +one sample console project included. + +== Does it work under Mono? + +You bet. Foretagsplatsen uses mainly windows (Visual Studio 2008 and .Net 3.5) but Divan is meant to +work fine in Mono too. + +== What about documentation? + +At the moment documentation is... this file! :) But there are unit tests in CouchTest.cs and there +is at least one sample project showing basic usage. One more sample with more advanced usage is coming soon. + +== Dependencies + +The only dependencies and their tested versions are: + +* Newtonsoft.JSON (3.5 Beta 4), MIT-licensed fast library for JSON reading and writing, see: http://json.codeplex.com +* NUnit (2.4.8). Unit testing framework, see: http://www.nunit.org +* CouchDB (0.9.1, 0.9). Running on a server somewhere, see: http://www.couchdb.org + +The two neeed dlls are included in the lib directory. + +== Getting started + +Well... it goes something like this: + +1. First get CouchDB up and running on some box. + +2. Clone Divan and build it. + +3. Run the "Trivial" sample console app by pointing it at a running CouchDB server getting output similar to this: + + C:\Divan\samples\Trivial>bin\Debug\Trivial.exe 192.168.9.205 5984 + Using 192.168.9.205:5984 + Created a CouchServer + Request: http://192.168.9.205:5984/trivial Method: HEAD + Request: http://192.168.9.205:5984/trivial Method: PUT + Created database 'trivial' + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Request: http://192.168.9.205:5984/trivial/ Method: POST + Saved 10 Cars with 170 hps each. + Request: http://192.168.9.205:5984/trivial/86a9d1ad306e204a037940c4fb0cbbe7 Method: PUT + Modified last Car with id 86a9d1ad306e204a037940c4fb0cbbe7 + Request: http://192.168.9.205:5984/trivial/86a9d1ad306e204a037940c4fb0cbbe7 Method: GET + Loaded last Car Saab 93 now with 400hps. + Request: http://192.168.9.205:5984/trivial/_all_docs?include_docs=true Method: GET + Loaded all Cars: 10 + Request: http://192.168.9.205:5984/trivial/1f6de464ae8034eb952b93105807f22c?rev1-208231211 Method: DELETE + Deleted car with id 1f6de464ae8034eb952b93105807f22c + Request: http://192.168.9.205:5984/trivial/44f9638877fc09e07bda6504a5bfd40d?rev1-345884075 Method: DELETE + Deleted car with id 44f9638877fc09e07bda6504a5bfd40d + Request: http://192.168.9.205:5984/trivial/591e0ef170154311aafa8a2a5fcbb310?rev1-2891194419 Method: DELETE + Deleted car with id 591e0ef170154311aafa8a2a5fcbb310 + Request: http://192.168.9.205:5984/trivial/7124fada0f90ed93f88168dc6c1c8b4f?rev1-2479256166 Method: DELETE + Deleted car with id 7124fada0f90ed93f88168dc6c1c8b4f + Request: http://192.168.9.205:5984/trivial/73115c4dd3b2bcd512412813dd04b901?rev1-3993011972 Method: DELETE + Deleted car with id 73115c4dd3b2bcd512412813dd04b901 + Request: http://192.168.9.205:5984/trivial/78a305543bff2f474cbcfe2ac667cc6d?rev1-1583012745 Method: DELETE + Deleted car with id 78a305543bff2f474cbcfe2ac667cc6d + Request: http://192.168.9.205:5984/trivial/86a9d1ad306e204a037940c4fb0cbbe7?rev2-1402499148 Method: DELETE + Deleted car with id 86a9d1ad306e204a037940c4fb0cbbe7 + Request: http://192.168.9.205:5984/trivial/9e647b114b9ba1c8a170ed1c1951260c?rev1-30425501 Method: DELETE + Deleted car with id 9e647b114b9ba1c8a170ed1c1951260c + Request: http://192.168.9.205:5984/trivial/bad56664dec8f3d1fd30db70510f7ff2?rev1-1765374009 Method: DELETE + Deleted car with id bad56664dec8f3d1fd30db70510f7ff2 + Request: http://192.168.9.205:5984/trivial/cbf0bb7f8ebc4aad02a2c90228d1076f?rev1-2425501185 Method: DELETE + Deleted car with id cbf0bb7f8ebc4aad02a2c90228d1076f + Request: http://192.168.9.205:5984/trivial Method: HEAD + Request: http://192.168.9.205:5984/trivial Method: DELETE + Deleted database + + +3. Look at couchTest.cs and make sure the tests are green, you may need to edit CochServer.cs with different default server ip. + +4. Have fun! + diff --git a/lib/Newtonsoft.Json.dll b/lib/Newtonsoft.Json.dll new file mode 100644 index 0000000000000000000000000000000000000000..782e9c0bea75ab433afa4af78b4b5beb2e1b2af9 GIT binary patch literal 225280 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 literal 0 HcmV?d00001 diff --git a/samples/Trivial/Program.cs b/samples/Trivial/Program.cs new file mode 100644 index 0000000..7f175f0 --- /dev/null +++ b/samples/Trivial/Program.cs @@ -0,0 +1,136 @@ +using System; +using System.Diagnostics; +using Divan; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Trivial +{ + ///

+ /// A trivial example of using Divan. Requires a running CouchDB on localhost! + /// + /// Run using: + /// + /// Trivial.exe + /// + /// + class Program + { + static void Main(string[] args) { + string host = "localhost"; + int port = 5984; + + // Lets you see all HTTP requests made by Divan + Trace.Listeners.Add(new ConsoleTraceListener()); + + // Trivial parse of args to get host and port + switch (args.Length) { + case 0: + Console.WriteLine("Using localhost:5984"); + break; + case 1: + Console.WriteLine("Using " + args[0] + ":5984"); + host = args[0]; + break; + case 2: + Console.WriteLine("Using " + args[0] + ":" + args[1]); + host = args[0]; + port = int.Parse(args[1]); + break; + } + + // Get a server for default couch port 5984 on localhost + var server = new CouchServer(host, port); + Console.WriteLine("Created a CouchServer"); + + // Get (creates it if needed) a CouchDB database. + var db = server.GetDatabase("trivial"); + Console.WriteLine("Created database 'trivial'"); + + // Create and save 10 Cars with automatically allocated Ids by Couch + Car car = null; + for (int i = 0; i < 10; i++) + { + car = new Car("Saab", "93", 170); + db.SaveDocument(car); + } + Console.WriteLine("Saved 10 Cars with 170 hps each."); + + // Modify the last Car we saved... + car.HorsePowers = 400; + + // ...and save the change. + // We could also have used WriteDocument if we knew it was an existing doc + db.SaveDocument(car); + Console.WriteLine("Modified last Car with id " + car.Id); + + // Load a Car by known id, class to instantiate using generics + var sameCar = db.GetDocument(car.Id); + Console.WriteLine("Loaded last Car " + sameCar.Make + " " + sameCar.Model + " now with " + sameCar.HorsePowers + "hps."); + + // Load all Cars, class to instantiate using generics + var cars = db.QueryAllDocuments().IncludeDocuments().GetResult().Documents(); + Console.WriteLine("Loaded all Cars: " + cars.Count); + + // Delete all Cars one by one + foreach (var eachCar in cars) + { + db.DeleteDocument(eachCar); + Console.WriteLine("Deleted car with id " + eachCar.Id); + } + + // Delete the db itself + db.Delete(); + Console.WriteLine("Deleted database"); + } + + /// + /// The simplest way to deal with domain objects is to subclass CouchDocument + /// and inherit members Id and Rev. You will need to implement WriteJson/ReadJson. + /// + private class Car : CouchDocument + { + public string Make; + public string Model; + public int HorsePowers; + + public Car() + { + // This constructor is needed by Divan + } + + public Car(string make, string model, int hps) + { + Make = make; + Model = model; + HorsePowers = hps; + } + #region CouchDocument Members + + public override void WriteJson(JsonWriter writer) + { + // This will write id and rev + base.WriteJson(writer); + + writer.WritePropertyName("Make"); + writer.WriteValue(Make); + writer.WritePropertyName("Model"); + writer.WriteValue(Model); + writer.WritePropertyName("Hps"); + writer.WriteValue(HorsePowers); + } + + public override void ReadJson(JObject obj) + { + // This will read id and rev + base.ReadJson(obj); + + Make = obj["Make"].Value(); + Model = obj["Model"].Value(); + HorsePowers = obj["Hps"].Value(); + } + + #endregion + } + } +} diff --git a/samples/Trivial/Properties/AssemblyInfo.cs b/samples/Trivial/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..66856b0 --- /dev/null +++ b/samples/Trivial/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Trivial")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSC Konsult AB")] +[assembly: AssemblyProduct("Trivial")] +[assembly: AssemblyCopyright("Copyright © MSC Konsult AB 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("05c213a1-cf72-45ce-8607-7827b32a1699")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/Trivial/Trivial.csproj b/samples/Trivial/Trivial.csproj new file mode 100644 index 0000000..54b823d --- /dev/null +++ b/samples/Trivial/Trivial.csproj @@ -0,0 +1,69 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {CDCC7924-F227-46DC-B2E6-2BBE06B84AF2} + Exe + Properties + Trivial + Trivial + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\lib\Newtonsoft.Json.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + {37AC0B66-5340-4B81-BC62-3EE80233A011} + Divan + + + + + \ No newline at end of file diff --git a/src/CouchBulkDeleteDocuments.cs b/src/CouchBulkDeleteDocuments.cs new file mode 100644 index 0000000..a6d6ae3 --- /dev/null +++ b/src/CouchBulkDeleteDocuments.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// Only used as pseudo doc when doing bulk updates/inserts. + /// + public class CouchBulkDeleteDocuments : CouchBulkDocuments + { + public CouchBulkDeleteDocuments(IList docs) : base(docs) + { + } + + public override void WriteJson(JsonWriter writer) + { + writer.WritePropertyName("docs"); + writer.WriteStartArray(); + foreach (ICouchDocument doc in Docs) + { + writer.WriteStartObject(); + CouchDocument.WriteIdAndRev(doc, writer); + writer.WritePropertyName("_deleted"); + writer.WriteValue(true); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + public override void ReadJson(JObject obj) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/CouchBulkDocuments.cs b/src/CouchBulkDocuments.cs new file mode 100644 index 0000000..dc95009 --- /dev/null +++ b/src/CouchBulkDocuments.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// Only used as psuedo doc when doing bulk updates/inserts. + /// + public class CouchBulkDocuments : ICanJson + { + public CouchBulkDocuments(IList docs) + { + Docs = docs; + } + + public IList Docs { get; private set; } + + #region ICouchBulk Members + + public int Count() + { + return Docs.Count; + } + + public virtual void WriteJson(JsonWriter writer) + { + writer.WritePropertyName("docs"); + writer.WriteStartArray(); + foreach (ICouchDocument doc in Docs) + { + writer.WriteStartObject(); + doc.WriteJson(writer); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + public virtual void ReadJson(JObject obj) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/CouchBulkKeys.cs b/src/CouchBulkKeys.cs new file mode 100644 index 0000000..2ac3787 --- /dev/null +++ b/src/CouchBulkKeys.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// Only used as psuedo doc when doing bulk reads. + /// + public class CouchBulkKeys : ICanJson + { + public CouchBulkKeys(IEnumerable ids) + { + Ids = ids.ToArray(); + } + + public CouchBulkKeys() + { + } + + public CouchBulkKeys(string[] ids) + { + Ids = ids; + } + + public string[] Ids { get; set; } + + #region ICouchBulk Members + + public virtual void WriteJson(JsonWriter writer) + { + writer.WritePropertyName("keys"); + writer.WriteStartArray(); + foreach (string id in Ids) + { + writer.WriteValue(id); + } + writer.WriteEndArray(); + } + + public virtual void ReadJson(JObject obj) + { + throw new NotImplementedException(); + } + + public int Count() + { + return Ids.Count(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/CouchConflictException.cs b/src/CouchConflictException.cs new file mode 100644 index 0000000..0573066 --- /dev/null +++ b/src/CouchConflictException.cs @@ -0,0 +1,14 @@ +using System; + +namespace Divan +{ + /// + /// Represents a CouchDB HTTP 409 conflict. + /// + public class CouchConflictException : Exception + { + public CouchConflictException(string msg, Exception e) : base(msg, e) + { + } + } +} \ No newline at end of file diff --git a/src/CouchDatabase.cs b/src/CouchDatabase.cs new file mode 100644 index 0000000..672a67b --- /dev/null +++ b/src/CouchDatabase.cs @@ -0,0 +1,615 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Net; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// A CouchDatabase corresponds to a named CouchDB database in a specific CouchServer. + /// This is the main API to work with CouchDB. One useful approach is to create your own subclasses + /// for your different databases. + /// + public class CouchDatabase + { + public CouchDatabase() : this("default", new CouchServer()) + { + } + + public CouchDatabase(string name) : this(name, new CouchServer()) + { + } + + public CouchDatabase(CouchServer server) : this("default", server) + { + } + + public CouchDatabase(string name, CouchServer server) + { + Name = server.DatabasePrefix + name; + Server = server; + } + + public CouchServer Server { get; set; } + public string Name { get; set; } + + public CouchRequest Request() + { + return new CouchRequest(this); + } + + public CouchRequest Request(string path) + { + return (new CouchRequest(this)).Path(path); + } + + public int CountDocuments() + { + return (Request().Parse())["doc_count"].Value(); + } + + public CouchRequest RequestAllDocuments() + { + return Request("_all_docs"); + } + + /// + /// Read all documents in database. + /// + /// List of documents in database. + public IList GetAllDocuments() + { + var list = new List(); + JObject json = Request("_all_docs").Parse(); + foreach (JObject row in json["rows"]) + { + list.Add(new CouchDocument(row["id"].ToString(), (row["value"])["rev"].ToString())); + } + return list; + } + + /// + /// Initialize CouchDB database by loading design documents into it. + /// Override in subclasses. + /// + public virtual void Initialize() + { + // Nothing by default + } + + public bool Exists() + { + return Server.HasDatabase(Name); + } + + /// + /// Check first if database exists, and if it does not - create it and initialize it. + /// + public void Create() + { + if (!Exists()) + { + Server.CreateDatabase(Name); + Initialize(); + } + } + + public void Delete() + { + if (Exists()) + { + Server.DeleteDatabase(Name); + } + } + + /// + /// Write a CouchDocument, it may already exist in db and will then be overwritten. + /// + /// Document as Json + /// Document identifier + /// A new CouchDocument + public ICouchDocument WriteDocument(string json, string documentId) + { + return WriteDocument(new CouchDocument(json, documentId)); + } + + /// + /// Write a CouchDocument or ICouchDocument, it may already exist in db and will then be overwritten. + /// + /// Couch document + /// Couch Document with new Rev set. + /// This relies on the document to already have an id. + public ICouchDocument + WriteDocument(ICouchDocument document) + { + return WriteDocument(document, false); + } + + /// + /// This is a convenience method that creates or writes a ICouchDocument depending on if + /// it has an id or not. If it does not have an id we create the document and let CouchDB allocate + /// an id. If it has an id we use WriteDocument which will overwrite the document in CouchDB. + /// + /// Couch document + /// Couch Document with new Rev set and possibly an Id set. + public ICouchDocument SaveDocument(ICouchDocument document) + { + if (document.Id == null) + { + return CreateDocument(document); + } + return WriteDocument(document); + } + + /// + /// Write a CouchDocument or ICouchDocument, it may already exist in db and will then be overwritten. + /// + /// Couch document + /// True if we don't want to wait for flush (commit). + /// Couch Document with new Rev set. + /// This relies on the document to already have an id. + public ICouchDocument WriteDocument(ICouchDocument document, bool batch) + { + if (document.Id == null) + { + throw CouchException.Create( + "Failed to write document using PUT because it lacks an id, use CreateDocument instead to let CouchDB generate an id"); + } + JObject result = + Request(document.Id).Query(batch ? "?batch=ok" : null).Data(CouchDocument.WriteJson(document)).Put().Check("Failed to write document").Result(); + document.Id = result["id"].Value(); // Not really neeed + document.Rev = result["rev"].Value(); + + return document; + } + + /// + /// Add an attachment to an existing ICouchDocument, it may already exist in db and will then be overwritten. + /// + /// Couch document + /// Binary data as string + /// 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) + { + if (document.Id == null) + { + throw CouchException.Create( + "Failed to add attachment to document using PUT because it lacks an id"); + } + + JObject result = + Request(document.Id + "/attachment").Query("?rev=" + document.Rev).Data(attachment).MimeType(mimeType).Put().Check("Failed to write attachment") + .Result(); + document.Id = result["id"].Value(); // Not really neeed + document.Rev = result["rev"].Value(); + + return document; + } + + /// + /// Read a ICouchDocument with an id even if it has not changed revision. + /// + /// Document to fill. + public void ReadDocument(ICouchDocument document) + { + document.ReadJson(ReadDocument(document.Id)); + } + + /// + /// Read the attachment for an ICouchDocument. + /// + /// Document to read. + public string ReadAttachment(ICouchDocument document) + { + return ReadAttachment(document.Id); + } + + /// + /// First use HEAD to see if it has indeed changed. + /// + /// Document to fill. + public void FetchDocumentIfChanged(ICouchDocument document) + { + if (HasDocumentChanged(document)) + { + ReadDocument(document); + } + } + + /// + /// Read a CouchDocument or ICouchDocument, this relies on the document to obviously have an id. + /// We also check the revision so that we can avoid parsing JSON if the document is unchanged. + /// + /// Document to fill. + public void ReadDocumentIfChanged(ICouchDocument document) + { + JObject result = Request(document.Id).Etag(document.Rev).Parse(); + if (result == null) + { + return; + } + document.ReadJson(result); + } + + /// + /// Read a couch document given an id, this method does not have enough information to do caching. + /// + /// Document identifier + /// Document Json as JObject + public JObject ReadDocument(string documentId) + { + try + { + return Request(documentId).Parse(); + } + catch (WebException e) + { + throw CouchException.Create("Failed to read document", e); + } + } + + /// + /// Read a couch document given an id, this method does not have enough information to do caching. + /// + /// Document identifier + /// Document Json as string + public string ReadDocumentString(string documentId) + { + try + { + return Request(documentId).String(); + } + catch (WebException e) + { + throw CouchException.Create("Failed to read document: " + e.Message, e); + } + } + + /// + /// Read a couch attachment given a document id, this method does not have enough information to do caching. + /// + /// Document identifier + /// Document attachment + public string ReadAttachment(string documentId) + { + try + { + return Request(documentId + "/attachment").String(); + } + catch (WebException e) + { + throw CouchException.Create("Failed to read document: " + e.Message, e); + } + } + + /// + /// Create a CouchDocument given JSON as a string. Uses POST and CouchDB will allocate a new id. + /// + /// Json data to store. + /// Couch document with data, id and rev set. + /// POST which may be problematic in some environments. + public CouchJsonDocument CreateDocument(string json) + { + return (CouchJsonDocument) CreateDocument(new CouchJsonDocument(json)); + } + + /// + /// Create a given ICouchDocument in CouchDB. Uses POST and CouchDB will allocate a new id and overwrite any existing id. + /// + /// Document to store. + /// Document with Id and Rev set. + /// POST which may be problematic in some environments. + public ICouchDocument CreateDocument(ICouchDocument document) + { + try + { + JObject result = Request().Data(CouchDocument.WriteJson(document)).Post().Check("Failed to create document").Result(); + document.Id = result["id"].Value(); + document.Rev = result["rev"].Value(); + return document; + } + catch (WebException e) + { + throw CouchException.Create("Failed to create document", e); + } + } + + /// + /// Create or update a list of ICouchDocuments in CouchDB. Uses POST and CouchDB will + /// allocate new ids if the documents lack them. + /// + /// List of documents to store. + /// POST may be problematic in some environments. + public void SaveDocuments(IList documents, bool allOrNothing) + { + var bulk = new CouchBulkDocuments(documents); + try + { + var result = + Request("_bulk_docs").Data(CouchDocument.WriteJson(bulk)).Query("?all_or_nothing=" + allOrNothing.ToString().ToLower()).PostJson().Parse + (); + for (int i = 0; i < documents.Count; i++) + { + documents[i].Id = (result[i])["id"].Value(); + documents[i].Rev = (result[i])["rev"].Value(); + } + } + catch (WebException e) + { + throw CouchException.Create("Failed to create bulk documents", e); + } + } + + /// + /// Create or updates documents in bulk fashion, chunk wise. Optionally access given view + /// after each chunk to trigger reindexing. + /// + /// List of documents to store. + /// Number of documents to store per "POST" + /// List of views to touch per chunk. + public void SaveDocuments(IList documents, int chunkCount, List views, bool allOrNothing) + { + var chunk = new List(chunkCount); + int counter = 0; + + foreach (ICouchDocument doc in documents) + { + // Do we have a chunk ready to create? + if (counter == chunkCount) + { + counter = 0; + SaveDocuments(chunk, allOrNothing); + TouchViews(views); + /* Skipping separate thread for now, ASP.Net goes bonkers... + (new Thread( + () => GetView(designDocumentName, viewName, "")) + { + Name = "View access in background", Priority = ThreadPriority.BelowNormal + }).Start(); */ + + chunk = new List(chunkCount); + } + counter++; + chunk.Add(doc); + } + + SaveDocuments(chunk, allOrNothing); + TouchViews(views); + } + + public void TouchViews(List views) + { + var timer = new Stopwatch(); + if (views != null) + { + foreach (CouchViewDefinition view in views) + { + if (view != null) + { + timer.Reset(); + timer.Start(); + view.Touch(); + timer.Stop(); + Trace.WriteLine("Update view " + view.Path() + ":" + timer.ElapsedMilliseconds + " ms"); + } + } + } + } + + /// + /// Create documents in bulk fashion, chunk wise. + /// + /// List of documents to store. + /// Number of documents to store per "POST" + public void SaveDocuments(IList documents, int chunkCount, bool allOrNothing) + { + SaveDocuments(documents, chunkCount, null, allOrNothing); + } + + /// + /// Get multiple documents. + /// + /// List of documents to get. + public IList GetDocuments(IList documentIds) where T : ICouchDocument, new() + { + return GetDocuments(documentIds.ToArray()); + } + + public IList GetDocuments(IList documentIds) + { + return GetDocuments(documentIds); + } + + public IList GetDocuments(string[] documentIds) + { + return GetDocuments(documentIds); + } + + public IList GetDocuments(string[] documentIds) where T : ICouchDocument, new() + { + var bulk = new CouchBulkKeys(documentIds); + return QueryAllDocuments().Data(CouchDocument.WriteJson(bulk)).IncludeDocuments().GetResult().Documents(); + } + + public T GetDocument(string documentId) where T : ICouchDocument, new() + { + var doc = new T {Id = documentId}; + try + { + ReadDocument(doc); + } + catch (CouchNotFoundException) + { + return default(T); + } + return doc; + } + + public CouchJsonDocument GetDocument(string documentId) + { + try + { + try + { + return new CouchJsonDocument(Request(documentId).Parse()); + } + catch (WebException e) + { + throw CouchException.Create("Failed to get document", e); + } + } + catch (CouchNotFoundException) + { + return null; + } + } + + public CouchQuery Query(string designName, string viewName) + { + return Query(new CouchViewDefinition(viewName, new DesignCouchDocument(designName, this))); + } + + public CouchQuery Query(CouchViewDefinition view) + { + return new CouchQuery(view); + } + + public CouchQuery QueryAllDocuments() + { + return Query(null, "_all_docs"); + } + + public void TouchView(string designDocumentId, string viewName) + { + Query(designDocumentId, viewName).Limit(0).GetResult(); + } + + public void DeleteDocument(ICouchDocument document) + { + DeleteDocument(document.Id, document.Rev); + } + + public ICouchDocument DeleteAttachment(ICouchDocument document) + { + JObject result = Request(document.Id + "/attachment").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; + } + + public void DeleteAttachment(string id, string rev) + { + Request(id + "/attachment").Query("?rev=" + rev).Delete().Check("Failed to delete attachment"); + } + + public void DeleteDocument(string id, string rev) + { + Request(id).Query("?rev=" + rev).Delete().Check("Failed to delete document"); + } + + /// + /// Delete documents in bulk fashion. + /// + /// List of documents to delete. + public void DeleteDocuments(IList documents) + { + DeleteDocuments(documents.ToArray()); + } + + /// + /// Delete documents in key range. This method needs to retrieve + /// revisions and then use them to post a bulk delete. Couch can not + /// delete documents without being told about their revisions. + /// + public void DeleteDocuments(string startKey, string endKey) + { + IList docs = QueryAllDocuments().StartKey(startKey).EndKey(endKey).GetResult().RowDocuments(); + DeleteDocuments(docs.ToArray()); + } + + /// + /// Delete documents in bulk fashion. + /// + /// Array of documents to delete. + public void DeleteDocuments(ICouchDocument[] documents) + { + DeleteDocuments(new CouchBulkDeleteDocuments(documents)); + } + + /// + /// Delete documents in bulk fashion. + /// + public void DeleteDocuments(ICanJson bulk) + { + try + { + var result = Request("_bulk_docs").Data(CouchDocument.WriteJson(bulk)).PostJson().Parse(); + for (int i = 0; i < result.Count(); i++) + { + //documents[i].id = (result[i])["id"].Value(); + //documents[i].rev = (result[i])["rev"].Value(); + if ((result[i])["error"] != null) + { + throw CouchException.Create(string.Format(CultureInfo.InvariantCulture, + "Document with id {0} was not deleted: {1}: {2}", + (result[i])["id"].Value(), (result[i])["error"], (result[i])["reason"])); + } + } + } + catch (WebException e) + { + throw CouchException.Create("Failed to bulk delete documents", e); + } + } + + public bool HasDocument(ICouchDocument document) + { + return HasDocument(document.Id); + } + + public bool HasAttachment(ICouchDocument document) + { + return HasAttachment(document.Id); + } + + public bool HasDocumentChanged(ICouchDocument document) + { + return HasDocumentChanged(document.Id, document.Rev); + } + + public bool HasDocumentChanged(string documentId, string rev) + { + return Request(documentId).Head().Send().Etag() != rev; + } + + public bool HasDocument(string documentId) + { + try + { + Request(documentId).Head().Send(); + return true; + } + catch (WebException) + { + return false; + } + } + + public bool HasAttachment(string documentId) + { + try + { + Request(documentId + "/attachment").Head().Send(); + return true; + } + catch (WebException) + { + return false; + } + } + } +} \ No newline at end of file diff --git a/src/CouchDocument.cs b/src/CouchDocument.cs new file mode 100644 index 0000000..279607d --- /dev/null +++ b/src/CouchDocument.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// This is a base class that domain objects can inherit in order to get + /// Id and Rev instance variables. You can also implement ICouchDocument yourself if + /// you are not free to pick this class as your base. Some static methods to read and write + /// CouchDB documents are also kept here. + /// + /// See sample subclasses to understand how to use this class + /// + public class CouchDocument : ICouchDocument + { + public CouchDocument(string id, string rev) + { + Id = id; + Rev = rev; + } + + public CouchDocument(string id) + { + Id = id; + } + + public CouchDocument() + { + } + + public CouchDocument(IDictionary doc) + : this(doc["_id"].Value(), doc["_rev"].Value()) + { + } + + #region ICouchDocument Members + + public string Id { get; set; } + public string Rev { get; set; } + + public virtual void WriteJson(JsonWriter writer) + { + WriteIdAndRev(this, writer); + } + + public virtual void ReadJson(JObject obj) + { + ReadIdAndRev(this, obj); + } + + #endregion + + public void WriteJsonObject(JsonWriter writer) + { + writer.WriteStartObject(); + WriteJson(writer); + writer.WriteEndObject(); + } + + public static string WriteJson(ICanJson doc) + { + var sb = new StringBuilder(); + using (JsonWriter jsonWriter = new JsonTextWriter(new StringWriter(sb, CultureInfo.InvariantCulture))) + { + //jsonWriter.Formatting = Formatting.Indented; + jsonWriter.WriteStartObject(); + doc.WriteJson(jsonWriter); + jsonWriter.WriteEndObject(); + string result = sb.ToString(); + return result; + } + } + + public static void WriteIdAndRev(ICouchDocument doc, JsonWriter writer) + { + if (doc.Id != null) + { + writer.WritePropertyName("_id"); + writer.WriteValue(doc.Id); + } + if (doc.Rev != null) + { + writer.WritePropertyName("_rev"); + writer.WriteValue(doc.Rev); + } + } + + public static void ReadIdAndRev(ICouchDocument doc, JObject obj) + { + doc.Id = obj["_id"].Value(); + doc.Rev = obj["_rev"].Value(); + } + + public static void ReadIdAndRev(ICouchDocument doc, JsonReader reader) + { + reader.Read(); + if (reader.TokenType == JsonToken.PropertyName && (reader.Value as string == "_id")) + { + reader.Read(); + doc.Id = reader.Value as string; + } + reader.Read(); + if (reader.TokenType == JsonToken.PropertyName && (reader.Value as string == "_rev")) + { + reader.Read(); + doc.Rev = reader.Value as string; + } + } + } +} \ No newline at end of file diff --git a/src/CouchException.cs b/src/CouchException.cs new file mode 100644 index 0000000..55c057c --- /dev/null +++ b/src/CouchException.cs @@ -0,0 +1,62 @@ +using System; +using System.Globalization; +using System.Net; +using System.Runtime.Serialization; + +namespace Divan +{ + /// + /// All Exceptions thrown inside Divan uses this class, MOST of these wrap a WebException + /// and we extract the HttpStatusCode to make it easily accessible. + /// + [Serializable] + public class CouchException : Exception + { + public HttpStatusCode StatusCode; + + public CouchException() + { + } + + public CouchException(string message) + : base(message) + { + } + + public CouchException(string message, Exception innerException) : base(message, innerException) + { + } + + protected CouchException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public static Exception Create(string message) + { + return new CouchException(message); + } + + public static Exception Create(string message, WebException e) + { + string msg = string.Format(CultureInfo.InvariantCulture, message + ": {0}", e.Message); + if (e.Response != null) + { + // Pick out status code + HttpStatusCode code = ((HttpWebResponse) e.Response).StatusCode; + + // Create any specific exceptions we care to use + if (code == HttpStatusCode.Conflict) + { + return new CouchConflictException(msg, e); + } + if (code == HttpStatusCode.NotFound) + { + return new CouchNotFoundException(msg, e); + } + } + + // Fall back on generic CouchException + return new CouchException(msg, e); + } + } +} \ No newline at end of file diff --git a/src/CouchGenericViewResult.cs b/src/CouchGenericViewResult.cs new file mode 100644 index 0000000..8af8c18 --- /dev/null +++ b/src/CouchGenericViewResult.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// This is a view result from a CouchQuery that can return CouchDocuments for + /// resulting documents (include_docs) and/or ICanJson documents for the + /// result values. A value returned from a CouchDB view does not need to be + /// a CouchDocument. + /// + public class CouchGenericViewResult : CouchViewResult + { + /// + /// Return all found values as documents of given type + /// + /// Type of value. + /// All found values. + public IList ValueDocuments() where T : ICanJson, new() + { + return RetrieveDocuments("value"); + } + + /// + /// Return first value found as document of given type. + /// + /// Type of value + /// First value found or null if not found. + public T ValueDocument() where T : ICanJson, new() + { + return RetrieveDocument("value"); + } + + /// + /// Return all found docs as documents of given type + /// + /// Type of documents. + /// List of documents found. + public IList Documents() where T : ICouchDocument, new() + { + return RetrieveDocuments("doc"); + } + + /// + /// Return first document found as document of given type + /// + /// Type of document + /// First document found or null if not found. + public T Document() where T : ICouchDocument, new() + { + return RetrieveDocument("doc"); + } + + protected virtual IList RetrieveDocuments(string docOrValue) where T : ICanJson, new() + { + var list = new List(); + foreach (JToken row in Rows()) + { + var doc = new T(); + doc.ReadJson(row[docOrValue].Value()); + list.Add(doc); + } + return list; + } + + protected virtual T RetrieveDocument(string docOrValue) where T : ICanJson, new() + { + foreach (JToken row in Rows()) + { + var doc = new T(); + doc.ReadJson(row[docOrValue].Value()); + return doc; + } + return default(T); + } + + public IList RowDocuments() + { + return RowDocuments(); + } + + public IList RowDocuments() where T : ICanJson, new() + { + var list = new List(); + foreach (JObject row in Rows()) + { + var doc = new T(); + doc.ReadJson(row); + list.Add(doc); + } + return list; + } + } +} \ No newline at end of file diff --git a/src/CouchJSONDocument.cs b/src/CouchJSONDocument.cs new file mode 100644 index 0000000..dae5662 --- /dev/null +++ b/src/CouchJSONDocument.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// A CouchDocument that holds its contents as a parsed JObject DOM which can be used + /// as a "light weight" base document instead of CouchDocument. + /// The _id and _rev are held inside the JObject. + /// + public class CouchJsonDocument : ICouchDocument + { + public CouchJsonDocument(string json, string id, string rev) + { + Obj = JObject.Parse(json); + Id = id; + Rev = rev; + } + + public CouchJsonDocument(string json, string id) + { + Obj = JObject.Parse(json); + Id = id; + } + + public CouchJsonDocument(string json) + { + Obj = JObject.Parse(json); + } + + public CouchJsonDocument(JObject doc) + { + Obj = doc; + } + + public CouchJsonDocument() + { + Obj = new JObject(); + } + + public JObject Obj { get; set; } + + #region ICouchDocument Members + + public void WriteJson(JsonWriter writer) + { + foreach (JToken token in Obj.Children()) + { + token.WriteTo(writer); + } + } + + // Presume that Obj has _id and _rev + public void ReadJson(JObject obj) + { + Obj = obj; + } + + public string Rev + { + get + { + if (Obj["_rev"] == null) + { + return null; + } + return Obj["_rev"].Value(); + } + set { Obj["_rev"] = JToken.FromObject(value); } + } + public string Id + { + get + { + if (Obj["_id"] == null) + { + return null; + } + return Obj["_id"].Value(); + } + set { Obj["_id"] = JToken.FromObject(value); } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/CouchNotFoundException.cs b/src/CouchNotFoundException.cs new file mode 100644 index 0000000..63c9903 --- /dev/null +++ b/src/CouchNotFoundException.cs @@ -0,0 +1,14 @@ +using System; + +namespace Divan +{ + /// + /// Represents a HttpStatusCode of 404, document not found. + /// + public class CouchNotFoundException : Exception + { + public CouchNotFoundException(string msg, Exception e) : base(msg, e) + { + } + } +} \ No newline at end of file diff --git a/src/CouchPermanentViewResult.cs b/src/CouchPermanentViewResult.cs new file mode 100644 index 0000000..66731b7 --- /dev/null +++ b/src/CouchPermanentViewResult.cs @@ -0,0 +1,9 @@ +namespace Divan +{ + /// + /// This is a view result from a CouchQuery on a permanent CouchDB view. + /// + public class CouchPermanentViewResult : CouchViewResult + { + } +} \ No newline at end of file diff --git a/src/CouchQuery.cs b/src/CouchQuery.cs new file mode 100644 index 0000000..d7f9600 --- /dev/null +++ b/src/CouchQuery.cs @@ -0,0 +1,212 @@ +using System.Collections.Generic; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// A view query with all its options. A CouchQuery is constructed to hold all query options that + /// CouchDB views support and to support ETag caching. + /// A CouchQuery object can be executed multiple times, holds the last result, the ETag for it, + /// and a reference to the CouchDatabase object used to perform the query. + /// + public class CouchQuery + { + public readonly CouchViewDefinition View; + + // Special options + public bool checkETagUsingHead; + public Dictionary Options = new Dictionary(); + public string postData; + public CouchViewResult Result; + + public CouchQuery(CouchViewDefinition view) + { + View = view; + } + + public void ClearOptions() + { + Options = new Dictionary(); + } + + public CouchQuery Data(string data) + { + postData = data; + return this; + } + + public CouchQuery Key(string value) + { + Options["key"] = "\"" + value + "\""; + return this; + } + + public CouchQuery Key(params object[] value) + { + Options["key"] = JArray.FromObject(value).ToString(); + return this; + } + + public CouchQuery StartKey(string value) + { + Options["startkey"] = "\"" + value + "\""; + return this; + } + + public CouchQuery StartKey(params object[] value) + { + Options["startkey"] = JArray.FromObject(value).ToString(); + return this; + } + + public CouchQuery StartKeyDocumentId(string value) + { + Options["startkey_docid"] = value; + return this; + } + + public CouchQuery EndKey(string value) + { + Options["endkey"] = "\"" + value + "\""; + return this; + } + + public CouchQuery EndKey(params object[] value) + { + Options["endkey"] = JArray.FromObject(value).ToString(); + return this; + } + + public CouchQuery EndKeyDocumentId(string value) + { + Options["endkey_docid"] = value; + return this; + } + + public CouchQuery Limit(int value) + { + Options["limit"] = value.ToString(); + return this; + } + + public CouchQuery Stale() + { + Options["stale"] = "ok"; + return this; + } + + public CouchQuery Descending() + { + Options["descending"] = "true"; + return this; + } + + public CouchQuery Skip(int value) + { + Options["skip"] = value.ToString(); + return this; + } + + public CouchQuery Group() + { + Options["group"] = "true"; + return this; + } + + public CouchQuery GroupLevel(int value) + { + Options["group_level"] = value.ToString(); + return this; + } + + public CouchQuery Reduce() + { + Options["reduce"] = "true"; + return this; + } + + public CouchQuery IncludeDocuments() + { + Options["include_docs"] = "true"; + return this; + } + + /// + /// Tell this query to do a HEAD request first to see + /// if ETag has changed and only then do the full request. + /// This is only interesting if you are reusing this query object. + /// + public CouchQuery CheckETagUsingHead() + { + checkETagUsingHead = true; + return this; + } + + public CouchGenericViewResult GetResult() + { + return GetResult(); + } + + public bool IsCachedAndValid() + { + // If we do not have a result it is not cached + if (Result == null) + { + return false; + } + CouchRequest req = View.Request().QueryOptions(Options); + req.Etag(Result.etag); + return req.Head().Send().IsETagValid(); + } + + public string String() + { + CouchRequest req = View.Request().QueryOptions(Options); + + if (postData != null) + { + req.Data(postData).Post(); + } + + return req.String(); + } + + public T GetResult() where T : CouchViewResult, new() + { + CouchRequest req = View.Request().QueryOptions(Options); + + if (postData != null) + { + req.Data(postData).Post(); + } + + if (Result == null) + { + Result = new T(); + } + else + { + // Tell the request what we already have + req.Etag(Result.etag); + if (checkETagUsingHead) + { + // Make a HEAD request to avoid transfer of data + if (req.Head().Send().IsETagValid()) + { + return (T) Result; + } + // Set back to GET before proceeding below + req.Get(); + } + } + + JObject json = req.Parse(); + if (json != null) // ETag did not match, view has changed + { + Result.Result(json); + Result.etag = req.Etag(); + } + return (T) Result; + } + } +} \ No newline at end of file diff --git a/src/CouchQueryDocument.cs b/src/CouchQueryDocument.cs new file mode 100644 index 0000000..e14ffef --- /dev/null +++ b/src/CouchQueryDocument.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// This is used to hold only metadata about a document retrieved from view queries. + /// + public class CouchQueryDocument : CouchDocument + { + public string Key { get; set; } + + public override void ReadJson(JObject obj) + { + Id = obj["id"].Value(); + Key = obj["key"].Value(); + Rev = (obj["value"].Value())["rev"].Value(); + } + } +} \ No newline at end of file diff --git a/src/CouchRequest.cs b/src/CouchRequest.cs new file mode 100644 index 0000000..8aab17f --- /dev/null +++ b/src/CouchRequest.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text; +using System.Web; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// A CouchDB HTTP request with all its options. This is where we do the actual HTTP requests to CouchDB. + /// + public class CouchRequest + { + private readonly CouchDatabase db; + private readonly CouchServer server; + private string etag, etagToCheck; + public Dictionary headers = new Dictionary(); + + // Query options + public string method = "GET"; // PUT, DELETE, POST, HEAD + public string mimeType; + public string path; + public string postData; + public string query; + + public JToken result; + + public CouchRequest(CouchServer server) + { + this.server = server; + } + + public CouchRequest(CouchDatabase db) + { + server = db.Server; + this.db = db; + } + + public CouchRequest Etag(string value) + { + etagToCheck = value; + headers["If-Modified"] = value; + return this; + } + + public CouchRequest Path(string name) + { + path = name; + return this; + } + + public CouchRequest Query(string name) + { + query = name; + return this; + } + + public CouchRequest QueryOptions(ICollection> options) + { + if (options == null || options.Count == 0) + { + return this; + } + + var sb = new StringBuilder(); + sb.Append("?"); + foreach (var q in options) + { + if (sb.Length > 1) + { + sb.Append("&"); + } + sb.Append(HttpUtility.UrlEncode(q.Key)); + sb.Append("="); + sb.Append(HttpUtility.UrlEncode(q.Value)); + } + + return Query(sb.ToString()); + } + + public CouchRequest Head() + { + method = "HEAD"; + return this; + } + + public CouchRequest PostJson() + { + MimeTypeJson(); + return Post(); + } + + public CouchRequest Post() + { + method = "POST"; + return this; + } + + public CouchRequest Get() + { + method = "GET"; + return this; + } + + public CouchRequest Put() + { + method = "PUT"; + return this; + } + + public CouchRequest Delete() + { + method = "DELETE"; + return this; + } + + public CouchRequest Data(string data) + { + postData = data; + return this; + } + + public CouchRequest MimeType(string type) + { + mimeType = type; + return this; + } + + public CouchRequest MimeTypeJson() + { + MimeType("application/json"); + return this; + } + + public JObject Result() + { + return (JObject) result; + } + + public T Result() where T : JToken + { + return (T) result; + } + + public string Etag() + { + return etag; + } + + public CouchRequest Check(string message) + { + try + { + if (result == null) + { + Parse(); + } + if (!result["ok"].Value()) + { + throw CouchException.Create(string.Format(CultureInfo.InvariantCulture, message + ": {0}", result)); + } + return this; + } + catch (WebException e) + { + throw CouchException.Create(message, e); + } + } + + private HttpWebRequest GetRequest() + { + Uri requestUri = new UriBuilder("http", server.Host, server.Port, ((db != null) ? db.Name + "/" : "") + path, query).Uri; + var request = WebRequest.Create(requestUri) as HttpWebRequest; + if (request == null) + { + throw CouchException.Create("Failed to create request"); + } + request.Timeout = 3600000; // 1 hour. May use System.Threading.Timeout.Infinite; + request.Method = method; + + if (mimeType != null) + { + request.ContentType = mimeType; + } + + if (postData != null) + { + byte[] bytes = Encoding.UTF8.GetBytes(postData); + request.ContentLength = bytes.Length; + using (Stream ps = request.GetRequestStream()) + { + ps.Write(bytes, 0, bytes.Length); + ps.Close(); + } + } + + Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Request: {0} Method: {1}", requestUri, method)); + return request; + } + + public JObject Parse() + { + return Parse(); + } + + public T Parse() where T : JToken + { + //var timer = new Stopwatch(); + //timer.Start(); + using (WebResponse response = GetResponse()) + { + using (Stream stream = response.GetResponseStream()) + { + using (var reader = new StreamReader(stream)) + { + using (var textReader = new JsonTextReader(reader)) + { + PickETag(response); + if (etagToCheck != null) + { + if (IsETagValid()) + { + return null; + } + } + result = JToken.ReadFrom(textReader); // We know it is a top level JSON JObject. + } + } + } + } + //timer.Stop(); + //Trace.WriteLine("Time for Couch HTTP & JSON PARSE: " + timer.ElapsedMilliseconds); + return (T) result; + } + + private void PickETag(WebResponse response) + { + etag = response.Headers["ETag"]; + if (etag != null) + { + etag = etag.EndsWith("\"") ? etag.Substring(1, etag.Length - 2) : etag; + } + } + + /// + /// Return the request as a plain string instead of trying to parse it. + /// + public string String() + { + using (WebResponse response = GetResponse()) + { + using (var reader = new StreamReader(response.GetResponseStream())) + { + PickETag(response); + if (etagToCheck != null) + { + if (IsETagValid()) + { + return null; + } + } + return reader.ReadToEnd(); + } + } + } + + private WebResponse GetResponse() + { + return GetRequest().GetResponse(); + } + + public CouchRequest Send() + { + using (WebResponse response = GetResponse()) + { + PickETag(response); + return this; + } + } + + public bool IsETagValid() + { + return etagToCheck == etag; + } + } +} \ No newline at end of file diff --git a/src/CouchServer.cs b/src/CouchServer.cs new file mode 100644 index 0000000..561c6ed --- /dev/null +++ b/src/CouchServer.cs @@ -0,0 +1,180 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text.RegularExpressions; +using Newtonsoft.Json; + +namespace Divan +{ + /// + /// A CouchServer is simply a communication end point holding a hostname and a port number to talk to. + /// It has an API to list, lookup, create or delete CouchDB "databases" in the CouchDB server. + /// One nice approach is to create a specific subclass that knows about its databases. + /// DatabasePrefix can be used to separate all databases created from other CouchDB databases. + /// + public class CouchServer + { + private const string DefaultHost = "192.168.9.205"; + private const int DefaultPort = 5984; + private readonly JsonSerializer serializer = new JsonSerializer(); + + public readonly string Host; + public readonly int Port; + + public string DatabasePrefix = ""; // Used by databases to prefix their names + + public CouchServer(string host, int port) + { + Host = host; + Port = port; + } + + public CouchServer(string host) + : this(host, DefaultPort) + { + } + + public CouchServer() + : this(DefaultHost, DefaultPort) + { + } + + public string ServerName + { + get { return Host + ":" + Port; } + } + + public CouchRequest Request() + { + return new CouchRequest(this); + } + + public bool HasDatabase(string name) + { + //return GetDatabaseNames().Contains(name); // This is too slow when we have thousands of dbs!!! + try + { + Request().Path(name).Head().Send(); + return true; + } + catch (WebException) + { + return false; + } + } + + /// + /// Get a CouchDatabase with given name. We create + /// the database if needed. + /// + public CouchDatabase GetDatabase(string name) + { + var db = new CouchDatabase(name, this); + db.Create(); + return db; + } + + /// + /// Get specialized subclass of CouchDatabase with given name. + /// We check if the database exists and delete it if it does, + /// then we recreate it. + /// + public CouchDatabase GetNewDatabase(string name) + { + var db = new CouchDatabase(name, this); + if (db.Exists()) + { + db.Delete(); + } + db.Create(); + return db; + } + + /// + /// Get specialized subclass of CouchDatabase. That class should + /// define its own database name. We presume it is already created. + /// + public T GetExistingDatabase() where T : CouchDatabase, new() + { + return new T {Server = this}; + } + + /// + /// Get specialized subclass of CouchDatabase with given name. + /// We presume it is already created. + /// + public T GetExistingDatabase(string name) where T : CouchDatabase, new() + { + return new T {Name = name, Server = this}; + } + + /// + /// Get specialized subclass of CouchDatabase. That class should + /// define its own database name. We ensure that it is created. + /// + public T GetDatabase() where T : CouchDatabase, new() + { + var db = GetExistingDatabase(); + db.Create(); + return db; + } + + /// + /// Get specialized subclass of CouchDatabase with given name. + /// We create the database if needed. + /// + public T GetDatabase(string name) where T : CouchDatabase, new() + { + var db = GetExistingDatabase(name); + db.Create(); + return db; + } + + public void CreateDatabase(string name) + { + try + { + Request().Path(name).Put().Check("Failed to create database"); + } + catch (WebException e) + { + throw CouchException.Create("Failed to create database", e); + } + } + + public void DeleteAllDatabases() + { + DeleteDatabases(".*"); + } + + public void DeleteDatabases(string regExp) + { + var reg = new Regex(regExp); + foreach (string name in GetDatabaseNames()) + { + if (reg.IsMatch(name)) + { + DeleteDatabase(name); + } + } + } + + public void DeleteDatabase(string name) + { + try + { + Request().Path(name).Delete().Check("Failed to delete database"); + } + catch (WebException e) + { + throw new CouchException("Failed to delete database", e); + } + } + + public IList GetDatabaseNames() + { + return (List) serializer.Deserialize(new JsonTextReader(new StringReader(Request().Path("_all_dbs").String())), typeof (List)); + } + } +} \ No newline at end of file diff --git a/src/CouchTest.cs b/src/CouchTest.cs new file mode 100644 index 0000000..3a4bbc3 --- /dev/null +++ b/src/CouchTest.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace Divan +{ + /// + /// Unit tests for Divan. Operates in a separate CouchDB database called divan_unit_tests. + /// + [TestFixture] + public class CouchTest + { + #region Setup/Teardown + + [SetUp] + public void SetUp() + { + server = new CouchServer(); + db = server.GetNewDatabase(DbName); + } + + [TearDown] + public void TearDown() + { + db.Delete(); + } + + #endregion + + private CouchServer server; + private CouchDatabase db; + private const string DbName = "divan_unit_tests"; + + [Test] + public void ShouldCheckChangedDocument() + { + CouchJsonDocument doc = db.CreateDocument("{\"CPU\": \"Intel\"}"); + CouchJsonDocument doc2 = db.GetDocument(doc.Id); + Assert.That(db.HasDocumentChanged(doc), Is.False); + doc2.Obj["CPU"] = JToken.FromObject("AMD"); + db.WriteDocument(doc2); + Assert.That(db.HasDocumentChanged(doc), Is.True); + } + + [Test] + public void ShouldCountDocuments() + { + Assert.That(db.CountDocuments(), Is.EqualTo(0)); + db.CreateDocument("{\"CPU\": \"Intel\"}"); + Assert.That(db.CountDocuments(), Is.EqualTo(1)); + } + + [Test] + public void ShouldCreateDocument() + { + var doc = new CouchJsonDocument("{\"CPU\": \"Intel\"}"); + ICouchDocument cd = db.CreateDocument(doc); + Assert.That(db.CountDocuments(), Is.EqualTo(1)); + Assert.That(cd.Id, Is.Not.Null); + Assert.That(cd.Rev, Is.Not.Null); + } + + [Test] + public void ShouldCreateDocuments() + { + const string doc = "{\"CPU\": \"Intel\"}"; + var doc1 = new CouchJsonDocument(doc); + var doc2 = new CouchJsonDocument(doc); + IList list = new List {doc1, doc2}; + db.SaveDocuments(list, true); + Assert.That(db.CountDocuments(), Is.EqualTo(2)); + Assert.That(doc1.Id, Is.Not.Null); + Assert.That(doc1.Rev, Is.Not.Null); + Assert.That(doc2.Id, Is.Not.Null); + Assert.That(doc2.Rev, Is.Not.Null); + Assert.That(doc1.Id, Is.Not.EqualTo(doc2.Id)); + } + + [Test, ExpectedException(typeof (CouchNotFoundException))] + public void ShouldDeleteDatabase() + { + db.Delete(); + Assert.That(server.HasDatabase(db.Name), Is.EqualTo(false)); + server.DeleteDatabase(db.Name); // one more time should fail + } + + [Test] + public void ShouldDeleteDocuments() + { + const string doc = "{\"CPU\": \"Intel\"}"; + CouchJsonDocument doc1 = db.CreateDocument(doc); + CouchJsonDocument doc2 = db.CreateDocument(doc); + if (String.Compare(doc1.Id, doc2.Id) < 0) + { + db.DeleteDocuments(doc1.Id, doc2.Id); + } + else + { + db.DeleteDocuments(doc2.Id, doc1.Id); + } + Assert.That(db.HasDocument(doc1.Id), Is.False); + Assert.That(db.HasDocument(doc2.Id), Is.False); + } + + [Test, ExpectedException(typeof (CouchException))] + public void ShouldFailCreateDatabase() + { + server.CreateDatabase(db.Name); // one more time should fail + } + + [Test] + public void ShouldGetDatabaseNames() + { + bool result = server.GetDatabaseNames().Contains(db.Name); + Assert.That(result, Is.EqualTo(true)); + } + + [Test] + public void ShouldGetDocument() + { + const string doc = "{\"CPU\": \"Intel\"}"; + CouchJsonDocument oldDoc = db.CreateDocument(doc); + CouchJsonDocument newDoc = db.GetDocument(oldDoc.Id); + Assert.That(oldDoc.Id, Is.EqualTo(newDoc.Id)); + Assert.That(oldDoc.Rev, Is.EqualTo(newDoc.Rev)); + } + + [Test] + public void ShouldGetDocuments() + { + const string doc = "{\"CPU\": \"Intel\"}"; + CouchJsonDocument doc1 = db.CreateDocument(doc); + CouchJsonDocument doc2 = db.CreateDocument(doc); + var ids = new List {doc1.Id, doc2.Id}; + IList docs = db.GetDocuments(ids); + Assert.That(doc1.Id, Is.EqualTo(docs.First().Id)); + Assert.That(doc2.Id, Is.EqualTo(docs.Last().Id)); + } + + [Test] + public void ShouldReturnNullWhenNotFound() + { + var doc = db.GetDocument("jadda"); + Assert.That(doc, Is.Null); + CouchJsonDocument doc2 = db.GetDocument("jadda"); + Assert.That(doc2, Is.Null); + } + + [Test] + public void ShouldSaveDocumentWithId() + { + var doc = new CouchJsonDocument("{\"_id\":\"123\", \"CPU\": \"Intel\"}"); + ICouchDocument cd = db.SaveDocument(doc); + Assert.That(db.CountDocuments(), Is.EqualTo(1)); + Assert.That(cd.Id, Is.Not.Null); + Assert.That(cd.Rev, Is.Not.Null); + } + + [Test] + public void ShouldSaveDocumentWithoutId() + { + var doc = new CouchJsonDocument("{\"CPU\": \"Intel\"}"); + ICouchDocument cd = db.SaveDocument(doc); + Assert.That(db.CountDocuments(), Is.EqualTo(1)); + Assert.That(cd.Id, Is.Not.Null); + Assert.That(cd.Rev, Is.Not.Null); + } + + [Test] + 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); + } + + [Test, ExpectedException(typeof (CouchConflictException))] + public void ShouldThrowConflictExceptionOnAlreadyExists() + { + const string doc = "{\"CPU\": \"Intel\"}"; + CouchJsonDocument doc1 = db.CreateDocument(doc); + var doc2 = new CouchJsonDocument(doc) {Id = doc1.Id}; + db.WriteDocument(doc2); + } + + [Test, ExpectedException(typeof (CouchConflictException))] + public void ShouldThrowConflictExceptionOnStaleWrite() + { + const string doc = "{\"CPU\": \"Intel\"}"; + CouchJsonDocument doc1 = db.CreateDocument(doc); + CouchJsonDocument doc2 = db.GetDocument(doc1.Id); + doc1.Obj["CPU"] = JToken.FromObject("AMD"); + db.SaveDocument(doc1); + doc2.Obj["CPU"] = JToken.FromObject("Via"); + db.SaveDocument(doc2); + } + + [Test] + public void ShouldUseETagForView() + { + var design = new DesignCouchDocument("computers", db); + design.AddView("by_cpumake", + @"function(doc) { + emit(doc.CPU, doc); + }"); + db.WriteDocument(design); + + CouchJsonDocument doc1 = db.CreateDocument("{\"CPU\": \"Intel\"}"); + db.CreateDocument("{\"CPU\": \"AMD\"}"); + db.CreateDocument("{\"CPU\": \"Via\"}"); + db.CreateDocument("{\"CPU\": \"Sparq\"}"); + + CouchQuery query = db.Query("computers", "by_cpumake").StartKey("Intel").EndKey("Via").CheckETagUsingHead(); + // Query has no result yet so should not be cached + Assert.That(query.IsCachedAndValid(), Is.False); + query.GetResult(); + // Now it is cached and should be valid + Assert.That(query.IsCachedAndValid(), Is.True); + // Make a change invalidating the view + db.SaveDocument(doc1); + // It should now be false + Assert.That(query.IsCachedAndValid(), Is.False); + query.GetResult(); + // And now it should be cached again + Assert.That(query.IsCachedAndValid(), Is.True); + query.GetResult(); + // Still cached of course + Assert.That(query.IsCachedAndValid(), Is.True); + } + + [Test] + public void ShouldWriteDocument() + { + var doc = new CouchJsonDocument("{\"_id\":\"123\", \"CPU\": \"Intel\"}"); + ICouchDocument cd = db.WriteDocument(doc); + Assert.That(db.CountDocuments(), Is.EqualTo(1)); + Assert.That(cd.Id, Is.Not.Null); + Assert.That(cd.Rev, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/src/CouchViewDefinition.cs b/src/CouchViewDefinition.cs new file mode 100644 index 0000000..769674f --- /dev/null +++ b/src/CouchViewDefinition.cs @@ -0,0 +1,85 @@ +using Newtonsoft.Json; + +namespace Divan +{ + /// + /// A definition of a CouchDB view with a name, a map and a reduce function and a reference to the + /// owning DesignCouchDocument. + /// + public class CouchViewDefinition + { + /// + /// Constructor used to create "on the fly" definitions, like for example for "_all_docs". + /// + /// View name used in URI. + /// A design doc, can also be created on the fly. + public CouchViewDefinition(string name, DesignCouchDocument doc) + { + Doc = doc; + Name = name; + } + + /// + /// Constructor used for permanent views, see CouchDesignDocument. + /// + /// View name. + /// Map function. + /// Optional reduce function. + /// Parent document. + public CouchViewDefinition(string name, string map, string reduce, DesignCouchDocument doc) + { + Doc = doc; + Name = name; + Map = map; + Reduce = reduce; + } + + public DesignCouchDocument Doc { get; set; } + public string Name { get; set; } + public string Map { get; set; } + public string Reduce { get; set; } + + public CouchRequest Request() + { + return Doc.Owner.Request(Path()); + } + + public CouchDatabase Db() + { + return Doc.Owner; + } + + public void WriteJson(JsonWriter writer) + { + writer.WritePropertyName(Name); + writer.WriteStartObject(); + writer.WritePropertyName("map"); + writer.WriteValue(Map); + if (Reduce != null) + { + writer.WritePropertyName("reduce"); + writer.WriteValue(Reduce); + } + writer.WriteEndObject(); + } + + public CouchQuery Query() + { + return Doc.Owner.Query(this); + } + + public void Touch() + { + Query().Limit(0).GetResult(); + } + + public string Path() + { + if (Doc.Id == "_design/") + { + return Name; + } + return Doc.Id + "/_view/" + Name; + } + } +} \ No newline at end of file diff --git a/src/CouchViewResult.cs b/src/CouchViewResult.cs new file mode 100644 index 0000000..b31ec5e --- /dev/null +++ b/src/CouchViewResult.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// This is a view result from a CouchQuery. The result is returned as JSON + /// from CouchDB and parsed into a JObject by Newtonsoft.Json. A view result + /// also includes some meta information and this class has methods to access these. + /// Typically you use a subclass. + /// + public class CouchViewResult + { + public string etag; + public JObject result; + + public void Result(JObject obj) + { + result = obj; + } + + public int Count() + { + return result["total_rows"].Value(); + } + + public int Offset() + { + return result["offset"].Value(); + } + + public JEnumerable Rows() + { + return result["rows"].Children(); + } + } +} \ No newline at end of file diff --git a/src/CustomDictionary.xml b/src/CustomDictionary.xml new file mode 100644 index 0000000..9b13063 --- /dev/null +++ b/src/CustomDictionary.xml @@ -0,0 +1,468 @@ + + + + + + cb + ch + csc + elem + gt + idx + img + lg + multi + num + ps + pw + scp + si + sig + tk + tw + val + + + + json + accessor + accessors + acos + aes + aptca + arg + args + asin + asm + aspx + async + atan + baml + bcl + bindable + bitrate + blittable + blog + bool + bootstrapper + bootstrappers + browsable + cacheability + callee + callees + canonicalize + cdecl + cdo + chtml + cim + cloneable + clr + clr's + cls + clsid + clsids + cmd + cmdlet + cmdlets + comparand + concat + config + contravariant + cookieless + cos + crm + css + cyclomatic + debuggable + decommission + deformatter + delegator + dequeue + dereferenced + des + deserialization + deserialize + deserialized + deserializing + dhcp + discardable + dll + dns + documentable + dsig + dtd + em + email + emails + emf + encodable + endian + enqueue + enum + enums + expando + finalizer + finalizers + fixup + fixups + formattable + func + guid + guids + hashtable + hashtables + hashtable's + hdc + hijri + href + iis + il + ime + initializer + initializers + int + interop + intrinsics + ipv + iterator + iterators + jit + ldap + linq + localhost + loopback + loopbacks + mapper + mappers + marshaler + marshalers + mdi + mergable + misc + miscased + monitorable + oks + mscoree + mscorlib + msh + multiline + multipanel + multipanels + multiview + multiviews + mutator + mutators + mutex + mutexes + ndpsec + nls + nop + ntfs + ntlm + nullable + obj + odbc + overridable + pageable + parameterless + pdb + persistable + playlist + pragma + prepend + prog + ptr + queryable + ras + rect + rects + recurse + refactor + reg + regex + remoted + remoting + representable + res + resolver + resolvers + rethrow + rethrows + rijndael + rpc + rtc + rva + sdl + searchspace + searchspaces + seekable + seq + serializable + serializer + serializers + smtp + specifier + specifiers + spline + sql + ssl + sta + stickies + struct + structs + subaddress + subaddresses + subclass + subclasses + subdirectories + subdirectory + subexpression + subexpressions + subitem + subitems + subkey + subkeys + submenu + submenus + subpath + subpaths + subsegment + subsegments + subtree + subtrees + tcp + templated + thunk + thunks + tlb + tuple + tuples + udp + udt + unboxing + uncategorize + unindent + uninitialize + uninitialized + uninstantiated + unmaintainable + unmarshal + unregister + unregistering + unregisters + unregistration + unrepresentable + unterminated + untrusted + uri + uris + url + urls + utc + utf + validator + vsa + weblog + wiki + wcf + wmf + wmi + wml + wpf + wql + wsdl + xaml + xhtml + xmlns + xor + xrml + xsd + xsi + xsl + xslt + + + + complus + cancelled + indices + login + logout + signon + signoff + writeable + cant + arent + dont + doesnt + didnt + couldnt + wouldnt + shouldnt + wont + havent + hasnt + hadnt + isnt + wasnt + werent + flag + flags + + + + datastore + datastores + dataset + datasets + textbox + textboxes + codepage + codepages + checkbox + checkboxes + pushbutton + pushbuttons + dropdown + dropdowns + toolbar + toolbars + scrollbar + scrollbars + bitflag + bitflags + filename + filenames + fileserver + fileservers + username + usernames + hostname + hostnames + fieldname + fieldnames + pathname + pathnames + whitespace + whitespaces + logon + logons + logoff + logoffs + signin + signins + signout + signouts + frontend + frontends + backend + backends + sitemap + sitemaps + datatype + datatypes + designtime + designtimes + readonly + truetype + netbios + autodetect + autodetects + autoscroll + autoscrolls + autocomplete + autocompletes + autosave + autosaves + javascript + jscript + voiceview + appletalk + mapinfo + newline + newlines + qword + qwords + keyset + keysets + + + + onset + inset + byname + setout + countertype + editor + longtime + drawstring + hookup + cleanup + breakout + setline + maybe + nods + classis + gettable + inform + beset + settable + standalone + threadlike + infield + infields + meantime + mackey + jscript + ipv + tooltip + tooltips + indispose + + + + + Pi + Na + NESW + NWSE + Json + + + diff --git a/src/DesignCouchDocument.cs b/src/DesignCouchDocument.cs new file mode 100644 index 0000000..6722026 --- /dev/null +++ b/src/DesignCouchDocument.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// A named design document in CouchDB. Holds CouchViewDefinitions. + /// + public class DesignCouchDocument : CouchDocument + { + public IList Definitions = new List(); + public string Language = "javascript"; + public CouchDatabase Owner; + + public DesignCouchDocument(string documentId, CouchDatabase owner) + : base("_design/" + documentId) + { + Owner = owner; + } + + /// + /// Add view without a reduce function. + /// + /// Name of view + /// Map function + /// + public CouchViewDefinition AddView(string name, string map) + { + return AddView(name, map, null); + } + + /// + /// Add view with a reduce function. + /// + /// Name of view + /// Map function + /// Reduce function + /// + public CouchViewDefinition AddView(string name, string map, string reduce) + { + var def = new CouchViewDefinition(name, map, reduce, this); + Definitions.Add(def); + return def; + } + + public override void WriteJson(JsonWriter writer) + { + WriteIdAndRev(this, writer); + writer.WritePropertyName("language"); + writer.WriteValue(Language); + writer.WritePropertyName("views"); + writer.WriteStartObject(); + foreach (CouchViewDefinition definition in Definitions) + { + definition.WriteJson(writer); + } + writer.WriteEndObject(); + } + + public override void ReadJson(JObject obj) + { + ReadIdAndRev(this, obj); + } + } +} \ No newline at end of file diff --git a/src/Divan.FxCop b/src/Divan.FxCop new file mode 100644 index 0000000..a3875f0 --- /dev/null +++ b/src/Divan.FxCop @@ -0,0 +1,307 @@ + + + + True + c:\sandbox\monitor2\thirdparty\tools\fxcop\Xml\FxCopReport.xsl + + + + + + True + True + True + 10 + 1 + + False + + False + 120 + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'Divan.dll' + + + + + + + + + + + + + 'CouchDatabase.GetAllDocuments()' + + + + + + + + + 'CouchDatabase.GetDocument<T>(string)' + 'T' + + + + + + + + + 'CouchDatabase.GetResultWithOptions<T>(string, string, Dictionary<string, string>)' + 'T' + + + + + + + + + 'CouchDatabase.GetView<T>(string, string)' + 'T' + + + + + + + + + 'CouchDatabase.GetView<T>(string, string, string)' + 'T' + + + + + + + + + 'CouchDatabase.GetView<T>(string, string, string, string)' + 'T' + + + + + + + + + + + + + 'CouchGenericViewResult.Document<T>()' + 'T' + + + + + + + + + 'CouchGenericViewResult.Documents<T>()' + 'T' + + + + + + + + + 'CouchGenericViewResult.RetrieveDocument<T>(string)' + 'T' + + + + + + + + + 'CouchGenericViewResult.RetrieveDocuments<T>(string)' + 'T' + + + + + + + + + 'CouchGenericViewResult.ValueDocument<T>()' + 'T' + + + + + + + + + 'CouchGenericViewResult.ValueDocuments<T>()' + 'T' + + + + + + + + + + + + + 'CouchQuery.GetResult<T>()' + 'T' + + + + + + + + + + + + + 'CouchServer.GetDatabaseNames()' + + + + + + + + + 'url' + 'CouchServer.Request(CouchDatabase, string, string)' + + + + + + + + + 'url' + 'CouchServer.Request(CouchDatabase, string, string, string, string)' + + + + + + + + + 'url' + 'CouchServer.RequestStream(CouchDatabase, string, string, string, string)' + + + + + + + + + + + + + SetUp + 'CouchTest.SetUp()' + Setup + + + + + + + + + + + + + + TearDown + 'CouchTest.TearDown()' + Teardown + + + + + + + + + + + + + + + + + + + + + NUnit + NUnit + + + + + Sign {0} with a strong name key. + + + The compound word '{0}' in member name {1} exists as a discrete term. If your usage is intended to be single word, case it as '{2}' or strip the first token entirely if it represents any sort of Hungarian notation. + + + Consider a design where {0} doesn't require explicit type parameter {1} in any call to it. + + + Change the type of parameter {0} of method {1} from string to System.Uri, or provide an overload of {1}, that allows {0} to be passed as a System.Uri object. + + + Change {0} to a property if appropriate. + + + + diff --git a/src/Divan.csproj b/src/Divan.csproj new file mode 100644 index 0000000..fe5cc85 --- /dev/null +++ b/src/Divan.csproj @@ -0,0 +1,80 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {37AC0B66-5340-4B81-BC62-3EE80233A011} + Library + Properties + Divan + Divan + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\lib\Newtonsoft.Json.dll + + + False + ..\lib\nunit.framework.dll + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ICanJSON.cs b/src/ICanJSON.cs new file mode 100644 index 0000000..02d7d4f --- /dev/null +++ b/src/ICanJSON.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Divan +{ + /// + /// Basic capability to write and read myself using Newtonsoft.JSON. + /// Writing is done using JsonWriter in a fast streaming fashion. + /// Reading is done using JObject "DOM style". + /// + public interface ICanJson + { + void WriteJson(JsonWriter writer); + void ReadJson(JObject obj); + } +} \ No newline at end of file diff --git a/src/ICouchDocument.cs b/src/ICouchDocument.cs new file mode 100644 index 0000000..d6b8999 --- /dev/null +++ b/src/ICouchDocument.cs @@ -0,0 +1,14 @@ +namespace Divan +{ + /// + /// An ICouchDocument needs to have a Rev and an Id. It also needs to implement ICanJson + /// which means it can read and write itself as JSON. Either you let your domain objects + /// that you want to store in CouchDB implement this interface or if you are free to pick + /// your own base class you can subclass from CouchDocument (or even CouchJsonDocument). + /// + public interface ICouchDocument : ICanJson + { + string Rev { get; set; } + string Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dc865a7 --- /dev/null +++ b/src/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("Divan")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Foretagsplatsen AB")] +[assembly: AssemblyProduct("Divan")] +[assembly: AssemblyCopyright("Copyright © Foretagsplatsen AB 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly: Guid("83fd2c08-3765-4908-a901-a9ce4f6d05c0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file