Blame gdata/gdata-batch-operation.c

Packit 4b6dd7
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Packit 4b6dd7
/*
Packit 4b6dd7
 * GData Client
Packit 4b6dd7
 * Copyright (C) Philip Withnall 2010 <philip@tecnocode.co.uk>
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is free software; you can redistribute it and/or
Packit 4b6dd7
 * modify it under the terms of the GNU Lesser General Public
Packit 4b6dd7
 * License as published by the Free Software Foundation; either
Packit 4b6dd7
 * version 2.1 of the License, or (at your option) any later version.
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is distributed in the hope that it will be useful,
Packit 4b6dd7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4b6dd7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 4b6dd7
 * Lesser General Public License for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * You should have received a copy of the GNU Lesser General Public
Packit 4b6dd7
 * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * SECTION:gdata-batch-operation
Packit 4b6dd7
 * @short_description: GData batch operation object
Packit 4b6dd7
 * @stability: Stable
Packit 4b6dd7
 * @include: gdata/gdata-batch-operation.h
Packit 4b6dd7
 *
Packit 4b6dd7
 * #GDataBatchOperation is a transient standalone class which represents and handles a single batch operation request to a service. To make a batch
Packit 4b6dd7
 * operation request: create a new #GDataBatchOperation; add the required queries, insertions, updates and deletions to the operation using
Packit 4b6dd7
 * gdata_batch_operation_add_query(), gdata_batch_operation_add_insertion(), gdata_batch_operation_add_update() and
Packit 4b6dd7
 * gdata_batch_operation_add_deletion(), respectively; run the request with gdata_batch_operation_run() or gdata_batch_operation_run_async(); and
Packit 4b6dd7
 * handle the results in the callback functions which are invoked by the operation as the results are received and parsed.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If authorization is required for any of the requests in the batch operation, the #GDataService set in #GDataBatchOperation:service must have
Packit 4b6dd7
 * a #GDataAuthorizer set as its #GDataService:authorizer property, and that authorizer must be authorized for the #GDataAuthorizationDomain set
Packit 4b6dd7
 * in #GDataBatchOperation:authorization-domain. It's not possible for requests in a single batch operation to be authorized under multiple domains;
Packit 4b6dd7
 * in that case, the requests must be split up across several batch operations using different authorization domains.
Packit 4b6dd7
 *
Packit 4b6dd7
 * If all of the requests in the batch operation don't require authorization (i.e. they all operate on public data; see the documentation for the
Packit 4b6dd7
 * #GDataService subclass in question's operations for details of which require authorization), #GDataBatchOperation:authorization-domain can be set
Packit 4b6dd7
 * to %NULL to save the overhead of sending authorization data to the online service.
Packit 4b6dd7
 *
Packit 4b6dd7
 * <example>
Packit 4b6dd7
 * 	<title>Running a Synchronous Operation</title>
Packit 4b6dd7
 * 	<programlisting>
Packit 4b6dd7
 *	guint op_id, op_id2;
Packit 4b6dd7
 *	GDataBatchOperation *operation;
Packit 4b6dd7
 *	GDataContactsContact *contact;
Packit 4b6dd7
 *	GDataService *service;
Packit 4b6dd7
 *	GDataAuthorizationDomain *domain;
Packit 4b6dd7
 *
Packit 4b6dd7
 *	service = create_contacts_service ();
Packit 4b6dd7
 *	domain = get_authorization_domain_from_service (service);
Packit 4b6dd7
 *	contact = create_new_contact ();
Packit 4b6dd7
 *	batch_link = gdata_feed_look_up_link (contacts_feed, GDATA_LINK_BATCH);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	operation = gdata_batchable_create_operation (GDATA_BATCHABLE (service), domain, gdata_link_get_uri (batch_link));
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* Add to the operation to insert a new contact and query for another one */
Packit 4b6dd7
 *	op_id = gdata_batch_operation_add_insertion (operation, GDATA_ENTRY (contact), insertion_cb, user_data);
Packit 4b6dd7
 *	op_id2 = gdata_batch_operation_add_query (operation, gdata_entry_get_id (other_contact), GDATA_TYPE_CONTACTS_CONTACT, query_cb, user_data);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	g_object_unref (contact);
Packit 4b6dd7
 *	g_object_unref (domain);
Packit 4b6dd7
 *	g_object_unref (service);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	/* Run the operations in a blocking fashion. Ideally, check and free the error as appropriate after running the operation. */
Packit 4b6dd7
 *	gdata_batch_operation_run (operation, NULL, &error);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	g_object_unref (operation);
Packit 4b6dd7
 *
Packit 4b6dd7
 *	static void
Packit 4b6dd7
 *	insertion_cb (guint operation_id, GDataBatchOperationType operation_type, GDataEntry *entry, GError *error, gpointer user_data)
Packit 4b6dd7
 *	{
Packit 4b6dd7
 *		/* operation_id == op_id, operation_type == GDATA_BATCH_OPERATION_INSERTION */
Packit 4b6dd7
 *
Packit 4b6dd7
 *		/* Process the new inserted entry, ideally after checking for errors. Note that the entry should be reffed if it needs to stay
Packit 4b6dd7
 *		 * alive after execution of the callback finishes. */
Packit 4b6dd7
 *		process_inserted_entry (entry, user_data);
Packit 4b6dd7
 *	}
Packit 4b6dd7
 *
Packit 4b6dd7
 *	static void
Packit 4b6dd7
 *	query_cb (guint operation_id, GDataBatchOperationType operation_type, GDataEntry *entry, GError *error, gpointer user_data)
Packit 4b6dd7
 *	{
Packit 4b6dd7
 *		/* operation_id == op_id2, operation_type == GDATA_BATCH_OPERATION_QUERY */
Packit 4b6dd7
 *
Packit 4b6dd7
 *		/* Process the results of the query, ideally after checking for errors. Note that the entry should be reffed if it needs to
Packit 4b6dd7
 *		 * stay alive after execution of the callback finishes. */
Packit 4b6dd7
 *		process_queried_entry (entry, user_data);
Packit 4b6dd7
 *	}
Packit 4b6dd7
 * 	</programlisting>
Packit 4b6dd7
 * </example>
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
#include <config.h>
Packit 4b6dd7
#include <glib.h>
Packit 4b6dd7
#include <glib/gi18n-lib.h>
Packit 4b6dd7
#include <string.h>
Packit 4b6dd7
Packit 4b6dd7
#include "gdata-batch-operation.h"
Packit 4b6dd7
#include "gdata-batch-feed.h"
Packit 4b6dd7
#include "gdata-batchable.h"
Packit 4b6dd7
#include "gdata-private.h"
Packit 4b6dd7
#include "gdata-batch-private.h"
Packit 4b6dd7
Packit 4b6dd7
static void operation_free (BatchOperation *op);
Packit 4b6dd7
Packit 4b6dd7
static void gdata_batch_operation_dispose (GObject *object);
Packit 4b6dd7
static void gdata_batch_operation_finalize (GObject *object);
Packit 4b6dd7
static void gdata_batch_operation_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
Packit 4b6dd7
static void gdata_batch_operation_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
Packit 4b6dd7
Packit 4b6dd7
struct _GDataBatchOperationPrivate {
Packit 4b6dd7
	GDataService *service;
Packit 4b6dd7
	GDataAuthorizationDomain *authorization_domain;
Packit 4b6dd7
	gchar *feed_uri;
Packit 4b6dd7
	GHashTable *operations;
Packit 4b6dd7
	guint next_id; /* next available operation ID */
Packit 4b6dd7
	gboolean has_run; /* TRUE if the operation has been run already (though it does not necessarily have to have finished running) */
Packit 4b6dd7
	gboolean is_async; /* TRUE if the operation was run with *_run_async(); FALSE if run with *_run() */
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
enum {
Packit 4b6dd7
	PROP_SERVICE = 1,
Packit 4b6dd7
	PROP_FEED_URI,
Packit 4b6dd7
	PROP_AUTHORIZATION_DOMAIN,
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
G_DEFINE_TYPE (GDataBatchOperation, gdata_batch_operation, G_TYPE_OBJECT)
Packit 4b6dd7
#define GDATA_BATCH_OPERATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDATA_TYPE_BATCH_OPERATION, GDataBatchOperationPrivate))
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_batch_operation_class_init (GDataBatchOperationClass *klass)
Packit 4b6dd7
{
Packit 4b6dd7
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit 4b6dd7
Packit 4b6dd7
	g_type_class_add_private (klass, sizeof (GDataBatchOperationPrivate));
Packit 4b6dd7
Packit 4b6dd7
	gobject_class->dispose = gdata_batch_operation_dispose;
Packit 4b6dd7
	gobject_class->finalize = gdata_batch_operation_finalize;
Packit 4b6dd7
	gobject_class->get_property = gdata_batch_operation_get_property;
Packit 4b6dd7
	gobject_class->set_property = gdata_batch_operation_set_property;
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataBatchOperation:service:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The service this batch operation is attached to.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.7.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_SERVICE,
Packit 4b6dd7
	                                 g_param_spec_object ("service",
Packit 4b6dd7
	                                                      "Service", "The service this batch operation is attached to.",
Packit 4b6dd7
	                                                      GDATA_TYPE_SERVICE,
Packit 4b6dd7
	                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataBatchOperation:authorization-domain:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The authorization domain for the batch operation, against which the #GDataService:authorizer for the #GDataBatchOperation:service should be
Packit 4b6dd7
	 * authorized. This may be %NULL if authorization is not needed for any of the requests in the batch operation.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * All requests in the batch operation must be authorizable under this single authorization domain. If requests need different authorization
Packit 4b6dd7
	 * domains, they must be performed in different batch operations.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.9.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_AUTHORIZATION_DOMAIN,
Packit 4b6dd7
	                                 g_param_spec_object ("authorization-domain",
Packit 4b6dd7
	                                                      "Authorization domain", "The authorization domain for the batch operation.",
Packit 4b6dd7
	                                                      GDATA_TYPE_AUTHORIZATION_DOMAIN,
Packit 4b6dd7
	                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
Packit 4b6dd7
	/**
Packit 4b6dd7
	 * GDataBatchOperation:feed-uri:
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * The feed URI that this batch operation will be sent to.
Packit 4b6dd7
	 *
Packit 4b6dd7
	 * Since: 0.7.0
Packit 4b6dd7
	 */
Packit 4b6dd7
	g_object_class_install_property (gobject_class, PROP_FEED_URI,
Packit 4b6dd7
	                                 g_param_spec_string ("feed-uri",
Packit 4b6dd7
	                                                      "Feed URI", "The feed URI that this batch operation will be sent to.",
Packit 4b6dd7
	                                                      NULL,
Packit 4b6dd7
	                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_batch_operation_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataBatchOperationPrivate *priv = GDATA_BATCH_OPERATION_GET_PRIVATE (object);
Packit 4b6dd7
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
		case PROP_SERVICE:
Packit 4b6dd7
			g_value_set_object (value, priv->service);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_AUTHORIZATION_DOMAIN:
Packit 4b6dd7
			g_value_set_object (value, priv->authorization_domain);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_FEED_URI:
Packit 4b6dd7
			g_value_set_string (value, priv->feed_uri);
Packit 4b6dd7
			break;
Packit 4b6dd7
		default:
Packit 4b6dd7
			/* We don't have any other property... */
Packit 4b6dd7
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
			break;
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_batch_operation_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataBatchOperationPrivate *priv = GDATA_BATCH_OPERATION_GET_PRIVATE (object);
Packit 4b6dd7
Packit 4b6dd7
	switch (property_id) {
Packit 4b6dd7
		case PROP_SERVICE:
Packit 4b6dd7
			priv->service = g_value_dup_object (value);
Packit 4b6dd7
			break;
Packit 4b6dd7
		/* Construct only */
Packit 4b6dd7
		case PROP_AUTHORIZATION_DOMAIN:
Packit 4b6dd7
			priv->authorization_domain = g_value_dup_object (value);
Packit 4b6dd7
			break;
Packit 4b6dd7
		case PROP_FEED_URI:
Packit 4b6dd7
			priv->feed_uri = g_value_dup_string (value);
Packit 4b6dd7
			break;
Packit 4b6dd7
		default:
Packit 4b6dd7
			/* We don't have any other property... */
Packit 4b6dd7
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
Packit 4b6dd7
			break;
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_batch_operation_init (GDataBatchOperation *self)
Packit 4b6dd7
{
Packit 4b6dd7
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_BATCH_OPERATION, GDataBatchOperationPrivate);
Packit 4b6dd7
	self->priv->next_id = 1; /* reserve ID 0 for error conditions */
Packit 4b6dd7
	self->priv->operations = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) operation_free);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_batch_operation_dispose (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataBatchOperationPrivate *priv = GDATA_BATCH_OPERATION_GET_PRIVATE (object);
Packit 4b6dd7
Packit 4b6dd7
	if (priv->authorization_domain != NULL)
Packit 4b6dd7
		g_object_unref (priv->authorization_domain);
Packit 4b6dd7
	priv->authorization_domain = NULL;
Packit 4b6dd7
Packit 4b6dd7
	if (priv->service != NULL)
Packit 4b6dd7
		g_object_unref (priv->service);
Packit 4b6dd7
	priv->service = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to the parent class */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_batch_operation_parent_class)->dispose (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
gdata_batch_operation_finalize (GObject *object)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataBatchOperationPrivate *priv = GDATA_BATCH_OPERATION_GET_PRIVATE (object);
Packit 4b6dd7
Packit 4b6dd7
	g_free (priv->feed_uri);
Packit 4b6dd7
	g_hash_table_destroy (priv->operations);
Packit 4b6dd7
Packit 4b6dd7
	/* Chain up to the parent class */
Packit 4b6dd7
	G_OBJECT_CLASS (gdata_batch_operation_parent_class)->finalize (object);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_get_service:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GDataBatchOperation:service property.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer none): the batch operation's attached service
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataService *
Packit 4b6dd7
gdata_batch_operation_get_service (GDataBatchOperation *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), NULL);
Packit 4b6dd7
	return self->priv->service;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_get_authorization_domain:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GDataBatchOperation:authorization-domain property.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: (transfer none) (allow-none): the #GDataAuthorizationDomain used to authorize the batch operation, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.9.0
Packit 4b6dd7
 */
Packit 4b6dd7
GDataAuthorizationDomain *
Packit 4b6dd7
gdata_batch_operation_get_authorization_domain (GDataBatchOperation *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), NULL);
Packit 4b6dd7
Packit 4b6dd7
	return self->priv->authorization_domain;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_get_feed_uri:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 *
Packit 4b6dd7
 * Gets the #GDataBatchOperation:feed-uri property.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the batch operation's feed URI
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
const gchar *
Packit 4b6dd7
gdata_batch_operation_get_feed_uri (GDataBatchOperation *self)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), NULL);
Packit 4b6dd7
	return self->priv->feed_uri;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Add an operation to the list of operations to be executed when the #GDataBatchOperation is run, and return its operation ID */
Packit 4b6dd7
static guint
Packit 4b6dd7
add_operation (GDataBatchOperation *self, GDataBatchOperationType type, GDataEntry *entry, GDataBatchOperationCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	BatchOperation *op;
Packit 4b6dd7
Packit 4b6dd7
	/* Create the operation */
Packit 4b6dd7
	op = g_slice_new0 (BatchOperation);
Packit 4b6dd7
	op->id = (self->priv->next_id++);
Packit 4b6dd7
	op->type = type;
Packit 4b6dd7
	op->callback = callback;
Packit 4b6dd7
	op->user_data = user_data;
Packit 4b6dd7
	op->entry = g_object_ref (entry);
Packit 4b6dd7
Packit 4b6dd7
	/* Add the operation to the table */
Packit 4b6dd7
	g_hash_table_insert (self->priv->operations, GUINT_TO_POINTER (op->id), op);
Packit 4b6dd7
Packit 4b6dd7
	return op->id;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * _gdata_batch_operation_get_operation:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @id: the operation ID
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return the #BatchOperation for the given operation ID.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: the relevant #BatchOperation, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
BatchOperation *
Packit 4b6dd7
_gdata_batch_operation_get_operation (GDataBatchOperation *self, guint id)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), NULL);
Packit 4b6dd7
	g_return_val_if_fail (id > 0, NULL);
Packit 4b6dd7
Packit 4b6dd7
	return g_hash_table_lookup (self->priv->operations, GUINT_TO_POINTER (id));
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Run a user-supplied callback for a #BatchOperation whose return value we've just processed. This is designed to be used in an idle handler, so
Packit 4b6dd7
 * that the callback is run in the main thread. It can be called if the user-supplied callback is %NULL (e.g. in the case that the callback's been
Packit 4b6dd7
 * called before). */
Packit 4b6dd7
static gboolean
Packit 4b6dd7
run_callback_cb (BatchOperation *op)
Packit 4b6dd7
{
Packit 4b6dd7
	if (op->callback != NULL)
Packit 4b6dd7
		op->callback (op->id, op->type, op->entry, op->error, op->user_data);
Packit 4b6dd7
Packit 4b6dd7
	/* Unset the callback so that it can't be called again */
Packit 4b6dd7
	op->callback = NULL;
Packit 4b6dd7
Packit 4b6dd7
	return FALSE;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * _gdata_batch_operation_run_callback:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @op: the #BatchOperation which has been finished
Packit 4b6dd7
 * @entry: (allow-none): the entry representing the operation's result, or %NULL
Packit 4b6dd7
 * @error: the error from the operation, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Run the callback for @op to notify the user code that the operation's result has been received and processed. Either @entry or @error should be
Packit 4b6dd7
 * set (and the other should be %NULL), signifying a successful operation or a failed operation, respectively.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The function will call @op's user-supplied callback, if available, in either the current or the main thread, depending on whether the
Packit 4b6dd7
 * #GDataBatchOperation was run with gdata_batch_operation_run() or gdata_batch_operation_run_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
_gdata_batch_operation_run_callback (GDataBatchOperation *self, BatchOperation *op, GDataEntry *entry, GError *error)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_BATCH_OPERATION (self));
Packit 4b6dd7
	g_return_if_fail (op != NULL);
Packit 4b6dd7
	g_return_if_fail (entry == NULL || GDATA_IS_ENTRY (entry));
Packit 4b6dd7
	g_return_if_fail (entry == NULL || error == NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* We can free the request data, and replace it with the response data */
Packit 4b6dd7
	g_free (op->query_id);
Packit 4b6dd7
	op->query_id = NULL;
Packit 4b6dd7
	if (op->entry != NULL)
Packit 4b6dd7
		g_object_unref (op->entry);
Packit 4b6dd7
	if (entry != NULL)
Packit 4b6dd7
		g_object_ref (entry);
Packit 4b6dd7
	op->entry = entry;
Packit 4b6dd7
	op->error = error;
Packit 4b6dd7
Packit 4b6dd7
	/* Don't bother scheduling run_callback_cb() if there is no callback to run */
Packit 4b6dd7
	if (op->callback == NULL)
Packit 4b6dd7
		return;
Packit 4b6dd7
Packit 4b6dd7
	/* Only dispatch it in the main thread if the request was run with *_run_async(). This allows applications to run batch operations entirely in
Packit 4b6dd7
	 * application-owned threads if desired. */
Packit 4b6dd7
	if (self->priv->is_async == TRUE) {
Packit 4b6dd7
		/* Send the callback; use G_PRIORITY_DEFAULT rather than G_PRIORITY_DEFAULT_IDLE
Packit 4b6dd7
		 * to contend with the priorities used by the callback functions in GAsyncResult */
Packit 4b6dd7
		g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) run_callback_cb, op, NULL);
Packit 4b6dd7
	} else {
Packit 4b6dd7
		run_callback_cb (op);
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Free a #BatchOperation */
Packit 4b6dd7
static void
Packit 4b6dd7
operation_free (BatchOperation *op)
Packit 4b6dd7
{
Packit 4b6dd7
	g_free (op->query_id);
Packit 4b6dd7
	if (op->entry != NULL)
Packit 4b6dd7
		g_object_unref (op->entry);
Packit 4b6dd7
	if (op->error != NULL)
Packit 4b6dd7
		g_error_free (op->error);
Packit 4b6dd7
Packit 4b6dd7
	g_slice_free (BatchOperation, op);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_add_query:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @id: the ID of the entry being queried for
Packit 4b6dd7
 * @entry_type: the type of the entry which will be returned
Packit 4b6dd7
 * @callback: (scope async): a #GDataBatchOperationCallback to call when the query is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Add a query to the #GDataBatchOperation, to be executed when the operation is run. The query will return a #GDataEntry (of subclass type
Packit 4b6dd7
 * @entry_type) representing the given entry @id. The ID is of the same format as that returned by gdata_entry_get_id().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that a single batch operation should not operate on a given #GDataEntry more than once, as there's no guarantee about the order in which the
Packit 4b6dd7
 * batch operation's operations will be performed.
Packit 4b6dd7
 *
Packit 4b6dd7
 * @callback will be called when the #GDataBatchOperation is run with gdata_batch_operation_run() (in which case it will be called in the thread which
Packit 4b6dd7
 * ran the batch operation), or with gdata_batch_operation_run_async() (in which case it will be called in an idle handler in the main thread). The
Packit 4b6dd7
 * @operation_id passed to the callback will match the return value of gdata_batch_operation_add_query(), and the @operation_type will be
Packit 4b6dd7
 * %GDATA_BATCH_OPERATION_QUERY. If the query was successful, the resulting entry will be passed to the callback function as @entry, and @error will
Packit 4b6dd7
 * be %NULL. If, however, the query was unsuccessful, @entry will be %NULL and @error will contain a #GError detailing what went wrong.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: operation ID for the added query, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
guint
Packit 4b6dd7
gdata_batch_operation_add_query (GDataBatchOperation *self, const gchar *id, GType entry_type,
Packit 4b6dd7
                                 GDataBatchOperationCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	BatchOperation *op;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), 0);
Packit 4b6dd7
	g_return_val_if_fail (id != NULL, 0);
Packit 4b6dd7
	g_return_val_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY), 0);
Packit 4b6dd7
	g_return_val_if_fail (self->priv->has_run == FALSE, 0);
Packit 4b6dd7
Packit 4b6dd7
	/* Create the operation manually, since it would be messy to special-case add_operation() to do this */
Packit 4b6dd7
	op = g_slice_new0 (BatchOperation);
Packit 4b6dd7
	op->id = (self->priv->next_id++);
Packit 4b6dd7
	op->type = GDATA_BATCH_OPERATION_QUERY;
Packit 4b6dd7
	op->callback = callback;
Packit 4b6dd7
	op->user_data = user_data;
Packit 4b6dd7
	op->query_id = g_strdup (id);
Packit 4b6dd7
	op->entry_type = entry_type;
Packit 4b6dd7
Packit 4b6dd7
	/* Add the operation to the table */
Packit 4b6dd7
	g_hash_table_insert (self->priv->operations, GUINT_TO_POINTER (op->id), op);
Packit 4b6dd7
Packit 4b6dd7
	return op->id;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_add_insertion:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @entry: the #GDataEntry to insert
Packit 4b6dd7
 * @callback: (scope async): a #GDataBatchOperationCallback to call when the insertion is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Add an entry to the #GDataBatchOperation, to be inserted on the server when the operation is run. The insertion will return the inserted version
Packit 4b6dd7
 * of @entry. @entry is reffed by the function, so may be freed after it returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * @callback will be called as specified in the documentation for gdata_batch_operation_add_query(), with an @operation_type of
Packit 4b6dd7
 * %GDATA_BATCH_OPERATION_INSERTION.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: operation ID for the added insertion, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
guint
Packit 4b6dd7
gdata_batch_operation_add_insertion (GDataBatchOperation *self, GDataEntry *entry, GDataBatchOperationCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), 0);
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_ENTRY (entry), 0);
Packit 4b6dd7
	g_return_val_if_fail (self->priv->has_run == FALSE, 0);
Packit 4b6dd7
Packit 4b6dd7
	return add_operation (self, GDATA_BATCH_OPERATION_INSERTION, entry, callback, user_data);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_add_update:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @entry: the #GDataEntry to update
Packit 4b6dd7
 * @callback: (scope async): a #GDataBatchOperationCallback to call when the update is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Add an entry to the #GDataBatchOperation, to be updated on the server when the operation is run. The update will return the updated version of
Packit 4b6dd7
 * @entry. @entry is reffed by the function, so may be freed after it returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that a single batch operation should not operate on a given #GDataEntry more than once, as there's no guarantee about the order in which the
Packit 4b6dd7
 * batch operation's operations will be performed.
Packit 4b6dd7
 *
Packit 4b6dd7
 * @callback will be called as specified in the documentation for gdata_batch_operation_add_query(), with an @operation_type of
Packit 4b6dd7
 * %GDATA_BATCH_OPERATION_UPDATE.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: operation ID for the added update, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
guint
Packit 4b6dd7
gdata_batch_operation_add_update (GDataBatchOperation *self, GDataEntry *entry, GDataBatchOperationCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), 0);
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_ENTRY (entry), 0);
Packit 4b6dd7
	g_return_val_if_fail (self->priv->has_run == FALSE, 0);
Packit 4b6dd7
Packit 4b6dd7
	return add_operation (self, GDATA_BATCH_OPERATION_UPDATE, entry, callback, user_data);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_add_deletion:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @entry: the #GDataEntry to delete
Packit 4b6dd7
 * @callback: (scope async): a #GDataBatchOperationCallback to call when the deletion is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Add an entry to the #GDataBatchOperation, to be deleted on the server when the operation is run. @entry is reffed by the function, so may be freed
Packit 4b6dd7
 * after it returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Note that a single batch operation should not operate on a given #GDataEntry more than once, as there's no guarantee about the order in which the
Packit 4b6dd7
 * batch operation's operations will be performed.
Packit 4b6dd7
 *
Packit 4b6dd7
 * @callback will be called as specified in the documentation for gdata_batch_operation_add_query(), with an @operation_type of
Packit 4b6dd7
 * %GDATA_BATCH_OPERATION_DELETION.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: operation ID for the added deletion, or 0
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
guint
Packit 4b6dd7
gdata_batch_operation_add_deletion (GDataBatchOperation *self, GDataEntry *entry, GDataBatchOperationCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), 0);
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_ENTRY (entry), 0);
Packit 4b6dd7
	g_return_val_if_fail (self->priv->has_run == FALSE, 0);
Packit 4b6dd7
Packit 4b6dd7
	return add_operation (self, GDATA_BATCH_OPERATION_DELETION, entry, callback, user_data);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_run:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @cancellable: (allow-none): a #GCancellable, or %NULL
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Run the #GDataBatchOperation synchronously. This will send all the operations in the batch operation to the server, and call their respective
Packit 4b6dd7
 * callbacks synchronously (i.e. before gdata_batch_operation_run() returns, and in the same thread that called gdata_batch_operation_run()) as the
Packit 4b6dd7
 * server returns results for each operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The callbacks for all of the operations in the batch operation are always guaranteed to be called, even if the batch operation as a whole fails.
Packit 4b6dd7
 * Each callback will be called exactly once for each time gdata_batch_operation_run() is called.
Packit 4b6dd7
 *
Packit 4b6dd7
 * The return value of the function indicates whether the overall batch operation was successful, and doesn't indicate the status of any of the
Packit 4b6dd7
 * operations it comprises. gdata_batch_operation_run() could return %TRUE even if all of its operations failed.
Packit 4b6dd7
 *
Packit 4b6dd7
 * @cancellable can be used to cancel the entire batch operation any time before or during the network activity. If @cancellable is cancelled
Packit 4b6dd7
 * after network activity has finished, gdata_batch_operation_run() will continue and finish as normal.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: %TRUE on success, %FALSE otherwise
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
gboolean
Packit 4b6dd7
gdata_batch_operation_run (GDataBatchOperation *self, GCancellable *cancellable, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataBatchOperationPrivate *priv = self->priv;
Packit 4b6dd7
	SoupMessage *message;
Packit 4b6dd7
	GDataFeed *feed;
Packit 4b6dd7
	GTimeVal updated;
Packit 4b6dd7
	gchar *upload_data;
Packit 4b6dd7
	guint status;
Packit 4b6dd7
	GHashTableIter iter;
Packit 4b6dd7
	gpointer op_id;
Packit 4b6dd7
	BatchOperation *op;
Packit 4b6dd7
	GError *child_error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
	g_return_val_if_fail (priv->has_run == FALSE, FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* Check for early cancellation. */
Packit 4b6dd7
	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Check whether the service actually supports these kinds of
Packit 4b6dd7
	 * operations. */
Packit 4b6dd7
	g_hash_table_iter_init (&iter, priv->operations);
Packit 4b6dd7
	while (g_hash_table_iter_next (&iter, &op_id, (gpointer*) &op) == TRUE) {
Packit 4b6dd7
		GDataBatchable *batchable = GDATA_BATCHABLE (priv->service);
Packit 4b6dd7
		GDataBatchableIface *batchable_iface;
Packit 4b6dd7
Packit 4b6dd7
		batchable_iface = GDATA_BATCHABLE_GET_IFACE (batchable);
Packit 4b6dd7
Packit 4b6dd7
		if (batchable_iface->is_supported != NULL &&
Packit 4b6dd7
		    !batchable_iface->is_supported (op->type)) {
Packit 4b6dd7
			g_set_error (error, GDATA_SERVICE_ERROR,
Packit 4b6dd7
			             GDATA_SERVICE_ERROR_WITH_BATCH_OPERATION,
Packit 4b6dd7
			             _("Batch operations are unsupported by "
Packit 4b6dd7
			               "this service."));
Packit 4b6dd7
			return FALSE;
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	message = _gdata_service_build_message (priv->service, priv->authorization_domain, SOUP_METHOD_POST, priv->feed_uri, NULL, TRUE);
Packit 4b6dd7
Packit 4b6dd7
	/* Build the request */
Packit 4b6dd7
	g_get_current_time (&updated);
Packit 4b6dd7
	feed = _gdata_feed_new (GDATA_TYPE_FEED, "Batch operation feed",
Packit 4b6dd7
	                        "batch1", updated.tv_sec);
Packit 4b6dd7
Packit 4b6dd7
	g_hash_table_iter_init (&iter, priv->operations);
Packit 4b6dd7
	while (g_hash_table_iter_next (&iter, &op_id, (gpointer*) &op) == TRUE) {
Packit 4b6dd7
		if (op->type == GDATA_BATCH_OPERATION_QUERY) {
Packit 4b6dd7
			/* Queries are weird; build a new throwaway entry, and add it to the feed */
Packit 4b6dd7
			GDataEntry *entry;
Packit 4b6dd7
			GDataEntryClass *klass;
Packit 4b6dd7
			gchar *entry_uri;
Packit 4b6dd7
Packit 4b6dd7
			klass = g_type_class_ref (op->entry_type);
Packit 4b6dd7
			g_assert (klass->get_entry_uri != NULL);
Packit 4b6dd7
Packit 4b6dd7
			entry_uri = klass->get_entry_uri (op->query_id);
Packit 4b6dd7
			entry = gdata_entry_new (entry_uri);
Packit 4b6dd7
			g_free (entry_uri);
Packit 4b6dd7
Packit 4b6dd7
			gdata_entry_set_title (entry, "Batch operation query");
Packit 4b6dd7
			_gdata_entry_set_updated (entry, updated.tv_sec);
Packit 4b6dd7
Packit 4b6dd7
			_gdata_entry_set_batch_data (entry, op->id, op->type);
Packit 4b6dd7
			_gdata_feed_add_entry (feed, entry);
Packit 4b6dd7
Packit 4b6dd7
			g_type_class_unref (klass);
Packit 4b6dd7
			g_object_unref (entry);
Packit 4b6dd7
		} else {
Packit 4b6dd7
			/* Everything else just dumps the entry's XML in the request */
Packit 4b6dd7
			_gdata_entry_set_batch_data (op->entry, op->id, op->type);
Packit 4b6dd7
			_gdata_feed_add_entry (feed, op->entry);
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	upload_data = gdata_parsable_get_xml (GDATA_PARSABLE (feed));
Packit 4b6dd7
	soup_message_set_request (message, "application/atom+xml", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (feed);
Packit 4b6dd7
Packit 4b6dd7
	/* Ensure that this GDataBatchOperation can't be run again */
Packit 4b6dd7
	priv->has_run = TRUE;
Packit 4b6dd7
Packit 4b6dd7
	/* Send the message */
Packit 4b6dd7
	status = _gdata_service_send_message (priv->service, message, cancellable, &child_error);
Packit 4b6dd7
Packit 4b6dd7
	if (status != SOUP_STATUS_OK) {
Packit 4b6dd7
		/* Iff status is SOUP_STATUS_NONE or SOUP_STATUS_CANCELLED, child_error has already been set */
Packit 4b6dd7
		if (status != SOUP_STATUS_NONE && status != SOUP_STATUS_CANCELLED) {
Packit 4b6dd7
			/* Error */
Packit 4b6dd7
			GDataServiceClass *klass = GDATA_SERVICE_GET_CLASS (priv->service);
Packit 4b6dd7
			g_assert (klass->parse_error_response != NULL);
Packit 4b6dd7
			klass->parse_error_response (priv->service, GDATA_OPERATION_BATCH, status, message->reason_phrase, message->response_body->data,
Packit 4b6dd7
			                             message->response_body->length, &child_error);
Packit 4b6dd7
		}
Packit 4b6dd7
		g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
		goto error;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Parse the XML; GDataBatchFeed will fire off the relevant callbacks */
Packit 4b6dd7
	g_assert (message->response_body->data != NULL);
Packit 4b6dd7
	feed = GDATA_FEED (_gdata_parsable_new_from_xml (GDATA_TYPE_BATCH_FEED, message->response_body->data, message->response_body->length,
Packit 4b6dd7
	                                                 self, &child_error));
Packit 4b6dd7
	g_object_unref (message);
Packit 4b6dd7
Packit 4b6dd7
	if (feed == NULL)
Packit 4b6dd7
		goto error;
Packit 4b6dd7
	g_object_unref (feed);
Packit 4b6dd7
Packit 4b6dd7
	return TRUE;
Packit 4b6dd7
Packit 4b6dd7
error:
Packit 4b6dd7
	/* Call the callbacks for each of our operations to notify them of the error */
Packit 4b6dd7
	g_hash_table_iter_init (&iter, priv->operations);
Packit 4b6dd7
	while (g_hash_table_iter_next (&iter, &op_id, (gpointer*) &op) == TRUE)
Packit 4b6dd7
		_gdata_batch_operation_run_callback (self, op, NULL, g_error_copy (child_error));
Packit 4b6dd7
Packit 4b6dd7
	g_propagate_error (error, child_error);
Packit 4b6dd7
Packit 4b6dd7
	return FALSE;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
run_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataBatchOperation *operation = GDATA_BATCH_OPERATION (source_object);
Packit 4b6dd7
	g_autoptr(GError) error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Run the batch operation and return */
Packit 4b6dd7
	if (!gdata_batch_operation_run (operation, cancellable, &error))
Packit 4b6dd7
		g_task_return_error (task, g_steal_pointer (&error));
Packit 4b6dd7
	else
Packit 4b6dd7
		g_task_return_boolean (task, TRUE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_run_async:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @cancellable: (allow-none): a #GCancellable, or %NULL
Packit 4b6dd7
 * @callback: a #GAsyncReadyCallback to call when the batch operation is finished, or %NULL
Packit 4b6dd7
 * @user_data: (closure): data to pass to the @callback function
Packit 4b6dd7
 *
Packit 4b6dd7
 * Run the #GDataBatchOperation asynchronously. This will send all the operations in the batch operation to the server, and call their respective
Packit 4b6dd7
 * callbacks asynchronously (i.e. in idle functions in the main thread, usually after gdata_batch_operation_run_async() has returned) as the
Packit 4b6dd7
 * server returns results for each operation. @self is reffed when this function is called, so can safely be unreffed after this function returns.
Packit 4b6dd7
 *
Packit 4b6dd7
 * For more details, see gdata_batch_operation_run(), which is the synchronous version of this function.
Packit 4b6dd7
 *
Packit 4b6dd7
 * When the entire batch operation is finished, @callback will be called. You can then call gdata_batch_operation_run_finish() to get the results of
Packit 4b6dd7
 * the batch operation.
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
void
Packit 4b6dd7
gdata_batch_operation_run_async (GDataBatchOperation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	g_autoptr(GTask) task = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_return_if_fail (GDATA_IS_BATCH_OPERATION (self));
Packit 4b6dd7
	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
Packit 4b6dd7
	g_return_if_fail (self->priv->has_run == FALSE);
Packit 4b6dd7
Packit 4b6dd7
	/* Mark the operation as async for the purposes of deciding where to call the callbacks */
Packit 4b6dd7
	self->priv->is_async = TRUE;
Packit 4b6dd7
Packit 4b6dd7
	task = g_task_new (self, cancellable, callback, user_data);
Packit 4b6dd7
	g_task_set_source_tag (task, gdata_batch_operation_run_async);
Packit 4b6dd7
	g_task_run_in_thread (task, run_thread);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/**
Packit 4b6dd7
 * gdata_batch_operation_run_finish:
Packit 4b6dd7
 * @self: a #GDataBatchOperation
Packit 4b6dd7
 * @async_result: a #GAsyncResult
Packit 4b6dd7
 * @error: a #GError, or %NULL
Packit 4b6dd7
 *
Packit 4b6dd7
 * Finishes an asynchronous batch operation run with gdata_batch_operation_run_async().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return values are as for gdata_batch_operation_run().
Packit 4b6dd7
 *
Packit 4b6dd7
 * Return value: %TRUE on success, %FALSE otherwise
Packit 4b6dd7
 *
Packit 4b6dd7
 * Since: 0.7.0
Packit 4b6dd7
 */
Packit 4b6dd7
gboolean
Packit 4b6dd7
gdata_batch_operation_run_finish (GDataBatchOperation *self, GAsyncResult *async_result, GError **error)
Packit 4b6dd7
{
Packit 4b6dd7
	GDataBatchOperationPrivate *priv = self->priv;
Packit 4b6dd7
	g_autoptr(GError) child_error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (GDATA_IS_BATCH_OPERATION (self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit 4b6dd7
	g_return_val_if_fail (g_task_is_valid (async_result, self), FALSE);
Packit 4b6dd7
	g_return_val_if_fail (g_async_result_is_tagged (async_result, gdata_batch_operation_run_async), FALSE);
Packit 4b6dd7
Packit 4b6dd7
	if (!g_task_propagate_boolean (G_TASK (async_result), &child_error)) {
Packit 4b6dd7
		if (priv->has_run == FALSE) {
Packit 4b6dd7
			GHashTableIter iter;
Packit 4b6dd7
			gpointer op_id;
Packit 4b6dd7
			BatchOperation *op;
Packit 4b6dd7
Packit 4b6dd7
			/* Temporarily mark the operation as synchronous so that the callbacks get dispatched in this thread */
Packit 4b6dd7
			priv->is_async = FALSE;
Packit 4b6dd7
Packit 4b6dd7
			/* If has_run hasn't been set, the call to gdata_batch_operation_run() was never made in the thread, and so none of the
Packit 4b6dd7
			 * operations' callbacks have been called. Call the callbacks for each of our operations to notify them of the error.
Packit 4b6dd7
			 * If has_run has been set, gdata_batch_operation_run() has already done this for us. */
Packit 4b6dd7
			g_hash_table_iter_init (&iter, priv->operations);
Packit 4b6dd7
			while (g_hash_table_iter_next (&iter, &op_id, (gpointer*) &op) == TRUE)
Packit 4b6dd7
				_gdata_batch_operation_run_callback (self, op, NULL, g_error_copy (child_error));
Packit 4b6dd7
Packit 4b6dd7
			priv->is_async = TRUE;
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		g_propagate_error (error, g_steal_pointer (&child_error));
Packit 4b6dd7
Packit 4b6dd7
		return FALSE;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return TRUE;
Packit 4b6dd7
}