Permalink
Browse files

NCBC-165: Provide support for view does not exist scenarios

Currently, when a view doesn't exist an InvalidOperationException
bubbles up to the caller.  New approach provides CheckExists method
of view, which may be called prior to iterating over view or new
typed exceptions which provide details of the error.

Change-Id: Iedec5543e3eb096815fcbd77a764675d8ed5fded
Reviewed-on: http://review.couchbase.org/24068
Reviewed-by: Matt Ingenthron <matt@couchbase.com>
Reviewed-by: Saakshi Manocha <saakshi.manocha@globallogic.com>
Tested-by: Saakshi Manocha <saakshi.manocha@globallogic.com>
  • Loading branch information...
1 parent 6eeab0d commit 66df6552345ef067d994cc8f0c81ae4b7ec946b1 @johnzablocki johnzablocki committed with saakshimanocha Jan 18, 2013
@@ -3,6 +3,7 @@
using System.Linq;
using System.Text;
using NUnit.Framework;
+using Couchbase.Exceptions;
namespace Couchbase.Tests
{
@@ -54,5 +55,139 @@ public void When_Querying_View_With_Debug_False_Debug_Info_Dictionary_Is_Null()
Assert.That(view.DebugInfo, Is.Null);
}
+
+ /// <summary>
+ /// @test: Check design document for non-existent view, expect false from IView.ViewExists
+ /// @pre: Default configuration to initialize client in app.config and have missing view with design document cities and view name by_postal_code
+ /// @post: Test passes if false is returned
+ /// </summary>
+ [Test]
+ public void When_Checking_For_View_That_Does_Not_Exist_Check_Exists_Returns_False()
+ {
+ var view = _Client.GetView("cities", "by_postal_code");
+ var exists = view.CheckExists();
+ Assert.That(exists, Is.False);
+ }
+
+ /// <summary>
+ /// @test: Check design document for non-existent view, expect false from IView.ViewExists
+ /// @pre: Default configuration to initialize client in app.config and have missing view with design document cities and view name by_postal_code
+ /// @post: Test passes if false is returned
+ /// </summary>
+ [Test]
+ public void When_Checking_For_View_That_Exists_Check_Exists_Returns_True()
+ {
+ var view = _Client.GetView("cities", "by_name");
+ var exists = view.CheckExists();
+ Assert.That(exists, Is.True);
+ }
+
+ /// <summary>
+ /// @test: Check that ViewNotFoundException is returned when querying a bad view name
+ /// @pre: Default configuration to initialize client in app.config and have missing view with design document cities and view name by_postal_code
+ /// @post: Test passes if exception is thrown
+ /// </summary>
+ [Test]
+ [ExpectedException(typeof(ViewNotFoundException))]
+ public void When_Querying_A_View_That_Does_Not_Exist_In_A_Design_Doc_That_Does_Exist_View_Not_Found_Exception_Is_Thrown()
+ {
+ var view = _Client.GetView("cities", "by_postal_code");
+ view.Count();
+ }
+
+ /// <summary>
+ /// @test: Check that ViewNotFoundException is returned when querying a bad view name
+ /// @pre: Default configuration to initialize client in app.config and have missing view with design document cities and view name by_postal_code
+ /// @post: Test passes if exception is thrown and string contains not_found
+ /// </summary>
+ [Test]
+ public void When_Querying_A_View_That_Does_Not_Exist_In_A_Design_Doc_That_Does_Exist_Exception_Contains_Not_Found_Error()
+ {
+ try
+ {
+ var view = _Client.GetView("cities", "by_postal_code");
+ view.Count();
+ }
+ catch (ViewNotFoundException e)
+ {
+ Assert.That(e.Reason, Is.StringContaining("not_found"));
+ return;
+ }
+
+ Assert.Fail();
+ }
+
+ /// <summary>
+ /// @test: Check that ViewNotFoundException is returned when querying a bad view name
+ /// @pre: Default configuration to initialize client in app.config and have missing view with design document cities and view name by_postal_code
+ /// @post: Test passes if exception is thrown with proper messages
+ /// </summary>
+ [Test]
+ [ExpectedException(typeof(ViewNotFoundException))]
+ public void When_Querying_A_View_In_A_Design_Doc_That_Does_Not_Exist_View_Not_Found_Exception_Is_Thrown()
+ {
+ var view = _Client.GetView("states", "by_name");
+ view.Count();
+ }
+
+ /// <summary>
+ /// @test: Check that ViewNotFoundException is returned when querying a bad view name
+ /// @pre: Default configuration to initialize client in app.config and have missing view with design document cities and view name by_postal_code
+ /// @post: Test passes if exception is thrown
+ /// </summary>
+ [Test]
+ public void When_Querying_A_View_That_In_A_Design_Doc_That_Does_Not_Exist_Exception_Contains_Not_Found_Error()
+ {
+ try
+ {
+ var view = _Client.GetView("states", "by_postal_code");
+ view.Count();
+ }
+ catch (ViewNotFoundException e)
+ {
+ Assert.That(e.Error, Is.StringContaining("not_found"));
+ return;
+ }
+
+ Assert.Fail();
+ }
+
+ /// <summary>
+ /// @test: Check that ViewException is returned when querying a view with bad params
+ /// @pre: Default configuration to initialize client in app.config and have design document cities
+ /// and view name by_name without a reduce function
+ /// @post: ViewException is thrown
+ /// </summary>
+ [Test]
+ [ExpectedException(typeof(ViewException))]
+ public void When_Providing_Invalid_Parameters_To_An_Existing_View_A_View_Exception_Is_Thrown()
+ {
+ var view = _Client.GetView("cities", "by_name").Group(true);
+ view.Count();
+ }
+
+ /// <summary>
+ /// @test: Check that ViewException is returned with error and reason when querying a view with bad params
+ /// @pre: Default configuration to initialize client in app.config and have design document cities
+ /// and view name by_name without a reduce function
+ /// @post: ViewException is thrown with error and reason
+ /// </summary>
+ [Test]
+ public void When_Providing_Invalid_Parameters_To_An_Existing_View_A_View_Exception_Is_Thrown_And_Contains_Error_And_Reason()
+ {
+ try
+ {
+ var view = _Client.GetView("cities", "by_name").Group(true);
+ view.Count();
+ }
+ catch (ViewException e)
+ {
+ Assert.That(e.Reason, Is.Not.Null.Or.Empty);
+ Assert.That(e.Message, Is.Not.Null.Or.Empty);
+ return;
+ }
+
+ Assert.Fail();
+ }
}
}
@@ -80,6 +80,8 @@
<Compile Include="CouchbaseSpatialView.cs" />
<Compile Include="CouchbaseSpatialViewBase.cs" />
<Compile Include="CouchbaseViewHandler.cs" />
+ <Compile Include="Exceptions\ViewExceptions.cs" />
+ <Compile Include="Exceptions\ViewNotFoundException.cs" />
<Compile Include="Extensions\CouchbaseClientExtensions.cs" />
<Compile Include="GenericViewRowTransformer.cs" />
<Compile Include="Helpers\DocHelper.cs" />
@@ -188,6 +188,11 @@ public IView<T> Debug(bool debug)
return new PagedView<T>(this, pageSize, pagedViewIdProperty, pagedViewKeyProperty);
}
+ public bool CheckExists()
+ {
+ return ViewHandler.CheckViewExists();
+ }
+
#endregion
#region IEnumerable<IViewRow> Members
@@ -7,11 +7,14 @@
using System.IO;
using Newtonsoft.Json.Linq;
using Couchbase.Helpers;
+using Couchbase.Exceptions;
namespace Couchbase
{
internal class CouchbaseViewHandler
{
+ private const string ERROR_VIEW_NOT_FOUND = "not_found";
+
protected static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(CouchbaseViewHandler));
public ICouchbaseClient Client { get; private set; }
@@ -36,7 +39,6 @@ internal CouchbaseViewHandler(ICouchbaseClient client, IHttpClientLocator client
public IEnumerator<T> TransformResults<T>(Func<JsonReader, T> rowTransformer, IDictionary<string, string> viewParams)
{
var response = GetResponse(viewParams);
- Debug.Assert(response != null);
using (var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true))
using (var jsonReader = new JsonTextReader(sr))
@@ -57,6 +59,26 @@ public IEnumerator<T> TransformResults<T>(Func<JsonReader, T> rowTransformer, ID
{
TotalRows = Convert.ToInt32(jsonReader.Value);
}
+ else if (jsonReader.Value as string == "error" && jsonReader.Read())
+ {
+ var error = jsonReader.Value as string;
+ var reason = "";
+ while (jsonReader.Read())
+ {
+ if (jsonReader.TokenType == JsonToken.PropertyName && jsonReader.Value as string == "reason" && jsonReader.Read())
+ {
+ reason = jsonReader.Value.ToString();
+ }
+ }
+
+ //When requesting a bad design document, the response will be a 404 with the error == "not_found"
+ //When requesting a bad view name and a valid design doc, response will be a 500 with a reason containing "not_found"
+ if (error == ERROR_VIEW_NOT_FOUND || reason.Contains(ERROR_VIEW_NOT_FOUND))
+ {
+ throw new ViewNotFoundException(DesignDocument, IndexName, error, reason);
+ }
+ throw new ViewException(DesignDocument, IndexName, error, reason);
+ }
else if (jsonReader.Value as string == "rows" && jsonReader.Read())
{
// position the reader on the first "rows" field which contains the actual resultset
@@ -91,6 +113,35 @@ public IEnumerator<T> TransformResults<T>(Func<JsonReader, T> rowTransformer, ID
}
}
+ public bool CheckViewExists()
+ {
+ var client = ClientLocator.Locate(DesignDocument);
+ var request = client.CreateRequest(this.DesignDocument + "/");
+ var response = request.GetResponse();
+
+ using (var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true))
+ using (var jsonReader = new JsonTextReader(sr))
+ {
+ while (jsonReader.Read())
+ {
+ if (jsonReader.TokenType == JsonToken.PropertyName
+ && jsonReader.Depth == 1
+ && ((string)jsonReader.Value) == "views")
+ {
+ while (jsonReader.Read())
+ {
+ if (jsonReader.TokenType == JsonToken.PropertyName && (string)jsonReader.Value == IndexName)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
protected static IEnumerable<string> FormatErrors(object[] list)
{
if (list == null || list.Length == 0)
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Couchbase.Exceptions
+{
+ /// <summary>
+ /// Standard exception class thrown on when view errors are encountered
+ /// </summary>
+ public class ViewException : Exception
+ {
+ public string Error { get; set; }
+
+ public string Reason { get; set; }
+
+ public ViewException(string designDoc, string viewName, string error, string reason) :
+ base(string.Format("Query failed for view {0} in design document {1}", viewName, designDoc))
+ {
+ Error = error;
+ Reason = reason;
+ }
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * @author Couchbase <info@couchbase.com>
+ * @copyright 2013 Couchbase, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Couchbase.Exceptions
+{
+ /// <summary>
+ /// Exception thrown when non-existent view is queried
+ /// </summary>
+ public class ViewNotFoundException : ViewException
+ {
+ public ViewNotFoundException(string designDoc, string viewName, string error, string reason) :
+ base(viewName, designDoc, error, reason) {}
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * @author Couchbase <info@couchbase.com>
+ * @copyright 2013 Couchbase, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
@@ -110,8 +110,6 @@ public void ExecuteWith(RestClient client)
this.response = client.Execute(request);
if (response.ErrorException != null) throw response.ErrorException;
- if (response.StatusCode != System.Net.HttpStatusCode.OK)
- throw new InvalidOperationException(String.Format("Server returned {0}: {1}, {2}", response.StatusCode, response.StatusDescription, response.Content));
}
Stream IHttpResponse.GetResponseStream()

0 comments on commit 66df655

Please sign in to comment.