[couchdb-glib/wip/query-response] introduce couchdb_session_execute_query
- From: Krzysztof Klimonda <kklimonda src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [couchdb-glib/wip/query-response] introduce couchdb_session_execute_query
- Date: Wed, 19 Jan 2011 12:55:24 +0000 (UTC)
commit 1ae46a976b25081ae4d638f79f9ac499e7eb1a5b
Author: Krzysztof Klimonda <kklimonda syntaxhighlighted com>
Date: Wed Jan 19 13:31:09 2011 +0100
introduce couchdb_session_execute_query
couchdb-glib/couchdb-session.c | 287 ++++++++++++++++++++++++++++++----------
couchdb-glib/couchdb-session.h | 10 ++-
2 files changed, 223 insertions(+), 74 deletions(-)
---
diff --git a/couchdb-glib/couchdb-session.c b/couchdb-glib/couchdb-session.c
index c995751..f0706f8 100644
--- a/couchdb-glib/couchdb-session.c
+++ b/couchdb-glib/couchdb-session.c
@@ -70,6 +70,14 @@ static void debug_message (const gchar *log_domain,
const gchar *message, gpointer user_data);
#endif
+static void add_oauth_signature (CouchdbSession *session,
+ SoupMessage *http_message,
+ const char *method, const char *url);
+
+static JsonNode *parse_json_response (CouchdbSession * session,
+ SoupMessage * http_message,
+ GError ** error);
+
static gboolean _session_authenticate(SoupSession *session,
SoupMessage *msg,
SoupAuth *auth,
@@ -197,6 +205,12 @@ couchdb_session_init (CouchdbSession *session)
#endif
}
+GQuark
+couchdb_session_error_quark ()
+{
+ return g_quark_from_static_string ("couchdb-session-error-quark");
+}
+
/**
* couchdb_session_new:
* @uri: URI of the CouchDB instance to connect to
@@ -232,6 +246,187 @@ couchdb_session_get_uri (CouchdbSession *session)
}
/**
+ * couchdb_session_execute_query
+ * @self: A #CouchdbSession object
+ * @query: #CouchdbQuery object describing the query
+ * @error: Placeholder for error information
+ *
+ * This method executes the given query on the database and returns
+ * a #CouchdbResponse object which keeps the response returned by CouchDB.
+ *
+ * Return value: (transfer full): A #CouchdbResponse object with the response
+ * from the server, or %NULL if there was an error, in which case @error
+ * will be set approprately.
+ */
+CouchdbResponse *
+couchdb_session_execute_query (CouchdbSession *self, CouchdbQuery *query,
+ GError **error)
+{
+ const gchar *path, *query_string, *method, *content_type, *etag;
+ gchar *uri, *full_uri, *body, *encoded_dbname;
+ JsonNode *node;
+ JsonObject *object;
+ CouchdbResponse *response;
+ SoupMessage *message;
+ guint status;
+ gsize content_length;
+
+ g_return_val_if_fail (COUCHDB_IS_SESSION (self), NULL);
+ g_return_val_if_fail (COUCHDB_IS_QUERY (query), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ node = NULL;
+ object = NULL;
+ body = NULL;
+ response = NULL;
+
+ path = couchdb_query_get_path (query);
+
+ /* to prevent creating uri with two slashes check if first character
+ of path is slash and, if it is, skip it. */
+ uri = g_strconcat (self->priv->uri, "/",
+ path ? (*path == '/' ? path+1 : path) : "", NULL);
+
+ query_string = couchdb_query_get_query_options_string (query);
+ if (query_string != NULL) {
+ full_uri = g_strdup_printf ("%s?%s", uri, query_string);
+
+ g_free (uri);
+
+ uri = full_uri;
+ }
+
+ method = couchdb_query_get_method (query);
+
+ object = couchdb_query_get_json_object (query);
+ if (object != NULL) {
+ JsonGenerator *generator;
+ JsonNode *node;
+
+ node = json_node_new (JSON_NODE_OBJECT);
+ json_node_set_object (node, object);
+
+ generator = json_generator_new ();
+ json_generator_set_root (generator, node);
+ body = json_generator_to_data (generator, NULL);
+
+ g_object_unref (generator);
+ json_node_free (node);
+ }
+
+ message = soup_message_new (method, uri);
+ if (body != NULL) {
+ soup_message_set_request (message, "application/json",
+ SOUP_MEMORY_COPY, body, strlen (body));
+ }
+
+ if (couchdb_session_is_authentication_enabled (self)) {
+ CouchdbCredentialsType cred_type;
+ CouchdbCredentials *credentials;
+
+ credentials = self->priv->credentials;
+ cred_type = couchdb_credentials_get_auth_type (credentials);
+
+ switch (cred_type) {
+ case COUCHDB_CREDENTIALS_TYPE_OAUTH:
+ add_oauth_signature (self, message, method, uri);
+ break;
+ default:
+ g_warning ("Unknown credentials object, not authenticating");
+ break;
+ }
+ }
+
+#ifdef DEBUG_MESSAGES
+ g_debug ("Sending %s to %s with headers: ", method, uri);
+ soup_message_headers_foreach (message->request_headers,
+ (SoupMessageHeadersForeachFunc)
+ debug_print_headers, NULL);
+#endif
+
+ status = soup_session_send_message (self->priv->http_session, message);
+ g_debug ("status is %u", status);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ const char *tmp;
+ GError *inner_error;
+ SoupMessageHeaders *headers;
+
+ inner_error = NULL;
+
+ /* node can be NULL (when there was no response) so we
+ have to pass new GError location and then deal with it */
+ node = parse_json_response (self, message, &inner_error);
+ if (inner_error != NULL) {
+ g_propagate_error (error, inner_error);
+ goto cleanup;
+ }
+
+ /* I'm going to burn in hell for it.. or person responsible
+ for returning dynamic results from database based on
+ query. It would be much simpler if we have used JsonBuilder
+ and JsonReader in our API.
+ In case of returned result being an array enclose it in
+ JsonObject with one member called "array" */
+ if (node && JSON_NODE_TYPE (node) == JSON_NODE_ARRAY) {
+ JsonArray *array;
+
+ object = json_object_new ();
+ array = json_node_dup_array (node);
+ json_object_set_array_member (object, "array", array);
+ } else if (node) {
+ object = json_node_dup_object (node);
+ } else {
+ object = NULL;
+ }
+
+ headers = message->response_headers;
+
+ etag = soup_message_headers_get_one (headers, "Etag");
+ content_type =
+ soup_message_headers_get_one (headers, "Content-Type");
+
+ tmp = soup_message_headers_get_one (headers, "Content-Length");
+ if (tmp) {
+ content_length = g_ascii_strtoll (tmp, NULL, 10);
+ }
+
+ response = g_object_new (COUCHDB_TYPE_RESPONSE,
+ "response", object,
+ "etag", etag,
+ "status-code", status,
+ "content-type", content_type,
+ "content-length", content_length, NULL);
+ } else {
+ uint error_enum;
+ const char *message;
+ switch (status) {
+ case 409:
+ error_enum = COUCHDB_SESSION_ERROR_CONFLICT;
+ message = "Document Conflict";
+ break;
+ default:
+ error_enum = COUCHDB_SESSION_ERROR_UNKNOWN;
+ message = "Unknown Error";
+ break;
+ }
+ g_set_error_literal (error, COUCHDB_SESSION_ERROR,
+ error_enum, message);
+ }
+
+cleanup:
+ g_free (uri);
+ g_free (body);
+ if (node)
+ json_node_free (node);
+ if (object)
+ json_object_unref (object);
+ g_object_unref (message);
+
+ return response;
+}
+
+/**
* couchdb_session_list_databases:
* @session: A #CouchdbSession object
* @error: Placeholder for error information
@@ -655,14 +850,22 @@ add_oauth_signature (CouchdbSession *session, SoupMessage *http_message, const c
#endif /* HAVE_OAUTH */
}
-static gboolean
-parse_json_response (CouchdbSession *session, JsonParser *json_parser, SoupMessage *http_message, GError **error)
+static JsonNode *
+parse_json_response (CouchdbSession * session, SoupMessage * http_message,
+ GError ** error)
{
SoupBuffer *buffer;
GString *str = NULL;
goffset offset = 0;
gboolean success = TRUE;
-
+ JsonParser *parser;
+ JsonNode *node;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ node = NULL;
+ parser = json_parser_new ();
+
while ((buffer = soup_message_body_get_chunk (http_message->response_body, offset))) {
if (!str)
str = g_string_new ("");
@@ -676,87 +879,25 @@ parse_json_response (CouchdbSession *session, JsonParser *json_parser, SoupMessa
#ifdef DEBUG_MESSAGES
g_debug ("Response body: %s", str->str);
#endif
- if (!json_parser_load_from_data (json_parser,
+ if (!json_parser_load_from_data (parser,
(const gchar *) str->str,
str->len,
error)) {
- g_object_unref (G_OBJECT (json_parser));
- g_set_error (error, COUCHDB_ERROR, -1, "Invalid JSON response");
+ g_set_error (error,
+ COUCHDB_SESSION_ERROR,
+ COUCHDB_SESSION_ERROR_INVALID_JSON,
+ "Invalid JSON response");
success = FALSE;
}
g_string_free (str, TRUE);
- }
- return success;
-}
-
-/**
- * couchdb_session_send_message:
- * @session: A #CouchdbSession object
- * @method: HTTP method to use
- * @url: URL to send the message to
- * @body: Body of the HTTP request
- * @output: Placeholder for output information
- * @error: Placeholder for error information
- *
- * This function is used to communicate with CouchDB over HTTP, and should not be used
- * by applications unless they really have a need (like missing API in couchdb-glib which
- * the application needs).
- *
- * Return value: TRUE if successful, FALSE otherwise.
- */
-gboolean
-couchdb_session_send_message (CouchdbSession *session, const char *method, const char *url, const char *body, JsonParser *output, GError **error)
-{
- SoupMessage *http_message;
- guint status;
- GError **real_error;
-
- g_return_val_if_fail (COUCHDB_IS_SESSION (session), FALSE);
- g_return_val_if_fail (method != NULL, FALSE);
-
- if (error != NULL)
- real_error = error;
- else
- real_error = NULL;
-
- http_message = soup_message_new (method, url);
- if (body != NULL) {
- soup_message_set_request (http_message, "application/json", SOUP_MEMORY_COPY,
- body, strlen (body));
+ node = json_node_copy (json_parser_get_root (parser));
}
- if (couchdb_session_is_authentication_enabled (session)) {
- switch (couchdb_credentials_get_auth_type (session->priv->credentials)) {
- case COUCHDB_CREDENTIALS_TYPE_OAUTH:
- add_oauth_signature (session, http_message, method, url);
- break;
- default:
- g_warning ("Got unknown credentials object, not authenticating message");
- }
- }
-
-#ifdef DEBUG_MESSAGES
- g_debug ("Sending %s to %s... with headers: ", method, url);
- soup_message_headers_foreach (http_message->request_headers,
- (SoupMessageHeadersForeachFunc) debug_print_headers,
- NULL);
-#endif
- status = soup_session_send_message (session->priv->http_session, http_message);
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- if (output != NULL)
- parse_json_response (session, output, http_message, real_error);
- g_object_unref (G_OBJECT (http_message));
-
- return TRUE;
- } else {
- if (error != NULL)
- g_set_error (error, COUCHDB_ERROR, status, "%s", http_message->reason_phrase);
- g_object_unref (G_OBJECT (http_message));
+ g_object_unref (G_OBJECT (parser));
- return FALSE;
- }
+ return node;
}
#ifdef DEBUG_MESSAGES
diff --git a/couchdb-glib/couchdb-session.h b/couchdb-glib/couchdb-session.h
index e65c852..116b901 100644
--- a/couchdb-glib/couchdb-session.h
+++ b/couchdb-glib/couchdb-session.h
@@ -26,9 +26,11 @@
#include <glib.h>
#include <glib-object.h>
+#include <json-glib/json-glib.h>
#include "couchdb-types.h"
#include "couchdb-credentials.h"
-#include "couchdb-database-info.h"
+#include "couchdb-response.h"
+#include "couchdb-query.h"
G_BEGIN_DECLS
@@ -39,6 +41,8 @@ G_BEGIN_DECLS
#define COUCHDB_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COUCHDB_TYPE_SESSION))
#define COUCHDB_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), COUCHDB_TYPE_SESSION, CouchdbSessionClass))
+#define COUCHDB_SESSION_ERROR (couchdb_session_error_quark ())
+
typedef struct _CouchdbSessionPrivate CouchdbSessionPrivate;
typedef struct _CouchdbDatabase CouchdbDatabase;
@@ -67,6 +71,10 @@ GSList *couchdb_session_list_databases (CouchdbSession *session, GE
void couchdb_session_free_database_list (GSList *dblist);
CouchdbDatabaseInfo *couchdb_session_get_database_info (CouchdbSession *session, const char *dbname, GError **error);
+CouchdbResponse *couchdb_session_execute_query (CouchdbSession * self,
+ CouchdbQuery * query,
+ GError ** error);
+
CouchdbDatabase *couchdb_session_get_database (CouchdbSession *session, const char *dbname, GError **error);
gboolean couchdb_session_create_database (CouchdbSession *session, const char *dbname, GError **error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]