summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Withnall <philip@tecnocode.co.uk>2015-04-13 23:59:39 +0100
committerPhilip Withnall <philip@tecnocode.co.uk>2015-04-13 23:59:55 +0100
commit8fe6897d25c65668c8d4ce78f1a010b6be3f704b (patch)
tree9a1e096ed25de327c26d4410f339c8444f25c0d0
parent530ad9b90ea20ac05e7b19dc7bcc01e9507e4c9a (diff)
downloadlibgdata-calendar-v3.tar.gz
calendar: WIP work to port to v3 APIcalendar-v3
-rw-r--r--docs/reference/gdata-sections.txt2
-rw-r--r--gdata/gdata-entry.c4
-rw-r--r--gdata/gdata-parser.c161
-rw-r--r--gdata/gdata-parser.h14
-rw-r--r--gdata/gdata-service.c1
-rw-r--r--gdata/gdata.symbols2
-rw-r--r--gdata/services/calendar/gdata-calendar-calendar.c195
-rw-r--r--gdata/services/calendar/gdata-calendar-event.c755
-rw-r--r--gdata/services/calendar/gdata-calendar-feed.c73
-rw-r--r--gdata/services/calendar/gdata-calendar-service.c434
-rw-r--r--gdata/services/calendar/gdata-calendar-service.h14
-rw-r--r--gdata/tests/calendar.c692
12 files changed, 1734 insertions, 613 deletions
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 0c109c21..dd6ce0ff 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -470,6 +470,8 @@ gdata_calendar_service_query_events
gdata_calendar_service_query_events_async
gdata_calendar_service_insert_event
gdata_calendar_service_insert_event_async
+gdata_calendar_service_insert_calendar_event
+gdata_calendar_service_insert_calendar_event_async
<SUBSECTION Standard>
gdata_calendar_service_get_type
GDATA_CALENDAR_SERVICE
diff --git a/gdata/gdata-entry.c b/gdata/gdata-entry.c
index be242e59..262bb082 100644
--- a/gdata/gdata-entry.c
+++ b/gdata/gdata-entry.c
@@ -881,13 +881,17 @@ gdata_entry_add_category (GDataEntry *self, GDataCategory *category)
/* Check to see if it's a kind category and if it matches the entry's predetermined kind */
if (g_strcmp0 (gdata_category_get_scheme (category), "http://schemas.google.com/g/2005#kind") == 0) {
+#if 0
GDataEntryClass *klass = GDATA_ENTRY_GET_CLASS (self);
+#endif
GList *element;
+#if 0
if (klass->kind_term != NULL && g_strcmp0 (gdata_category_get_term (category), klass->kind_term) != 0) {
g_warning ("Adding a kind category term, '%s', to an entry of kind '%s'.",
gdata_category_get_term (category), klass->kind_term);
}
+#endif
/* If it is a kind category, remove the entry’s existing kind category to allow the new one
* to be added. This is necessary because the existing category was set in
diff --git a/gdata/gdata-parser.c b/gdata/gdata-parser.c
index 0e655a5d..cd1d7621 100644
--- a/gdata/gdata-parser.c
+++ b/gdata/gdata-parser.c
@@ -29,6 +29,7 @@
#include "gdata-parser.h"
#include "gdata-service.h"
+#include "gdata-types.h"
#include "gdata-private.h"
static gchar *
@@ -305,8 +306,10 @@ gdata_parser_error_not_iso8601_format_json (JsonReader *reader, const gchar *act
return FALSE;
}
-static gboolean
-parser_error_from_json_error (JsonReader *reader, const GError *json_error, GError **error)
+gboolean
+gdata_parser_error_from_json_error (JsonReader *reader,
+ const GError *json_error,
+ GError **error)
{
g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
/* Translators: the parameter is an error message. */
@@ -730,6 +733,64 @@ gdata_parser_object_from_element (xmlNode *element, const gchar *element_name, G
}
/*
+ * gdata_parser_int_from_json_member:
+ * @reader: #JsonReader cursor object to read JSON node from
+ * @member_name: the name of the member to parse
+ * @options: a bitwise combination of parsing options from #GDataParserOptions, or %P_NONE
+ * @output: the return location for the parsed integer content
+ * @success: the return location for a value which is %TRUE if the integer was parsed successfully, %FALSE if an error was encountered,
+ * and undefined if @element didn't match @element_name
+ * @error: a #GError, or %NULL
+ *
+ * Gets the integer content of @element if its name is @element_name, subject to various checks specified by @options.
+ *
+ * If @element doesn't match @element_name, %FALSE will be returned, @error will be unset and @success will be unset.
+ *
+ * If @element matches @element_name but one of the checks specified by @options fails, %TRUE will be returned, @error will be set to a
+ * %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error and @success will be set to %FALSE.
+ *
+ * If @element matches @element_name and all of the checks specified by @options pass, %TRUE will be returned, @error will be unset and
+ * @success will be set to %TRUE.
+ *
+ * The reason for returning the success of the parsing in @success is so that calls to gdata_parser_integer_from_element() can be chained
+ * together in a large "or" statement based on their return values, for the purposes of determining whether any of the calls matched
+ * a given @element. If any of the calls to gdata_parser_integer_from_element() return %TRUE, the value of @success can be examined.
+ *
+ * Return value: %TRUE if @element matched @element_name, %FALSE otherwise
+ *
+ * Since: UNRELEASED
+ */
+gboolean
+gdata_parser_int_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions options,
+ gint64 *output, gboolean *success, GError **error)
+{
+ gint64 val;
+ const GError *child_error = NULL;
+
+ /* Check if there's such element */
+ if (g_strcmp0 (json_reader_get_member_name (reader), member_name) != 0) {
+ return FALSE;
+ }
+
+ /* Check if the output int has already been set. The JSON parser guarantees this can't happen. */
+ g_assert (!(options & P_NO_DUPES) || *output == 0);
+
+ /* Get the int and check it for NULLness or emptiness. Check for parser errors first. */
+ val = json_reader_get_int_value (reader);
+ child_error = json_reader_get_error (reader);
+ if (child_error != NULL) {
+ *success = gdata_parser_error_from_json_error (reader, child_error, error);
+ return TRUE;
+ }
+
+ /* Success! */
+ *output = val;
+ *success = TRUE;
+
+ return TRUE;
+}
+
+/*
* gdata_parser_string_from_json_member:
* @reader: #JsonReader cursor object to read JSON node from
* @member_name: the name of the member to parse
@@ -776,7 +837,7 @@ gdata_parser_string_from_json_member (JsonReader *reader, const gchar *member_na
text = json_reader_get_string_value (reader);
child_error = json_reader_get_error (reader);
if (child_error != NULL) {
- *success = parser_error_from_json_error (reader, child_error, error);
+ *success = gdata_parser_error_from_json_error (reader, child_error, error);
return TRUE;
} else if ((options & P_REQUIRED && text == NULL) || (options & P_NON_EMPTY && text != NULL && *text == '\0')) {
*success = gdata_parser_error_required_json_content_missing (reader, error);
@@ -842,7 +903,7 @@ gdata_parser_int64_time_from_json_member (JsonReader *reader, const gchar *membe
text = json_reader_get_string_value (reader);
child_error = json_reader_get_error (reader);
if (child_error != NULL) {
- *success = parser_error_from_json_error (reader, child_error, error);
+ *success = gdata_parser_error_from_json_error (reader, child_error, error);
return TRUE;
} else if (options & P_REQUIRED && (text == NULL || *text == '\0')) {
*success = gdata_parser_error_required_json_content_missing (reader, error);
@@ -905,7 +966,7 @@ gdata_parser_boolean_from_json_member (JsonReader *reader, const gchar *member_n
val = json_reader_get_boolean_value (reader);
child_error = json_reader_get_error (reader);
if (child_error != NULL) {
- *success = parser_error_from_json_error (reader, child_error, error);
+ *success = gdata_parser_error_from_json_error (reader, child_error, error);
return TRUE;
}
@@ -916,6 +977,96 @@ gdata_parser_boolean_from_json_member (JsonReader *reader, const gchar *member_n
return TRUE;
}
+/*
+ * gdata_parser_color_from_json_member:
+ * @reader: #JsonReader cursor object to read JSON node from
+ * @element_name: the name of the element to parse
+ * @options: a bitwise combination of parsing options from #GDataParserOptions,
+ * or %P_NONE
+ * @output: (out caller-allocates): the return location for the parsed colour
+ * value
+ * @success: the return location for a value which is %TRUE if the colour was
+ * parsed successfully, %FALSE if an error was encountered, and undefined if
+ * @element didn't match @element_name
+ * @error: a #GError, or %NULL
+ *
+ * TODO
+ * Gets the colour value of @element if its name is @element_name, subject to various checks specified by @options. It expects the text content
+ * of @element to be a date or time value in ISO 8601 format. The returned time value will be a UNIX timestamp (seconds since the epoch).
+ *
+ * If @element doesn't match @element_name, %FALSE will be returned, @error will be unset and @success will be unset.
+ *
+ * If @element matches @element_name but one of the checks specified by @options fails, %TRUE will be returned, @error will be set to a
+ * %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error and @success will be set to %FALSE.
+ *
+ * If @element matches @element_name and all of the checks specified by @options pass, %TRUE will be returned, @error will be unset and
+ * @success will be set to %TRUE.
+ *
+ * The reason for returning the success of the parsing in @success is so that calls to gdata_parser_int64_time_from_element() can be chained
+ * together in a large "or" statement based on their return values, for the purposes of determining whether any of the calls matched
+ * a given @element. If any of the calls to gdata_parser_int64_time_from_element() return %TRUE, the value of @success can be examined.
+ *
+ * Return value: %TRUE if @element matched @element_name, %FALSE otherwise
+ *
+ * Since: UNRELEASED
+ */
+gboolean
+gdata_parser_color_from_json_member (JsonReader *reader,
+ const gchar *member_name,
+ GDataParserOptions options,
+ GDataColor *output,
+ gboolean *success,
+ GError **error)
+{
+ const gchar *text;
+ GDataColor colour;
+ const GError *child_error = NULL;
+
+ /* Check if there's such an element */
+ if (g_strcmp0 (json_reader_get_member_name (reader), member_name) != 0) {
+ return FALSE;
+ }
+
+ /* Check if the output colour has already been set. The JSON parser
+ * guarantees this can't happen. */
+ g_assert (!(options & P_NO_DUPES) ||
+ (output->red == 0 && output->green == 0 && output->blue == 0));
+
+ /* Get the string and check it for NULLness. Check for errors first. */
+ text = json_reader_get_string_value (reader);
+ child_error = json_reader_get_error (reader);
+ if (child_error != NULL) {
+ *success = gdata_parser_error_from_json_error (reader, child_error, error);
+ return TRUE;
+ } else if (options & P_REQUIRED && (text == NULL || *text == '\0')) {
+ *success = gdata_parser_error_required_json_content_missing (reader, error);
+ return TRUE;
+ }
+
+ /* Attempt to parse the string as a hexadecimal colour. */
+ if (gdata_color_from_hexadecimal (text, &colour) == FALSE) {
+ /* Error */
+ g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
+ /* Translators: the first parameter is the name of an XML element (including the angle brackets
+ * ("<" and ">"), and the second parameter is the erroneous value (which was not in hexadecimal
+ * RGB format).
+ *
+ * For example:
+ * The content of a <entry/gCal:color> element ("00FG56") was not in hexadecimal RGB format. */
+ _("The content of a %s element (\"%s\") was not in hexadecimal RGB format."),
+ member_name, text);
+ *success = FALSE;
+
+ return TRUE;
+ }
+
+ /* Success! */
+ *output = colour;
+ *success = TRUE;
+
+ return TRUE;
+}
+
void
gdata_parser_string_append_escaped (GString *xml_string, const gchar *pre, const gchar *element_content, const gchar *post)
{
diff --git a/gdata/gdata-parser.h b/gdata/gdata-parser.h
index 2c5ad2ff..a35ecdf4 100644
--- a/gdata/gdata-parser.h
+++ b/gdata/gdata-parser.h
@@ -20,6 +20,7 @@
#include <glib.h>
#include "gdata-parsable.h"
+#include "gdata-types.h"
#ifndef GDATA_PARSER_H
#define GDATA_PARSER_H
@@ -38,6 +39,10 @@ gboolean gdata_parser_error_duplicate_element (xmlNode *element, GError **error)
gboolean gdata_parser_error_duplicate_json_element (JsonReader *reader, GError **error);
gboolean gdata_parser_error_required_json_content_missing (JsonReader *reader, GError **error);
gboolean gdata_parser_error_not_iso8601_format_json (JsonReader *reader, const gchar *actual_value, GError **error);
+gboolean
+gdata_parser_error_from_json_error (JsonReader *reader,
+ const GError *json_error,
+ GError **error);
gboolean gdata_parser_int64_from_date (const gchar *date, gint64 *_time);
gchar *gdata_parser_date_from_int64 (gint64 _time) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
@@ -86,10 +91,19 @@ gboolean gdata_parser_object_from_element (xmlNode *element, const gchar *elemen
gpointer /* GDataParsable ** */ _output, gboolean *success, GError **error);
gboolean gdata_parser_string_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions options,
gchar **output, gboolean *success, GError **error);
+gboolean gdata_parser_int_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions options,
+ gint64 *output, gboolean *success, GError **error);
gboolean gdata_parser_int64_time_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions options,
gint64 *output, gboolean *success, GError **error);
gboolean gdata_parser_boolean_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions options,
gboolean *output, gboolean *success, GError **error);
+gboolean
+gdata_parser_color_from_json_member (JsonReader *reader,
+ const gchar *member_name,
+ GDataParserOptions options,
+ GDataColor *output,
+ gboolean *success,
+ GError **error);
void gdata_parser_string_append_escaped (GString *xml_string, const gchar *pre, const gchar *element_content, const gchar *post);
gchar *gdata_parser_utf8_trim_whitespace (const gchar *s) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index 300824b2..f3898c0b 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -1850,6 +1850,7 @@ gdata_service_delete_entry (GDataService *self, GDataAuthorizationDomain *domain
} else {
_link = gdata_entry_look_up_link (entry, GDATA_LINK_EDIT);
}
+ g_message ("%p: looking up link %s", entry, klass->get_content_type ());
g_assert (_link != NULL);
fixed_uri = _gdata_service_fix_uri_scheme (gdata_link_get_uri (_link));
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index 7ae3e955..c71cf90d 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -238,6 +238,7 @@ gdata_calendar_service_query_own_calendars
gdata_calendar_service_query_own_calendars_async
gdata_calendar_service_query_events
gdata_calendar_service_insert_event
+gdata_calendar_service_insert_calendar_event
gdata_operation_type_get_type
gdata_service_error_get_type
gdata_media_expression_get_type
@@ -856,6 +857,7 @@ gdata_youtube_service_upload_video
gdata_youtube_service_finish_video_upload
gdata_calendar_service_query_events_async
gdata_calendar_service_insert_event_async
+gdata_calendar_service_insert_calendar_event_async
gdata_contacts_contact_get_photo_async
gdata_contacts_contact_get_photo_finish
gdata_contacts_contact_set_photo_async
diff --git a/gdata/services/calendar/gdata-calendar-calendar.c b/gdata/services/calendar/gdata-calendar-calendar.c
index 93f14534..0b6e70ee 100644
--- a/gdata/services/calendar/gdata-calendar-calendar.c
+++ b/gdata/services/calendar/gdata-calendar-calendar.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
* GData Client
- * Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
+ * Copyright (C) Philip Withnall 2009, 2010, 2014 <philip@tecnocode.co.uk>
*
* GData Client is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -32,6 +32,8 @@
* For more details of Google Calendar's GData API, see the <ulink type="http" url="http://code.google.com/apis/calendar/docs/2.0/reference.html">
* online documentation</ulink>.
*
+ * TODO: update examples, etags
+ *
* <example>
* <title>Listing Calendars</title>
* <programlisting>
@@ -77,7 +79,6 @@
#include <config.h>
#include <glib.h>
#include <glib/gi18n-lib.h>
-#include <libxml/parser.h>
#include <string.h>
#include "gdata-calendar-calendar.h"
@@ -93,9 +94,9 @@ static GObject *gdata_calendar_calendar_constructor (GType type, guint n_constru
static void gdata_calendar_calendar_finalize (GObject *object);
static void gdata_calendar_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
static void gdata_calendar_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
-static void get_xml (GDataParsable *parsable, GString *xml_string);
-static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error);
-static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+static void get_json (GDataParsable *parsable, JsonBuilder *builder);
+static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
+static const gchar *get_content_type (void);
struct _GDataCalendarCalendarPrivate {
gchar *timezone;
@@ -136,17 +137,17 @@ gdata_calendar_calendar_class_init (GDataCalendarCalendarClass *klass)
gobject_class->get_property = gdata_calendar_calendar_get_property;
gobject_class->finalize = gdata_calendar_calendar_finalize;
- parsable_class->parse_xml = parse_xml;
- parsable_class->get_xml = get_xml;
- parsable_class->get_namespaces = get_namespaces;
+ parsable_class->parse_json = parse_json;
+ parsable_class->get_json = get_json;
+ parsable_class->get_content_type = get_content_type;
- entry_class->kind_term = "http://schemas.google.com/gCal/2005#calendarmeta";
+ entry_class->kind_term = "calendar#calendarListEntry";
/**
* GDataCalendarCalendar:timezone:
*
* The timezone in which the calendar's times are given. This is a timezone name in tz database notation: <ulink type="http"
- * url="http://en.wikipedia.org/wiki/Tz_database#Names_of_time_zones">reference</ulink>.
+ * url="http://en.wikipedia.org/wiki/Tz_database#Names_of_time_zones">reference</ulink>. TODO: is it?
**/
g_object_class_install_property (gobject_class, PROP_TIMEZONE,
g_param_spec_string ("timezone",
@@ -182,7 +183,7 @@ gdata_calendar_calendar_class_init (GDataCalendarCalendarClass *klass)
* GDataCalendarCalendar:color:
*
* The color used to highlight the calendar in the user's browser. This must be one of a limited set of colors listed in the
- * <ulink type="http" url="http://code.google.com/apis/calendar/data/2.0/reference.html#gCalcolor">online documentation</ulink>.
+ * <ulink type="http" url="http://code.google.com/apis/calendar/data/2.0/reference.html#gCalcolor">online documentation</ulink>. TODO
**/
g_object_class_install_property (gobject_class, PROP_COLOR,
g_param_spec_boxed ("color",
@@ -360,113 +361,123 @@ gdata_calendar_calendar_set_property (GObject *object, guint property_id, const
}
static gboolean
-parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
{
gboolean success;
+ gchar *summary = NULL;
GDataCalendarCalendar *self = GDATA_CALENDAR_CALENDAR (parsable);
- if (gdata_parser_is_namespace (node, "http://www.w3.org/2007/app") == TRUE &&
- gdata_parser_int64_time_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE) {
- return success;
- } else if (gdata_parser_is_namespace (node, "http://schemas.google.com/gCal/2005") == TRUE) {
- if (xmlStrcmp (node->name, (xmlChar*) "timezone") == 0) {
- /* gCal:timezone */
- xmlChar *_timezone = xmlGetProp (node, (xmlChar*) "value");
- if (_timezone == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- self->priv->timezone = (gchar*) _timezone;
- } else if (xmlStrcmp (node->name, (xmlChar*) "timesCleaned") == 0) {
- /* gCal:timesCleaned */
- xmlChar *times_cleaned = xmlGetProp (node, (xmlChar*) "value");
- if (times_cleaned == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- self->priv->times_cleaned = g_ascii_strtoull ((gchar*) times_cleaned, NULL, 10);
- xmlFree (times_cleaned);
- } else if (xmlStrcmp (node->name, (xmlChar*) "hidden") == 0) {
- /* gCal:hidden */
- if (gdata_parser_boolean_from_property (node, "value", &(self->priv->is_hidden), -1, error) == FALSE)
- return FALSE;
- } else if (xmlStrcmp (node->name, (xmlChar*) "color") == 0) {
- /* gCal:color */
- xmlChar *value;
- GDataColor colour;
-
- value = xmlGetProp (node, (xmlChar*) "value");
- if (value == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- if (gdata_color_from_hexadecimal ((gchar*) value, &colour) == FALSE) {
- /* Error */
- g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
- /* Translators: the first parameter is the name of an XML element (including the angle brackets
- * ("<" and ">"), and the second parameter is the erroneous value (which was not in hexadecimal
- * RGB format).
- *
- * For example:
- * The content of a <entry/gCal:color> element ("00FG56") was not in hexadecimal RGB format. */
- _("The content of a %s element (\"%s\") was not in hexadecimal RGB format."),
- "<entry/gCal:color>", value);
- xmlFree (value);
-
- return FALSE;
+ /* TODO: things removed:
+ * edited
+ * timesCleaned
+ *
+ * things not yet implemented
+ * description
+ * location
+ * summaryOverride
+ * colorId
+ * foregroundColor
+ * defaultReminders
+ * notificationSettings
+ * primary
+ * deleted
+ *
+ * TODO: check values for accesslevel
+ */
+
+ if (gdata_parser_string_from_json_member (reader, "timeZone", P_DEFAULT, &self->priv->timezone, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "accessRole", P_DEFAULT, &self->priv->access_level, &success, error) ||
+ gdata_parser_color_from_json_member (reader, "backgroundColor", P_DEFAULT, &self->priv->colour, &success, error) ||
+ gdata_parser_boolean_from_json_member (reader, "hidden", P_DEFAULT, &self->priv->is_hidden, &success, error) ||
+ gdata_parser_boolean_from_json_member (reader, "selected", P_DEFAULT, &self->priv->is_selected, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "summary", P_DEFAULT, &summary, &success, error)) {
+ if (success) {
+ if (summary != NULL) {
+ gdata_entry_set_title (GDATA_ENTRY (parsable), summary);
}
+ }
- gdata_calendar_calendar_set_color (self, &colour);
- xmlFree (value);
- } else if (xmlStrcmp (node->name, (xmlChar*) "selected") == 0) {
- /* gCal:selected */
- if (gdata_parser_boolean_from_property (node, "value", &(self->priv->is_selected), -1, error) == FALSE)
- return FALSE;
- } else if (xmlStrcmp (node->name, (xmlChar*) "accesslevel") == 0) {
- /* gCal:accesslevel */
- self->priv->access_level = (gchar*) xmlGetProp (node, (xmlChar*) "value");
- if (self->priv->access_level == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- } else {
- return GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+ g_free (summary);
+
+ return success;
+ } else if (g_strcmp0 (json_reader_get_member_name (reader), "id") == 0) {
+ GDataLink *_link;
+ const gchar *id;
+ gchar *uri;
+
+ /* Calendar entries don’t contain their own selfLink, so we have
+ * to add one manually. */
+ id = json_reader_get_string_value (reader);
+ if (id != NULL && *id != '\0') {
+ uri = g_strconcat ("https://www.googleapis.com/calendar/v3/calendars/", id, NULL);
+ _link = gdata_link_new (uri, GDATA_LINK_SELF);
+ gdata_entry_add_link (GDATA_ENTRY (parsable), _link);
+ g_object_unref (_link);
+ g_free (uri);
}
+
+ g_free (summary);
+ return GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->parse_json (parsable, reader, user_data, error);
} else {
- return GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+ g_free (summary);
+ return GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->parse_json (parsable, reader, user_data, error);
}
return TRUE;
}
static void
-get_xml (GDataParsable *parsable, GString *xml_string)
+get_json (GDataParsable *parsable, JsonBuilder *builder)
{
+ const gchar *id, *etag, *title;
gchar *colour;
GDataCalendarCalendarPrivate *priv = GDATA_CALENDAR_CALENDAR (parsable)->priv;
- /* Chain up to the parent class */
- GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->get_xml (parsable, xml_string);
+ id = gdata_entry_get_id (GDATA_ENTRY (parsable));
+ if (id != NULL) {
+ json_builder_set_member_name (builder, "id");
+ json_builder_add_string_value (builder, id);
+ }
- /* Add all the Calendar-specific XML */
- if (priv->timezone != NULL)
- gdata_parser_string_append_escaped (xml_string, "<gCal:timezone value='", priv->timezone, "'/>");
+ json_builder_set_member_name (builder, "kind");
+ json_builder_add_string_value (builder, "calendar#calendar");
- if (priv->is_hidden == TRUE)
- g_string_append (xml_string, "<gCal:hidden value='true'/>");
- else
- g_string_append (xml_string, "<gCal:hidden value='false'/>");
+ /* Add the ETag, if available. */
+ etag = gdata_entry_get_etag (GDATA_ENTRY (parsable));
+ if (etag != NULL) {
+ json_builder_set_member_name (builder, "etag");
+ json_builder_add_string_value (builder, etag);
+ }
+
+ /* Calendar labels titles as ‘summary’. */
+ title = gdata_entry_get_title (GDATA_ENTRY (parsable));
+ if (title != NULL) {
+ json_builder_set_member_name (builder, "summary");
+ json_builder_add_string_value (builder, title);
+ }
+
+ /* Add all the calendar-specific JSON */
+ if (priv->timezone != NULL) {
+ json_builder_set_member_name (builder, "timeZone");
+ json_builder_add_string_value (builder, priv->timezone);
+ }
- colour = gdata_color_to_hexadecimal (&(priv->colour));
- g_string_append_printf (xml_string, "<gCal:color value='%s'/>", colour);
+ json_builder_set_member_name (builder, "hidden");
+ json_builder_add_boolean_value (builder, priv->is_hidden);
+
+ colour = gdata_color_to_hexadecimal (&priv->colour);
+ json_builder_set_member_name (builder, "backgroundColor");
+ json_builder_add_string_value (builder, colour);
g_free (colour);
- if (priv->is_selected == TRUE)
- g_string_append (xml_string, "<gCal:selected value='true'/>");
- else
- g_string_append (xml_string, "<gCal:selected value='false'/>");
+ json_builder_set_member_name (builder, "selected");
+ json_builder_add_boolean_value (builder, priv->is_selected);
}
-static void
-get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+static const gchar *
+get_content_type (void)
{
- /* Chain up to the parent class */
- GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->get_namespaces (parsable, namespaces);
-
- g_hash_table_insert (namespaces, (gchar*) "gCal", (gchar*) "http://schemas.google.com/gCal/2005");
- g_hash_table_insert (namespaces, (gchar*) "app", (gchar*) "http://www.w3.org/2007/app");
+ return "application/json";
}
/**
diff --git a/gdata/services/calendar/gdata-calendar-event.c b/gdata/services/calendar/gdata-calendar-event.c
index 069bf338..16008e4d 100644
--- a/gdata/services/calendar/gdata-calendar-event.c
+++ b/gdata/services/calendar/gdata-calendar-event.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
* GData Client
- * Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
+ * Copyright (C) Philip Withnall 2009, 2010, 2014 <philip@tecnocode.co.uk>
*
* GData Client is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -84,7 +84,6 @@
#include <config.h>
#include <glib.h>
#include <glib/gi18n-lib.h>
-#include <libxml/parser.h>
#include <string.h>
#include "gdata-calendar-event.h"
@@ -99,9 +98,9 @@ static void gdata_calendar_event_dispose (GObject *object);
static void gdata_calendar_event_finalize (GObject *object);
static void gdata_calendar_event_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
static void gdata_calendar_event_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
-static void get_xml (GDataParsable *parsable, GString *xml_string);
-static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error);
-static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+static void get_json (GDataParsable *parsable, JsonBuilder *builder);
+static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
+static const gchar *get_content_type (void);
struct _GDataCalendarEventPrivate {
gint64 edited;
@@ -109,17 +108,27 @@ struct _GDataCalendarEventPrivate {
gchar *visibility;
gchar *transparency;
gchar *uid;
- guint sequence;
+ gint64 sequence;
GList *times; /* GDataGDWhen */
- guint guests_can_modify : 1;
- guint guests_can_invite_others : 1;
- guint guests_can_see_guests : 1;
- guint anyone_can_add_self : 1;
+ gboolean guests_can_modify;
+ gboolean guests_can_invite_others;
+ gboolean guests_can_see_guests;
+ gboolean anyone_can_add_self;
GList *people; /* GDataGDWho */
GList *places; /* GDataGDWhere */
gchar *recurrence;
gchar *original_event_id;
gchar *original_event_uri;
+
+ /* Parsing state. */
+ struct {
+ gint64 start_time;
+ gint64 end_time;
+ gboolean seen_start;
+ gboolean seen_end;
+ gboolean start_is_date;
+ gboolean end_is_date;
+ } parser;
};
enum {
@@ -155,11 +164,11 @@ gdata_calendar_event_class_init (GDataCalendarEventClass *klass)
gobject_class->dispose = gdata_calendar_event_dispose;
gobject_class->finalize = gdata_calendar_event_finalize;
- parsable_class->parse_xml = parse_xml;
- parsable_class->get_xml = get_xml;
- parsable_class->get_namespaces = get_namespaces;
+ parsable_class->parse_json = parse_json;
+ parsable_class->get_json = get_json;
+ parsable_class->get_content_type = get_content_type;
- entry_class->kind_term = "http://schemas.google.com/g/2005#event";
+ entry_class->kind_term = "calendar#event";
/**
* GDataCalendarEvent:edited:
@@ -438,7 +447,7 @@ gdata_calendar_event_get_property (GObject *object, guint property_id, GValue *v
g_value_set_string (value, priv->uid);
break;
case PROP_SEQUENCE:
- g_value_set_uint (value, priv->sequence);
+ g_value_set_uint (value, CLAMP (priv->sequence, 0, G_MAXUINT));
break;
case PROP_GUESTS_CAN_MODIFY:
g_value_set_boolean (value, priv->guests_can_modify);
@@ -512,182 +521,614 @@ gdata_calendar_event_set_property (GObject *object, guint property_id, const GVa
}
static gboolean
-parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+date_object_from_json (JsonReader *reader,
+ const gchar *member_name,
+ GDataParserOptions options,
+ gint64 *date_time_output,
+ gboolean *is_date_output,
+ gboolean *success,
+ GError **error)
+{
+ gint64 date_time;
+ gboolean is_date = FALSE;
+ gboolean found_member = FALSE;
+
+ /* Check if there’s such an element */
+ if (g_strcmp0 (json_reader_get_member_name (reader), member_name) != 0) {
+ return FALSE;
+ }
+
+ /* Check that it’s an object. */
+ if (!json_reader_is_object (reader)) {
+ const GError *child_error;
+
+ /* Manufacture an error. */
+ json_reader_read_member (reader, "dateTime");
+ child_error = json_reader_get_error (reader);
+ g_assert (child_error != NULL);
+ *success = gdata_parser_error_from_json_error (reader,
+ child_error,
+ error);
+ json_reader_end_member (reader);
+
+ return TRUE;
+ }
+
+ /* Try to parse either the dateTime or date member. */
+ if (json_reader_read_member (reader, "dateTime")) {
+ const gchar *date_string;
+ const GError *child_error;
+ GTimeVal time_val;
+
+ date_string = json_reader_get_string_value (reader);
+ child_error = json_reader_get_error (reader);
+
+ if (child_error != NULL) {
+ *success = gdata_parser_error_from_json_error (reader,
+ child_error,
+ error);
+ json_reader_end_member (reader);
+ return TRUE;
+ }
+
+ if (!g_time_val_from_iso8601 (date_string, &time_val)) {
+ *success = gdata_parser_error_not_iso8601_format_json (reader, date_string, error);
+ json_reader_end_member (reader);
+ return TRUE;
+ }
+
+ date_time = time_val.tv_sec;
+ is_date = FALSE;
+ found_member = TRUE;
+ }
+ json_reader_end_member (reader);
+
+ if (json_reader_read_member (reader, "date")) {
+ const gchar *date_string;
+ const GError *child_error;
+
+ date_string = json_reader_get_string_value (reader);
+ child_error = json_reader_get_error (reader);
+
+ if (child_error != NULL) {
+ *success = gdata_parser_error_from_json_error (reader,
+ child_error,
+ error);
+ json_reader_end_member (reader);
+ return TRUE;
+ }
+
+ if (!gdata_parser_int64_from_date (date_string, &date_time)) {
+ *success = gdata_parser_error_not_iso8601_format_json (reader, date_string, error);
+ json_reader_end_member (reader);
+ return TRUE;
+ }
+
+ is_date = TRUE;
+ found_member = TRUE;
+ }
+ json_reader_end_member (reader);
+
+ /* Ignore timeZone; it should be specified in dateTime. */
+ if (!found_member) {
+ *success = gdata_parser_error_required_json_content_missing (reader, error);
+ return TRUE;
+ }
+
+ *date_time_output = date_time;
+ *is_date_output = is_date;
+ *success = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
{
gboolean success;
+ gchar *summary = NULL, *description = NULL;
GDataCalendarEvent *self = GDATA_CALENDAR_EVENT (parsable);
- if (gdata_parser_is_namespace (node, "http://www.w3.org/2007/app") == TRUE &&
- gdata_parser_int64_time_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE) {
+g_message ("parsing %s", json_reader_get_member_name (reader));
+ /* TODO:
+ "htmlLink": string,
+ "created": datetime,
+ "location": string,
+ "colorId": string,
+ "creator": {
+ "id": string,
+ "email": string,
+ "displayName": string,
+ "self": boolean
+ },
+ "organizer": {
+ "id": string,
+ "email": string,
+ "displayName": string,
+ "self": boolean
+ },
+ "endTimeUnspecified": boolean,
+ "originalStartTime": {
+ "date": date,
+ "dateTime": datetime,
+ "timeZone": string
+ },
+ "attendees": [
+ {
+ "id": string,
+ "email": string,
+ "displayName": string,
+ "organizer": boolean,
+ "self": boolean,
+ "resource": boolean,
+ "optional": boolean,
+ "responseStatus": string,
+ "comment": string,
+ "additionalGuests": integer
+ }
+ ],
+ "attendeesOmitted": boolean,
+ "extendedProperties": {
+ "private": {
+ (key): string
+ },
+ "shared": {
+ (key): string
+ }
+ },
+ "hangoutLink": string,
+ "gadget": {
+ "type": string,
+ "title": string,
+ "link": string,
+ "iconLink": string,
+ "width": integer,
+ "height": integer,
+ "display": string,
+ "preferences": {
+ (key): string
+ }
+ },
+ "privateCopy": boolean,
+ "locked": boolean,
+ "reminders": {
+ "useDefault": boolean,
+ "overrides": [
+ {
+ "method": string,
+ "minutes": integer
+ }
+ ]
+ },
+ "source": {
+ "url": string,
+ "title": string
+ }
+
+
+who
+where
+originalEvent
+
+TODO: check visibility, transparency values match
+TODO: check status/eventStatus values match
+TODO: set original_event_uri iff original_event_id is set
+ */
+
+ if (g_strcmp0 (json_reader_get_member_name (reader), "start") == 0) {
+ self->priv->parser.seen_start = TRUE;
+ } else if (g_strcmp0 (json_reader_get_member_name (reader), "end") == 0) {
+ self->priv->parser.seen_end = TRUE;
+ }
+
+ if (gdata_parser_string_from_json_member (reader, "recurringEventId", P_DEFAULT, &self->priv->original_event_id, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "status", P_DEFAULT, &self->priv->status, &success, error) ||
+ gdata_parser_boolean_from_json_member (reader, "guestsCanModify", P_DEFAULT, &self->priv->guests_can_modify, &success, error) ||
+ gdata_parser_boolean_from_json_member (reader, "guestsCanInviteOthers", P_DEFAULT, &self->priv->guests_can_invite_others, &success, error) ||
+ gdata_parser_boolean_from_json_member (reader, "guestsCanSeeOtherGuests", P_DEFAULT, &self->priv->guests_can_see_guests, &success, error) ||
+ gdata_parser_boolean_from_json_member (reader, "anyoneCanAddSelf", P_DEFAULT, &self->priv->anyone_can_add_self, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "iCalUID", P_DEFAULT, &self->priv->uid, &success, error) ||
+ gdata_parser_int_from_json_member (reader, "sequence", P_DEFAULT, &self->priv->sequence, &success, error) ||
+ gdata_parser_int64_time_from_json_member (reader, "updated", P_DEFAULT, &self->priv->edited, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "transparency", P_DEFAULT, &self->priv->transparency, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "visibility", P_DEFAULT, &self->priv->visibility, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "summary", P_DEFAULT, &summary, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "description", P_DEFAULT, &description, &success, error) ||
+ date_object_from_json (reader, "start", P_DEFAULT, &self->priv->parser.start_time, &self->priv->parser.start_is_date, &success, error) ||
+ date_object_from_json (reader, "end", P_DEFAULT, &self->priv->parser.end_time, &self->priv->parser.end_is_date, &success, error) ||
+ gdata_parser_string_from_json_member (reader, "recurringEventId", P_DEFAULT, &self->priv->original_event_id, &success, error)) {
+ if (success) {
+ if (summary != NULL) {
+ gdata_entry_set_title (GDATA_ENTRY (parsable), summary);
+ }
+
+ if (description != NULL) {
+ gdata_entry_set_content (GDATA_ENTRY (parsable), description);
+ }
+
+ if (self->priv->edited != -1) {
+ _gdata_entry_set_updated (GDATA_ENTRY (parsable),
+ self->priv->edited);
+ }
+
+ if (self->priv->original_event_id != NULL) {
+ g_free (self->priv->original_event_uri);
+ self->priv->original_event_uri = g_strconcat ("https://www.googleapis.com/calendar/v3/events/",
+ self->priv->original_event_id, NULL);
+ }
+
+ if (self->priv->parser.seen_start && self->priv->parser.seen_end) {
+ GDataGDWhen *when;
+
+ when = gdata_gd_when_new (self->priv->parser.start_time,
+ self->priv->parser.end_time,
+ self->priv->parser.start_is_date ||
+ self->priv->parser.end_is_date);
+ self->priv->times = g_list_prepend (self->priv->times, when); /* transfer ownership */
+
+ self->priv->parser.seen_start = FALSE;
+ self->priv->parser.seen_end = FALSE;
+ }
+ }
+
+ g_free (summary);
+ g_free (description);
+
return success;
- } else if (gdata_parser_is_namespace (node, "http://schemas.google.com/g/2005") == TRUE) {
- if (gdata_parser_object_from_element_setter (node, "when", P_REQUIRED, GDATA_TYPE_GD_WHEN,
- gdata_calendar_event_add_time, self, &success, error) == TRUE ||
- gdata_parser_object_from_element_setter (node, "who", P_REQUIRED, GDATA_TYPE_GD_WHO,
- gdata_calendar_event_add_person, self, &success, error) == TRUE ||
- gdata_parser_object_from_element_setter (node, "where", P_REQUIRED, GDATA_TYPE_GD_WHERE,
- gdata_calendar_event_add_place, self, &success, error) == TRUE) {
- return success;
- } else if (xmlStrcmp (node->name, (xmlChar*) "eventStatus") == 0) {
- /* gd:eventStatus */
- xmlChar *value = xmlGetProp (node, (xmlChar*) "value");
- if (value == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- self->priv->status = (gchar*) value;
- } else if (xmlStrcmp (node->name, (xmlChar*) "visibility") == 0) {
- /* gd:visibility */
- xmlChar *value = xmlGetProp (node, (xmlChar*) "value");
- if (value == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- self->priv->visibility = (gchar*) value;
- } else if (xmlStrcmp (node->name, (xmlChar*) "transparency") == 0) {
- /* gd:transparency */
- xmlChar *value = xmlGetProp (node, (xmlChar*) "value");
- if (value == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- self->priv->transparency = (gchar*) value;
- } else if (xmlStrcmp (node->name, (xmlChar*) "recurrence") == 0) {
- /* gd:recurrence */
- self->priv->recurrence = (gchar*) xmlNodeListGetString (doc, node->children, TRUE);
- } else if (xmlStrcmp (node->name, (xmlChar*) "originalEvent") == 0) {
- /* gd:originalEvent */
- self->priv->original_event_id = (gchar*) xmlGetProp (node, (xmlChar*) "id");
- self->priv->original_event_uri = (gchar*) xmlGetProp (node, (xmlChar*) "href");
- } else {
- return GDATA_PARSABLE_CLASS (gdata_calendar_event_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+ } else if (g_strcmp0 (json_reader_get_member_name (reader), "recurrence") == 0) {
+ guint i, j;
+ GString *recurrence = NULL; /* owned */
+
+ /* In the JSON API, the recurrence is given as an array of
+ * strings, each giving an RFC 2445 property such as RRULE,
+ * EXRULE, RDATE or EXDATE. Concatenate them all to form a
+ * recurrence string as used in v2 of the API. */
+ if (!json_reader_is_array (reader)) {
+ /* TODO: error */
+ return FALSE;
+ } else if (self->priv->recurrence != NULL) {
+ /* TODO: error */
+ return FALSE;
}
- } else if (gdata_parser_is_namespace (node, "http://schemas.google.com/gCal/2005") == TRUE) {
- if (xmlStrcmp (node->name, (xmlChar*) "uid") == 0) {
- /* gCal:uid */
- xmlChar *value = xmlGetProp (node, (xmlChar*) "value");
- if (value == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- self->priv->uid = (gchar*) value;
- } else if (xmlStrcmp (node->name, (xmlChar*) "sequence") == 0) {
- /* gCal:sequence */
- xmlChar *value;
- guint value_uint;
-
- value = xmlGetProp (node, (xmlChar*) "value");
- if (value == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- else
- value_uint = g_ascii_strtoull ((gchar*) value, NULL, 10);
- xmlFree (value);
-
- gdata_calendar_event_set_sequence (self, value_uint);
- } else if (xmlStrcmp (node->name, (xmlChar*) "guestsCanModify") == 0) {
- /* gCal:guestsCanModify */
- gboolean guests_can_modify;
- if (gdata_parser_boolean_from_property (node, "value", &guests_can_modify, -1, error) == FALSE)
- return FALSE;
- gdata_calendar_event_set_guests_can_modify (self, guests_can_modify);
- } else if (xmlStrcmp (node->name, (xmlChar*) "guestsCanInviteOthers") == 0) {
- /* gCal:guestsCanInviteOthers */
- gboolean guests_can_invite_others;
- if (gdata_parser_boolean_from_property (node, "value", &guests_can_invite_others, -1, error) == FALSE)
- return FALSE;
- gdata_calendar_event_set_guests_can_invite_others (self, guests_can_invite_others);
- } else if (xmlStrcmp (node->name, (xmlChar*) "guestsCanSeeGuests") == 0) {
- /* gCal:guestsCanSeeGuests */
- gboolean guests_can_see_guests;
- if (gdata_parser_boolean_from_property (node, "value", &guests_can_see_guests, -1, error) == FALSE)
- return FALSE;
- gdata_calendar_event_set_guests_can_see_guests (self, guests_can_see_guests);
- } else if (xmlStrcmp (node->name, (xmlChar*) "anyoneCanAddSelf") == 0) {
- /* gCal:anyoneCanAddSelf */
- gboolean anyone_can_add_self;
- if (gdata_parser_boolean_from_property (node, "value", &anyone_can_add_self, -1, error) == FALSE)
- return FALSE;
- gdata_calendar_event_set_anyone_can_add_self (self, anyone_can_add_self);
- } else {
- return GDATA_PARSABLE_CLASS (gdata_calendar_event_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+
+ recurrence = g_string_new ("");
+
+ for (i = 0, j = json_reader_count_elements (reader); i < j; i++) {
+ const gchar *line;
+ const GError *child_error;
+
+ json_reader_read_element (reader, i);
+
+ line = json_reader_get_string_value (reader);
+ child_error = json_reader_get_error (reader);
+ if (child_error != NULL) {
+ return gdata_parser_error_from_json_error (reader, child_error, error);
+ }
+
+ g_string_append (recurrence, line);
+ g_string_append (recurrence, "\n");
+
+ json_reader_end_element (reader);
}
+
+ g_assert (self->priv->recurrence == NULL);
+ self->priv->recurrence = g_string_free (recurrence, FALSE);
+
+ return TRUE;
} else {
- return GDATA_PARSABLE_CLASS (gdata_calendar_event_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+ g_free (summary);
+ g_free (description);
+ return GDATA_PARSABLE_CLASS (gdata_calendar_event_parent_class)->parse_json (parsable, reader, user_data, error);
}
return TRUE;
}
static void
-get_child_xml (GList *list, GString *xml_string)
+get_json (GDataParsable *parsable, JsonBuilder *builder)
{
- GList *i;
+ GList *l;
+ const gchar *id, *etag, *title, *description;
+ GDataGDWho *organiser_who = NULL; /* unowned */
+ GDataCalendarEventPrivate *priv = GDATA_CALENDAR_EVENT (parsable)->priv;
- for (i = list; i != NULL; i = i->next)
- _gdata_parsable_get_xml (GDATA_PARSABLE (i->data), xml_string, FALSE);
-}
+/* TODO:
+ "htmlLink": string,
+ "created": datetime,
+ "colorId": string,
+ "creator": {
+ "id": string,
+ "email": string,
+ "displayName": string,
+ "self": boolean
+ },
+ "originalStartTime": {
+ "date": date,
+ "dateTime": datetime,
+ "timeZone": string
+ },
+ "attendeesOmitted": boolean,
+ "extendedProperties": {
+ "private": {
+ (key): string
+ },
+ "shared": {
+ (key): string
+ }
+ },
+ "hangoutLink": string,
+ "gadget": {
+ "type": string,
+ "title": string,
+ "link": string,
+ "iconLink": string,
+ "width": integer,
+ "height": integer,
+ "display": string,
+ "preferences": {
+ (key): string
+ }
+ },
+ "privateCopy": boolean,
+ "locked": boolean,
+ "reminders": {
+ "useDefault": boolean,
+ "overrides": [
+ {
+ "method": string,
+ "minutes": integer
+ }
+ ]
+ },
+ "source": {
+ "url": string,
+ "title": string
+ }
+*/
+
+
+
+ id = gdata_entry_get_id (GDATA_ENTRY (parsable));
+ if (id != NULL) {
+ json_builder_set_member_name (builder, "id");
+ json_builder_add_string_value (builder, id);
+ }
-static void
-get_xml (GDataParsable *parsable, GString *xml_string)
-{
- GDataCalendarEventPrivate *priv = GDATA_CALENDAR_EVENT (parsable)->priv;
+ json_builder_set_member_name (builder, "kind");
+ json_builder_add_string_value (builder, "calendar#event");
- /* Chain up to the parent class */
- GDATA_PARSABLE_CLASS (gdata_calendar_event_parent_class)->get_xml (parsable, xml_string);
+ /* Add the ETag, if available. */
+ etag = gdata_entry_get_etag (GDATA_ENTRY (parsable));
+ if (etag != NULL) {
+ json_builder_set_member_name (builder, "etag");
+ json_builder_add_string_value (builder, etag);
+ }
- /* Add all the Calendar-specific XML */
+ /* Calendar labels titles as ‘summary’. */
+ title = gdata_entry_get_title (GDATA_ENTRY (parsable));
+ if (title != NULL) {
+ json_builder_set_member_name (builder, "summary");
+ json_builder_add_string_value (builder, title);
+ }
- /* TODO: gd:comments? */
+ description = gdata_entry_get_content (GDATA_ENTRY (parsable));
+ if (description != NULL) {
+ json_builder_set_member_name (builder, "description");
+ json_builder_add_string_value (builder, description);
+ }
- if (priv->status != NULL)
- gdata_parser_string_append_escaped (xml_string, "<gd:eventStatus value='", priv->status, "'/>");
+ /* Add all the calendar-specific JSON */
+ json_builder_set_member_name (builder, "anyoneCanAddSelf");
+ json_builder_add_boolean_value (builder, priv->anyone_can_add_self);
- if (priv->visibility != NULL)
- gdata_parser_string_append_escaped (xml_string, "<gd:visibility value='", priv->visibility, "'/>");
+ json_builder_set_member_name (builder, "guestsCanInviteOthers");
+ json_builder_add_boolean_value (builder, priv->guests_can_invite_others);
- if (priv->transparency != NULL)
- gdata_parser_string_append_escaped (xml_string, "<gd:transparency value='", priv->transparency, "'/>");
+ json_builder_set_member_name (builder, "guestsCanModify");
+ json_builder_add_boolean_value (builder, priv->guests_can_modify);
- if (priv->uid != NULL)
- gdata_parser_string_append_escaped (xml_string, "<gCal:uid value='", priv->uid, "'/>");
+ json_builder_set_member_name (builder, "guestsCanSeeOtherGuests");
+ json_builder_add_boolean_value (builder, priv->guests_can_see_guests);
- if (priv->sequence != 0)
- g_string_append_printf (xml_string, "<gCal:sequence value='%u'/>", priv->sequence);
+ if (priv->transparency != NULL) {
+ json_builder_set_member_name (builder, "transparency");
+ json_builder_add_string_value (builder, priv->transparency);
+ }
- if (priv->guests_can_modify == TRUE)
- g_string_append (xml_string, "<gCal:guestsCanModify value='true'/>");
- else
- g_string_append (xml_string, "<gCal:guestsCanModify value='false'/>");
+ if (priv->visibility != NULL) {
+ json_builder_set_member_name (builder, "visibility");
+ json_builder_add_string_value (builder, priv->visibility);
+ }
- if (priv->guests_can_invite_others == TRUE)
- g_string_append (xml_string, "<gCal:guestsCanInviteOthers value='true'/>");
- else
- g_string_append (xml_string, "<gCal:guestsCanInviteOthers value='false'/>");
+ if (priv->uid != NULL) {
+ json_builder_set_member_name (builder, "iCalUID");
+ json_builder_add_string_value (builder, priv->uid);
+ }
- if (priv->guests_can_see_guests == TRUE)
- g_string_append (xml_string, "<gCal:guestsCanSeeGuests value='true'/>");
- else
- g_string_append (xml_string, "<gCal:guestsCanSeeGuests value='false'/>");
+ if (priv->sequence > 0) {
+ json_builder_set_member_name (builder, "sequence");
+ json_builder_add_int_value (builder, priv->sequence);
+ }
- if (priv->anyone_can_add_self == TRUE)
- g_string_append (xml_string, "<gCal:anyoneCanAddSelf value='true'/>");
- else
- g_string_append (xml_string, "<gCal:anyoneCanAddSelf value='false'/>");
+ if (priv->status != NULL) {
+ json_builder_set_member_name (builder, "status");
+ json_builder_add_string_value (builder, priv->status);
+ }
- if (priv->recurrence != NULL)
- gdata_parser_string_append_escaped (xml_string, "<gd:recurrence>", priv->recurrence, "</gd:recurrence>");
+ if (priv->recurrence != NULL) {
+ gchar **parts;
+ guint i;
- get_child_xml (priv->times, xml_string);
- get_child_xml (priv->people, xml_string);
- get_child_xml (priv->places, xml_string);
+ json_builder_set_member_name (builder, "recurrence");
+ json_builder_begin_array (builder);
- /* TODO:
- * - Finish supporting all tags
- * - Check all tags here are valid for insertions and updates
- */
+ parts = g_strsplit (priv->recurrence, "\n", -1);
+
+ for (i = 0; parts[i] != NULL; i++) {
+ json_builder_add_string_value (builder, parts[i]);
+ }
+
+ g_strfreev (parts);
+
+ json_builder_end_array (builder);
+ }
+
+ if (priv->original_event_id != NULL) {
+ json_builder_set_member_name (builder, "recurringEventId");
+ json_builder_add_string_value (builder, priv->original_event_id);
+ }
+
+ /* Times. */
+ for (l = priv->times; l != NULL; l = l->next) {
+ GDataGDWhen *when; /* unowned */
+ gchar *val = NULL; /* owned */
+ const gchar *member_name;
+ gint64 start_time, end_time;
+
+ when = l->data;
+
+ /* Start time. */
+ start_time = gdata_gd_when_get_start_time (when);
+ json_builder_set_member_name (builder, "start");
+ json_builder_begin_object (builder);
+
+ if (gdata_gd_when_is_date (when)) {
+ member_name = "date";
+ val = gdata_parser_date_from_int64 (start_time);
+ } else {
+ member_name = "dateTime";
+ val = gdata_parser_int64_to_iso8601 (start_time);
+ }
+
+ json_builder_set_member_name (builder, member_name);
+ json_builder_add_string_value (builder, val);
+ g_free (val);
+
+ json_builder_set_member_name (builder, "timeZone");
+ json_builder_add_string_value (builder, "UTC");
+
+ json_builder_end_object (builder);
+
+ /* End time. */
+ end_time = gdata_gd_when_get_end_time (when);
+
+ if (end_time > -1) {
+ json_builder_set_member_name (builder, "end");
+ json_builder_begin_object (builder);
+
+ if (gdata_gd_when_is_date (when)) {
+ member_name = "date";
+ val = gdata_parser_date_from_int64 (end_time);
+ } else {
+ member_name = "dateTime";
+ val = gdata_parser_int64_to_iso8601 (end_time);
+ }
+
+ json_builder_set_member_name (builder, member_name);
+ json_builder_add_string_value (builder, val);
+ g_free (val);
+
+ json_builder_set_member_name (builder, "timeZone");
+ json_builder_add_string_value (builder, "UTC");
+
+ json_builder_end_object (builder);
+ } else {
+ json_builder_set_member_name (builder, "endTimeUnspecified");
+ json_builder_add_boolean_value (builder, TRUE);
+ }
+
+ /* Only use the first time. :-(
+ * FIXME: There must be a better solution. */
+ if (l->next != NULL) {
+ g_warning ("Ignoring secondary times; they are no "
+ "longer supported by the server-side API.");
+ break;
+ }
+ }
+
+ /* Locations. */
+ for (l = priv->places; l != NULL; l = l->next) {
+ GDataGDWhere *where; /* unowned */
+ const gchar *location;
+
+ where = l->data;
+ location = gdata_gd_where_get_value_string (where);
+
+ json_builder_set_member_name (builder, "location");
+ json_builder_add_string_value (builder, location);
+
+ /* Only use the first location. :-(
+ * FIXME: There must be a better solution. */
+ if (l->next != NULL) {
+ g_warning ("Ignoring secondary locations; they are no "
+ "longer supported by the server-side API.");
+ break;
+ }
+ }
+
+ /* People. */
+ json_builder_set_member_name (builder, "attendees");
+ json_builder_begin_array (builder);
+
+ for (l = priv->people; l != NULL; l = l->next) {
+ GDataGDWho *who; /* unowned */
+ const gchar *display_name, *email_address;
+
+ who = l->data;
+
+ json_builder_begin_object (builder);
+
+ display_name = gdata_gd_who_get_value_string (who);
+ if (display_name != NULL) {
+ json_builder_set_member_name (builder, "displayName");
+ json_builder_add_string_value (builder, display_name);
+ }
+
+ email_address = gdata_gd_who_get_email_address (who);
+ if (email_address != NULL) {
+ json_builder_set_member_name (builder, "email");
+ json_builder_add_string_value (builder, email_address);
+ }
+
+ if (g_strcmp0 (gdata_gd_who_get_relation_type (who),
+ GDATA_GD_WHO_EVENT_ORGANIZER) == 0) {
+ json_builder_set_member_name (builder, "organizer");
+ json_builder_add_boolean_value (builder, TRUE);
+
+ organiser_who = who;
+ }
+
+ json_builder_end_object (builder);
+ }
+
+ json_builder_end_array (builder);
+
+ if (organiser_who != NULL) {
+ const gchar *display_name, *email_address;
+
+ json_builder_set_member_name (builder, "organizer");
+ json_builder_begin_object (builder);
+
+ display_name = gdata_gd_who_get_value_string (organiser_who);
+ if (display_name != NULL) {
+ json_builder_set_member_name (builder, "displayName");
+ json_builder_add_string_value (builder, display_name);
+ }
+
+ email_address = gdata_gd_who_get_email_address (organiser_who);
+ if (email_address != NULL) {
+ json_builder_set_member_name (builder, "email");
+ json_builder_add_string_value (builder, email_address);
+ }
+
+ json_builder_end_object (builder);
+ }
}
-static void
-get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+static const gchar *
+get_content_type (void)
{
- /* Chain up to the parent class */
- GDATA_PARSABLE_CLASS (gdata_calendar_event_parent_class)->get_namespaces (parsable, namespaces);
-
- g_hash_table_insert (namespaces, (gchar*) "gd", (gchar*) "http://schemas.google.com/g/2005");
- g_hash_table_insert (namespaces, (gchar*) "gCal", (gchar*) "http://schemas.google.com/gCal/2005");
- g_hash_table_insert (namespaces, (gchar*) "app", (gchar*) "http://www.w3.org/2007/app");
+ return "application/json";
}
/**
@@ -872,7 +1313,7 @@ guint
gdata_calendar_event_get_sequence (GDataCalendarEvent *self)
{
g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), 0);
- return self->priv->sequence;
+ return CLAMP (self->priv->sequence, 0, G_MAXUINT);
}
/**
diff --git a/gdata/services/calendar/gdata-calendar-feed.c b/gdata/services/calendar/gdata-calendar-feed.c
index ce0ca915..78a67203 100644
--- a/gdata/services/calendar/gdata-calendar-feed.c
+++ b/gdata/services/calendar/gdata-calendar-feed.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
* GData Client
- * Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
+ * Copyright (C) Philip Withnall 2009, 2010, 2014 <philip@tecnocode.co.uk>
*
* GData Client is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -25,6 +25,8 @@
*
* #GDataCalendarFeed is a subclass of #GDataFeed to represent a results feed from Google Calendar. It adds a couple of
* properties which are specific to the Google Calendar API.
+ *
+ * TODO: deprecate things?
**/
#include <glib.h>
@@ -34,14 +36,7 @@
#include "gdata-feed.h"
#include "gdata-private.h"
-static void gdata_calendar_feed_finalize (GObject *object);
static void gdata_calendar_feed_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
-static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error);
-
-struct _GDataCalendarFeedPrivate {
- gchar *timezone;
- guint times_cleaned;
-};
enum {
PROP_TIMEZONE = 1,
@@ -54,14 +49,8 @@ static void
gdata_calendar_feed_class_init (GDataCalendarFeedClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (GDataCalendarFeedPrivate));
gobject_class->get_property = gdata_calendar_feed_get_property;
- gobject_class->finalize = gdata_calendar_feed_finalize;
-
- parsable_class->parse_xml = parse_xml;
/**
* GDataCalendarFeed:timezone:
@@ -94,31 +83,22 @@ gdata_calendar_feed_class_init (GDataCalendarFeedClass *klass)
static void
gdata_calendar_feed_init (GDataCalendarFeed *self)
{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_CALENDAR_FEED, GDataCalendarFeedPrivate);
-}
-
-static void
-gdata_calendar_feed_finalize (GObject *object)
-{
- GDataCalendarFeedPrivate *priv = GDATA_CALENDAR_FEED (object)->priv;
-
- g_free (priv->timezone);
-
- /* Chain up to the parent class */
- G_OBJECT_CLASS (gdata_calendar_feed_parent_class)->finalize (object);
+ /* Nothing to see here. */
}
static void
gdata_calendar_feed_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
- GDataCalendarFeedPrivate *priv = GDATA_CALENDAR_FEED (object)->priv;
+ GDataCalendarFeed *self = GDATA_CALENDAR_FEED (object);
switch (property_id) {
case PROP_TIMEZONE:
- g_value_set_string (value, priv->timezone);
+ g_value_set_string (value,
+ gdata_calendar_feed_get_timezone (self));
break;
case PROP_TIMES_CLEANED:
- g_value_set_uint (value, priv->times_cleaned);
+ g_value_set_uint (value,
+ gdata_calendar_feed_get_times_cleaned (self));
break;
default:
/* We don't have any other property... */
@@ -127,35 +107,6 @@ gdata_calendar_feed_get_property (GObject *object, guint property_id, GValue *va
}
}
-static gboolean
-parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
-{
- GDataCalendarFeed *self = GDATA_CALENDAR_FEED (parsable);
-
- if (gdata_parser_is_namespace (node, "http://schemas.google.com/gCal/2005") == FALSE)
- return GDATA_PARSABLE_CLASS (gdata_calendar_feed_parent_class)->parse_xml (parsable, doc, node, user_data, error);
-
- if (xmlStrcmp (node->name, (xmlChar*) "timezone") == 0) {
- /* gCal:timezone */
- xmlChar *_timezone = xmlGetProp (node, (xmlChar*) "value");
- if (_timezone == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- g_free (self->priv->timezone);
- self->priv->timezone = (gchar*) _timezone;
- } else if (xmlStrcmp (node->name, (xmlChar*) "timesCleaned") == 0) {
- /* gCal:timesCleaned */
- xmlChar *times_cleaned = xmlGetProp (node, (xmlChar*) "value");
- if (times_cleaned == NULL)
- return gdata_parser_error_required_property_missing (node, "value", error);
- self->priv->times_cleaned = g_ascii_strtoull ((gchar*) times_cleaned, NULL, 10);
- xmlFree (times_cleaned);
- } else {
- return GDATA_PARSABLE_CLASS (gdata_calendar_feed_parent_class)->parse_xml (parsable, doc, node, user_data, error);
- }
-
- return TRUE;
-}
-
/**
* gdata_calendar_feed_get_timezone:
* @self: a #GDataCalendarFeed
@@ -169,8 +120,9 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
const gchar *
gdata_calendar_feed_get_timezone (GDataCalendarFeed *self)
{
+ /* Not supported any more by version 3 of the API. */
g_return_val_if_fail (GDATA_IS_CALENDAR_FEED (self), NULL);
- return self->priv->timezone;
+ return NULL;
}
/**
@@ -186,6 +138,7 @@ gdata_calendar_feed_get_timezone (GDataCalendarFeed *self)
guint
gdata_calendar_feed_get_times_cleaned (GDataCalendarFeed *self)
{
+ /* Not supported any more by version 3 of the API. */
g_return_val_if_fail (GDATA_IS_CALENDAR_FEED (self), 0);
- return self->priv->times_cleaned;
+ return 0;
}
diff --git a/gdata/services/calendar/gdata-calendar-service.c b/gdata/services/calendar/gdata-calendar-service.c
index e326eabd..11f270ec 100644
--- a/gdata/services/calendar/gdata-calendar-service.c
+++ b/gdata/services/calendar/gdata-calendar-service.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
* GData Client
- * Copyright (C) Philip Withnall 2009 <philip@tecnocode.co.uk>
+ * Copyright (C) Philip Withnall 2009, 2014 <philip@tecnocode.co.uk>
*
* GData Client is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -15,6 +15,11 @@
*
* You should have received a copy of the GNU Lesser General Public
* License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * TODO: ACLs, batchable
+ * TODO: update links to references; proofread documentation
+ * TODO: add new API to avoid naked calls to gdata_service_insert_entry() (for example)
+ * TODO: port Evo and Geary code
*/
/**
@@ -215,6 +220,14 @@
/* Standards reference here: http://code.google.com/apis/calendar/docs/2.0/reference.html */
+static void
+parse_error_response (GDataService *self,
+ GDataOperationType operation_type,
+ guint status,
+ const gchar *reason_phrase,
+ const gchar *response_body,
+ gint length,
+ GError **error);
static GList *get_authorization_domains (void);
_GDATA_DEFINE_AUTHORIZATION_DOMAIN (calendar, "cl", "https://www.google.com/calendar/feeds/")
@@ -225,6 +238,7 @@ gdata_calendar_service_class_init (GDataCalendarServiceClass *klass)
{
GDataServiceClass *service_class = GDATA_SERVICE_CLASS (klass);
service_class->feed_type = GDATA_TYPE_CALENDAR_FEED;
+ service_class->parse_error_response = parse_error_response;
service_class->get_authorization_domains = get_authorization_domains;
}
@@ -234,6 +248,212 @@ gdata_calendar_service_init (GDataCalendarService *self)
/* Nothing to see here */
}
+/* The error format used by the Google Calendar API doesn’t seem to be
+ * documented anywhere, which is a little frustrating. Here’s an example of it:
+ * {
+ * "error": {
+ * "errors": [
+ * {
+ * "domain": "global",
+ * "reason": "parseError",
+ * "message": "Parse Error",
+ * }
+ * ],
+ * "code": 400,
+ * "message": "Parse Error"
+ * }
+ * }
+ * or:
+ * {
+ * "error": {
+ * "errors": [
+ * {
+ * "domain": "global",
+ * "reason": "required",
+ * "message": "Missing end time."
+ * }
+ * ],
+ * "code": 400,
+ * "message": "Missing end time."
+ * }
+ * }
+ */
+static void
+parse_error_response (GDataService *self,
+ GDataOperationType operation_type,
+ guint status,
+ const gchar *reason_phrase,
+ const gchar *response_body,
+ gint length,
+ GError **error)
+{
+ JsonParser *parser = NULL; /* owned */
+ JsonReader *reader = NULL; /* owned */
+ gint i;
+ GError *child_error = NULL;
+
+ if (response_body == NULL) {
+ goto parent;
+ }
+
+ if (length == -1) {
+ length = strlen (response_body);
+ }
+
+ parser = json_parser_new ();
+ if (!json_parser_load_from_data (parser, response_body, length,
+ &child_error)) {
+ goto parent;
+ }
+
+ reader = json_reader_new (json_parser_get_root (parser));
+
+ /* Check that the outermost node is an object. */
+ if (!json_reader_is_object (reader)) {
+ goto parent;
+ }
+
+ /* Grab the ‘error’ member, then its ‘errors’ member. */
+ if (!json_reader_read_member (reader, "error") ||
+ !json_reader_is_object (reader) ||
+ !json_reader_read_member (reader, "errors") ||
+ !json_reader_is_array (reader)) {
+ goto parent;
+ }
+
+ /* Parse each of the errors. Return the first one, and print out any
+ * others. */
+ for (i = 0; i < json_reader_count_elements (reader); i++) {
+ const gchar *domain, *reason, *message, *extended_help;
+ const gchar *location_type, *location;
+
+ /* Parse the error. */
+ if (!json_reader_read_element (reader, i) ||
+ !json_reader_is_object (reader)) {
+ goto parent;
+ }
+
+ json_reader_read_member (reader, "domain");
+ domain = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+
+ json_reader_read_member (reader, "reason");
+ reason = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+
+ json_reader_read_member (reader, "message");
+ message = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+
+ json_reader_read_member (reader, "extendedHelp");
+ extended_help = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+
+ json_reader_read_member (reader, "locationType");
+ location_type = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+
+ json_reader_read_member (reader, "location");
+ location = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+
+ /* End the error element. */
+ json_reader_end_element (reader);
+
+ /* Create an error message, but only for the first error */
+ if (error == NULL || *error == NULL) {
+ if (g_strcmp0 (domain, "usageLimits") == 0 &&
+ g_strcmp0 (reason,
+ "dailyLimitExceededUnreg") == 0) {
+ /* Daily Limit for Unauthenticated Use
+ * Exceeded. */
+ g_set_error (error, GDATA_SERVICE_ERROR,
+ GDATA_SERVICE_ERROR_API_QUOTA_EXCEEDED,
+ _("You have made too many API "
+ "calls recently. Please wait a "
+ "few minutes and try again."));
+ } else if (g_strcmp0 (domain, "global") == 0 &&
+ g_strcmp0 (reason, "notFound") == 0) {
+ /* Calendar not found. */
+ g_set_error (error, GDATA_SERVICE_ERROR,
+ GDATA_SERVICE_ERROR_NOT_FOUND,
+ /* Translators: the parameter is an
+ * error message returned by the
+ * server. */
+ _("The requested resource was not found: %s"),
+ message);
+ } else if ((g_strcmp0 (domain, "global") == 0 &&
+ g_strcmp0 (reason, "required") == 0) ||
+ (g_strcmp0 (domain, "global") == 0 &&
+ g_strcmp0 (reason, "conditionNotMet") == 0)) {
+ /* Client-side protocol error. */
+ g_set_error (error, GDATA_SERVICE_ERROR,
+ GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
+ /* Translators: the parameter is an
+ * error message returned by the
+ * server. */
+ _("Invalid request URI or header, "
+ "or unsupported nonstandard "
+ "parameter: %s"), message);
+ } else if (g_strcmp0 (domain, "global") == 0 &&
+ (g_strcmp0 (reason, "authError") == 0 ||
+ g_strcmp0 (reason, "required") == 0)) {
+ /* Authentication problem */
+ g_set_error (error, GDATA_SERVICE_ERROR,
+ GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
+ _("You must be authenticated to "
+ "do this."));
+ } else if (g_strcmp0 (domain, "global") == 0 &&
+ g_strcmp0 (reason, "forbidden") == 0) {
+ g_set_error (error, GDATA_SERVICE_ERROR,
+ GDATA_SERVICE_ERROR_FORBIDDEN,
+ _("Access was denied by the user "
+ "or server."));
+ } else {
+ /* Unknown or validation (protocol) error. Fall
+ * back to working off the HTTP status code. */
+ g_warning ("Unknown error code ‘%s’ in domain "
+ "‘%s’ received with location type "
+ "‘%s’, location ‘%s’, extended help "
+ "‘%s’ and message ‘%s’.",
+ reason, domain, location_type,
+ location, extended_help, message);
+
+ goto parent;
+ }
+ } else {
+ /* For all errors after the first, log the error in the
+ * terminal. */
+ g_debug ("Error message received in response: domain "
+ "‘%s’, reason ‘%s’, extended help ‘%s’, "
+ "message ‘%s’, location type ‘%s’, location "
+ "‘%s’.",
+ domain, reason, extended_help, message,
+ location_type, location);
+ }
+ }
+
+ /* End the ‘errors’ and ‘error’ members. */
+ json_reader_end_element (reader);
+ json_reader_end_element (reader);
+
+ g_clear_object (&reader);
+ g_clear_object (&parser);
+
+ /* Ensure we’ve actually set an error message. */
+ g_assert (error == NULL || *error != NULL);
+
+ return;
+
+parent:
+ g_clear_object (&reader);
+ g_clear_object (&parser);
+
+ /* Chain up to the parent class */
+ GDATA_SERVICE_CLASS (gdata_calendar_service_parent_class)->parse_error_response (self, operation_type, status, reason_phrase,
+ response_body, length, error);
+}
+
static GList *
get_authorization_domains (void)
{
@@ -316,7 +536,7 @@ gdata_calendar_service_query_all_calendars (GDataCalendarService *self, GDataQue
return NULL;
}
- request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.google.com/calendar/feeds/default/allcalendars/full", NULL);
+ request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList", NULL);
feed = gdata_service_query (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
cancellable, progress_callback, progress_user_data, error);
g_free (request_uri);
@@ -370,7 +590,7 @@ gdata_calendar_service_query_all_calendars_async (GDataCalendarService *self, GD
return;
}
- request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.google.com/calendar/feeds/default/allcalendars/full", NULL);
+ request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList", NULL);
gdata_service_query_async (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
cancellable, progress_callback, progress_user_data, destroy_progress_user_data, callback, user_data);
g_free (request_uri);
@@ -413,7 +633,7 @@ gdata_calendar_service_query_own_calendars (GDataCalendarService *self, GDataQue
return NULL;
}
- request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.google.com/calendar/feeds/default/owncalendars/full", NULL);
+ request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList?minAccessRole=owner", NULL);
feed = gdata_service_query (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
cancellable, progress_callback, progress_user_data, error);
g_free (request_uri);
@@ -467,12 +687,62 @@ gdata_calendar_service_query_own_calendars_async (GDataCalendarService *self, GD
return;
}
- request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.google.com/calendar/feeds/default/owncalendars/full", NULL);
+ request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList?minAccessRole=owner", NULL);
gdata_service_query_async (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
cancellable, progress_callback, progress_user_data, destroy_progress_user_data, callback, user_data);
g_free (request_uri);
}
+static gchar *
+build_events_uri (GDataCalendarCalendar *calendar)
+{
+ GString *uri;
+ const gchar *calendar_id;
+
+ calendar_id = (calendar != NULL) ? gdata_entry_get_id (GDATA_ENTRY (calendar)) : "default";
+
+ uri = g_string_new (_gdata_service_get_scheme ());
+ g_string_append (uri, "://www.googleapis.com/calendar/v3/calendars/");
+ g_string_append_uri_escaped (uri, calendar_id, NULL, FALSE);
+ g_string_append (uri, "/events");
+
+ return g_string_free (uri, FALSE);
+}
+
+static void
+set_event_id (GDataCalendarEvent *event,
+ GDataCalendarCalendar *calendar)
+{
+ GDataLink *_link = NULL; /* owned */
+ const gchar *id, *calendar_id;
+ gchar *uri = NULL; /* owned */
+
+ id = gdata_entry_get_id (GDATA_ENTRY (event));
+ calendar_id = gdata_entry_get_id (GDATA_ENTRY (calendar));
+
+ uri = g_strconcat ("https://www.googleapis.com/calendar/v3/calendars/",
+ calendar_id, "/events/", id, NULL);
+ _link = gdata_link_new (uri, GDATA_LINK_SELF);
+ gdata_entry_add_link (GDATA_ENTRY (event), _link);
+ g_object_unref (_link);
+ g_free (uri);
+}
+
+static void
+set_event_ids (GDataFeed *feed,
+ GDataCalendarCalendar *calendar)
+{
+ GList/*<unowned GDataEntry>*/ *entries, *i; /* unowned */
+
+ /* Add a selfLink to each event, since they don’t contain one by
+ * default in the data returned by the server. */
+ entries = gdata_feed_get_entries (feed);
+
+ for (i = entries; i != NULL; i = i->next) {
+ set_event_id (GDATA_CALENDAR_EVENT (i->data), calendar);
+ }
+}
+
/**
* gdata_calendar_service_query_events:
* @self: a #GDataCalendarService
@@ -493,7 +763,8 @@ GDataFeed *
gdata_calendar_service_query_events (GDataCalendarService *self, GDataCalendarCalendar *calendar, GDataQuery *query, GCancellable *cancellable,
GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
{
- const gchar *uri;
+ gchar *request_uri;
+ GDataFeed *feed;
g_return_val_if_fail (GDATA_IS_CALENDAR_SERVICE (self), NULL);
g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (calendar), NULL);
@@ -509,18 +780,20 @@ gdata_calendar_service_query_events (GDataCalendarService *self, GDataCalendarCa
return NULL;
}
- /* Use the calendar's content src */
- uri = gdata_entry_get_content_uri (GDATA_ENTRY (calendar));
- if (uri == NULL) {
- /* Erroring out is probably the safest thing to do */
- g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
- _("The calendar did not have a content URI."));
- return NULL;
- }
+ /* Execute the query. */
+ request_uri = build_events_uri (calendar);
+ feed = gdata_service_query (GDATA_SERVICE (self),
+ get_calendar_authorization_domain (),
+ request_uri, query,
+ GDATA_TYPE_CALENDAR_EVENT, cancellable,
+ progress_callback, progress_user_data,
+ error);
+ g_free (request_uri);
- /* Execute the query */
- return gdata_service_query (GDATA_SERVICE (self), get_calendar_authorization_domain (), uri, query, GDATA_TYPE_CALENDAR_EVENT, cancellable,
- progress_callback, progress_user_data, error);
+ /* Set the events’ IDs. */
+ set_event_ids (feed, calendar);
+
+ return feed;
}
/**
@@ -552,7 +825,7 @@ gdata_calendar_service_query_events_async (GDataCalendarService *self, GDataCale
GDestroyNotify destroy_progress_user_data,
GAsyncReadyCallback callback, gpointer user_data)
{
- const gchar *uri;
+ gchar *request_uri;
g_return_if_fail (GDATA_IS_CALENDAR_SERVICE (self));
g_return_if_fail (GDATA_IS_CALENDAR_CALENDAR (calendar));
@@ -572,22 +845,18 @@ gdata_calendar_service_query_events_async (GDataCalendarService *self, GDataCale
return;
}
- /* Use the calendar's content src */
- uri = gdata_entry_get_content_uri (GDATA_ENTRY (calendar));
- if (uri == NULL) {
- /* Erroring out is probably the safest thing to do */
- GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_service_query_async);
- g_simple_async_result_set_error (result, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED, "%s",
- _("The calendar did not have a content URI."));
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
-
- return;
- }
-
- /* Execute the query */
- gdata_service_query_async (GDATA_SERVICE (self), get_calendar_authorization_domain (), uri, query, GDATA_TYPE_CALENDAR_EVENT, cancellable,
- progress_callback, progress_user_data, destroy_progress_user_data, callback, user_data);
+ /* Execute the query. */
+ request_uri = build_events_uri (calendar);
+ gdata_service_query_async (GDATA_SERVICE (self),
+ get_calendar_authorization_domain (),
+ request_uri, query,
+ GDATA_TYPE_CALENDAR_EVENT, cancellable,
+ progress_callback, progress_user_data,
+ destroy_progress_user_data, callback,
+ user_data);
+/* TODO: Set the events’ IDs.
+ set_event_ids (feed, calendar); */
+ g_free (request_uri);
}
/**
@@ -602,6 +871,7 @@ gdata_calendar_service_query_events_async (GDataCalendarService *self, GDataCale
* For more details, see gdata_service_insert_entry().
*
* Return value: (transfer full): an updated #GDataCalendarEvent, or %NULL; unref with g_object_unref()
+ * TODO: Deprecate
*
* Since: 0.2.0
**/
@@ -617,7 +887,7 @@ gdata_calendar_service_insert_event (GDataCalendarService *self, GDataCalendarEv
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
- uri = g_strconcat (_gdata_service_get_scheme (), "://www.google.com/calendar/feeds/default/private/full", NULL);
+ uri = build_events_uri (NULL);
entry = gdata_service_insert_entry (GDATA_SERVICE (self), get_calendar_authorization_domain (), uri, GDATA_ENTRY (event), cancellable, error);
g_free (uri);
@@ -625,6 +895,52 @@ gdata_calendar_service_insert_event (GDataCalendarService *self, GDataCalendarEv
}
/**
+ * gdata_calendar_service_insert_calendar_event:
+ * @self: a #GDataCalendarService
+ * @calendar: the #GDataCalendarCalendar to insert the event into
+ * @event: the #GDataCalendarEvent to insert
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Inserts @event by uploading it to the online calendar service, adding it to
+ * the specified @calendar.
+ *
+ * For more details, see gdata_service_insert_entry().
+ *
+ * Return value: (transfer full): an updated #GDataCalendarEvent, or %NULL;
+ * unref with g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataCalendarEvent *
+gdata_calendar_service_insert_calendar_event (GDataCalendarService *self,
+ GDataCalendarCalendar *calendar,
+ GDataCalendarEvent *event,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar *uri;
+ GDataEntry *entry;
+
+ g_return_val_if_fail (GDATA_IS_CALENDAR_SERVICE (self), NULL);
+ g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (calendar), NULL);
+ g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (event), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ uri = build_events_uri (calendar);
+ entry = gdata_service_insert_entry (GDATA_SERVICE (self),
+ get_calendar_authorization_domain (),
+ uri, GDATA_ENTRY (event),
+ cancellable, error);
+ g_free (uri);
+
+ set_event_id (GDATA_CALENDAR_EVENT (entry), calendar);
+
+ return GDATA_CALENDAR_EVENT (entry);
+}
+
+/**
* gdata_calendar_service_insert_event_async:
* @self: a #GDataCalendarService
* @event: the #GDataCalendarEvent to insert
@@ -640,6 +956,7 @@ gdata_calendar_service_insert_event (GDataCalendarService *self, GDataCalendarEv
*
* For more details, see gdata_calendar_service_insert_event(), which is the synchronous version of this function, and
* gdata_service_insert_entry_async(), which is the base asynchronous insertion function.
+ * TODO: deprecate
*
* Since: 0.8.0
**/
@@ -653,8 +970,53 @@ gdata_calendar_service_insert_event_async (GDataCalendarService *self, GDataCale
g_return_if_fail (GDATA_IS_CALENDAR_EVENT (event));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- uri = g_strconcat (_gdata_service_get_scheme (), "://www.google.com/calendar/feeds/default/private/full", NULL);
+ uri = build_events_uri (NULL);
gdata_service_insert_entry_async (GDATA_SERVICE (self), get_calendar_authorization_domain (), uri, GDATA_ENTRY (event), cancellable,
callback, user_data);
g_free (uri);
}
+
+/**
+ * gdata_calendar_service_insert_calendar_event_async:
+ * @self: a #GDataCalendarService
+ * @calendar: the #GDataCalendarCalendar to insert the event into
+ * @event: the #GDataCalendarEvent to insert
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when insertion is finished
+ * @user_data: (closure): data to pass to the @callback function
+ *
+ * Inserts @event by uploading it to the online calendar service, adding it to
+ * the specified @calendar. @self and @event are both reffed when this function
+ * is called, so can safely be unreffed after this function returns.
+ *
+ * @callback should call gdata_service_insert_entry_finish() to obtain a
+ * #GDataCalendarEvent representing the inserted event and to check for possible
+ * errors.
+ *
+ * For more details, see gdata_calendar_service_insert_event(), which is the
+ * synchronous version of this function, and gdata_service_insert_entry_async(),
+ * which is the base asynchronous insertion function.
+ *
+ * Since: UNRELEASED
+ */
+void
+gdata_calendar_service_insert_calendar_event_async (GDataCalendarService *self,
+ GDataCalendarCalendar *calendar,
+ GDataCalendarEvent *event,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ gchar *uri;
+
+ g_return_if_fail (GDATA_IS_CALENDAR_SERVICE (self));
+ g_return_if_fail (GDATA_IS_CALENDAR_EVENT (event));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ uri = build_events_uri (calendar);
+ gdata_service_insert_entry_async (GDATA_SERVICE (self),
+ get_calendar_authorization_domain (),
+ uri, GDATA_ENTRY (event), cancellable,
+ callback, user_data);
+ g_free (uri);/* TODO: self link */
+}
diff --git a/gdata/services/calendar/gdata-calendar-service.h b/gdata/services/calendar/gdata-calendar-service.h
index 8177491f..ffc63ac2 100644
--- a/gdata/services/calendar/gdata-calendar-service.h
+++ b/gdata/services/calendar/gdata-calendar-service.h
@@ -100,6 +100,20 @@ GDataCalendarEvent *gdata_calendar_service_insert_event (GDataCalendarService *s
void gdata_calendar_service_insert_event_async (GDataCalendarService *self, GDataCalendarEvent *event, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer user_data);
+GDataCalendarEvent *
+gdata_calendar_service_insert_calendar_event (GDataCalendarService *self,
+ GDataCalendarCalendar *calendar,
+ GDataCalendarEvent *event,
+ GCancellable *cancellable,
+ GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+void
+gdata_calendar_service_insert_calendar_event_async (GDataCalendarService *self,
+ GDataCalendarCalendar *calendar,
+ GDataCalendarEvent *event,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
G_END_DECLS
#endif /* !GDATA_CALENDAR_SERVICE_H */
diff --git a/gdata/tests/calendar.c b/gdata/tests/calendar.c
index c54ddda0..4994d010 100644
--- a/gdata/tests/calendar.c
+++ b/gdata/tests/calendar.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
* GData Client
- * Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
+ * Copyright (C) Philip Withnall 2009, 2010, 2014 <philip@tecnocode.co.uk>
*
* GData Client is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,9 +23,16 @@
#include "gdata.h"
#include "common.h"
+#include "gdata-dummy-authorizer.h"
static UhmServer *mock_server = NULL;
+#undef CLIENT_ID /* from common.h */
+
+#define CLIENT_ID "352818697630-nqu2cmt5quqd6lr17ouoqmb684u84l1f.apps.googleusercontent.com"
+#define CLIENT_SECRET "-fA4pHQJxR3zJ-FyAMPQsikg"
+#define REDIRECT_URI "urn:ietf:wg:oauth:2.0:oob"
+
typedef struct {
GDataCalendarCalendar *calendar;
} TempCalendarData;
@@ -46,7 +53,7 @@ set_up_temp_calendar (TempCalendarData *data, gconstpointer service)
gdata_calendar_calendar_set_color (calendar, &colour);
data->calendar = GDATA_CALENDAR_CALENDAR (gdata_service_insert_entry (GDATA_SERVICE (service),
gdata_calendar_service_get_primary_authorization_domain (),
- "https://www.google.com/calendar/feeds/default/owncalendars/full",
+ "https://www.googleapis.com/calendar/v3/calendars",
GDATA_ENTRY (calendar), NULL, NULL));
g_assert (GDATA_IS_CALENDAR_CALENDAR (data->calendar));
g_object_unref (calendar);
@@ -70,75 +77,48 @@ tear_down_temp_calendar (TempCalendarData *data, gconstpointer service)
static void
test_authentication (void)
{
- gboolean retval;
- GDataClientLoginAuthorizer *authorizer;
- GError *error = NULL;
+ GDataOAuth2Authorizer *authorizer = NULL; /* owned */
+ gchar *authentication_uri, *authorisation_code;
gdata_test_mock_server_start_trace (mock_server, "authentication");
- /* Create an authorizer */
- authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_CALENDAR_SERVICE);
+ authorizer = gdata_oauth2_authorizer_new (CLIENT_ID, CLIENT_SECRET,
+ REDIRECT_URI,
+ GDATA_TYPE_CALENDAR_SERVICE);
- g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
+ /* Get an authentication URI. */
+ authentication_uri = gdata_oauth2_authorizer_build_authentication_uri (authorizer, NULL, FALSE);
+ g_assert (authentication_uri != NULL);
- /* Log in */
- retval = gdata_client_login_authorizer_authenticate (authorizer, USERNAME, PASSWORD, NULL, &error);
- g_assert_no_error (error);
- g_assert (retval == TRUE);
- g_clear_error (&error);
+ /* Get the authorisation code off the user. */
+ if (uhm_server_get_enable_online (mock_server)) {
+ authorisation_code = gdata_test_query_user_for_verifier (authentication_uri);
+ } else {
+ /* Hard coded, extracted from the trace file. TODO */
+ authorisation_code = g_strdup ("4/OEX-S1iMbOA_dOnNgUlSYmGWh3TK.QrR73axcNMkWoiIBeO6P2m_su7cwkQI");
+ }
- /* Check all is as it should be */
- g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
- g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
+ g_free (authentication_uri);
+
+ if (authorisation_code == NULL) {
+ /* Skip tests. */
+ goto skip_test;
+ }
+
+ /* Authorise the token */
+ g_assert (gdata_oauth2_authorizer_request_authorization (authorizer, authorisation_code, NULL, NULL) == TRUE);
+ /* Check all is as it should be */
g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
gdata_calendar_service_get_primary_authorization_domain ()) == TRUE);
+skip_test:
+ g_free (authorisation_code);
g_object_unref (authorizer);
uhm_server_end_trace (mock_server);
}
-GDATA_ASYNC_TEST_FUNCTIONS (authentication, void,
-G_STMT_START {
- GDataClientLoginAuthorizer *authorizer;
-
- /* Create an authorizer */
- authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_CALENDAR_SERVICE);
-
- g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
-
- gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, cancellable, async_ready_callback, async_data);
-
- g_object_unref (authorizer);
-} G_STMT_END,
-G_STMT_START {
- gboolean retval;
- GDataClientLoginAuthorizer *authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (obj);
-
- retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
-
- if (error == NULL) {
- g_assert (retval == TRUE);
-
- /* Check all is as it should be */
- g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
- g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
-
- g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
- gdata_calendar_service_get_primary_authorization_domain ()) == TRUE);
- } else {
- g_assert (retval == FALSE);
-
- /* Check nothing's changed */
- g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, NULL);
- g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, NULL);
-
- g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
- gdata_calendar_service_get_primary_authorization_domain ()) == FALSE);
- }
-} G_STMT_END);
-
typedef struct {
GDataCalendarCalendar *calendar1;
GDataCalendarCalendar *calendar2;
@@ -158,9 +138,10 @@ set_up_query_calendars (QueryCalendarsData *data, gconstpointer service)
calendar = gdata_calendar_calendar_new (NULL);
gdata_entry_set_title (GDATA_ENTRY (calendar), "Test Calendar 1");
gdata_calendar_calendar_set_color (calendar, &colour);
+ /* TODO: abstract this */
data->calendar1 = GDATA_CALENDAR_CALENDAR (gdata_service_insert_entry (GDATA_SERVICE (service),
gdata_calendar_service_get_primary_authorization_domain (),
- "https://www.google.com/calendar/feeds/default/owncalendars/full",
+ "https://www.googleapis.com/calendar/v3/calendars",
GDATA_ENTRY (calendar), NULL, NULL));
g_assert (GDATA_IS_CALENDAR_CALENDAR (data->calendar1));
g_object_unref (calendar);
@@ -170,7 +151,7 @@ set_up_query_calendars (QueryCalendarsData *data, gconstpointer service)
gdata_calendar_calendar_set_color (calendar, &colour);
data->calendar2 = GDATA_CALENDAR_CALENDAR (gdata_service_insert_entry (GDATA_SERVICE (service),
gdata_calendar_service_get_primary_authorization_domain (),
- "https://www.google.com/calendar/feeds/default/owncalendars/full",
+ "https://www.googleapis.com/calendar/v3/calendars",
GDATA_ENTRY (calendar), NULL, NULL));
g_assert (GDATA_IS_CALENDAR_CALENDAR (data->calendar2));
g_object_unref (calendar);
@@ -184,6 +165,7 @@ tear_down_query_calendars (QueryCalendarsData *data, gconstpointer service)
gdata_test_mock_server_start_trace (mock_server, "teardown-query-calendars");
/* Delete the calendars */
+ /* TODO: Crashes because there’s no self link. Maybe insert one manually? */
g_assert (gdata_service_delete_entry (GDATA_SERVICE (service), gdata_calendar_service_get_primary_authorization_domain (),
GDATA_ENTRY (data->calendar1), NULL, NULL) == TRUE);
g_object_unref (data->calendar1);
@@ -340,6 +322,8 @@ static void
set_up_query_events (QueryEventsData *data, gconstpointer service)
{
GDataCalendarEvent *event;
+ GDataGDWhen *when;
+ GError *error = NULL;
/* Set up a temporary calendar */
set_up_temp_calendar ((TempCalendarData*) data, service);
@@ -349,19 +333,37 @@ set_up_query_events (QueryEventsData *data, gconstpointer service)
/* Add some test events to it */
event = gdata_calendar_event_new (NULL);
gdata_entry_set_title (GDATA_ENTRY (event), "Test Event 1");
- data->event1 = gdata_calendar_service_insert_event (GDATA_CALENDAR_SERVICE (service), event, NULL, NULL);
+
+ when = gdata_gd_when_new (1419113727, 1419113728, FALSE);
+ gdata_calendar_event_add_time (event, when);
+ g_object_unref (when);
+
+ data->event1 = gdata_calendar_service_insert_calendar_event (GDATA_CALENDAR_SERVICE (service), data->parent.calendar, event, NULL, &error);
+ g_assert_no_error (error);
g_assert (GDATA_IS_CALENDAR_EVENT (data->event1));
g_object_unref (event);
event = gdata_calendar_event_new (NULL);
gdata_entry_set_title (GDATA_ENTRY (event), "Test Event 2");
- data->event2 = gdata_calendar_service_insert_event (GDATA_CALENDAR_SERVICE (service), event, NULL, NULL);
+
+ when = gdata_gd_when_new (1419113000, 1419114000, FALSE);
+ gdata_calendar_event_add_time (event, when);
+ g_object_unref (when);
+
+ data->event2 = gdata_calendar_service_insert_calendar_event (GDATA_CALENDAR_SERVICE (service), data->parent.calendar, event, NULL, &error);
+ g_assert_no_error (error);
g_assert (GDATA_IS_CALENDAR_EVENT (data->event2));
g_object_unref (event);
event = gdata_calendar_event_new (NULL);
gdata_entry_set_title (GDATA_ENTRY (event), "Test Event 3");
- data->event3 = gdata_calendar_service_insert_event (GDATA_CALENDAR_SERVICE (service), event, NULL, NULL);
+
+ when = gdata_gd_when_new (1419110000, 1419120000, TRUE);
+ gdata_calendar_event_add_time (event, when);
+ g_object_unref (when);
+
+ data->event3 = gdata_calendar_service_insert_calendar_event (GDATA_CALENDAR_SERVICE (service), data->parent.calendar, event, NULL, &error);
+ g_assert_no_error (error);
g_assert (GDATA_IS_CALENDAR_EVENT (data->event3));
g_object_unref (event);
@@ -519,7 +521,8 @@ test_event_insert (InsertEventData *data, gconstpointer service)
g_object_unref (when);
/* Insert the event */
- new_event = data->new_event = gdata_calendar_service_insert_event (GDATA_CALENDAR_SERVICE (service), event, NULL, &error);
+ new_event = data->new_event = gdata_calendar_service_insert_calendar_event (GDATA_CALENDAR_SERVICE (service),
+ data->parent.calendar, event, NULL, &error);
g_assert_no_error (error);
g_assert (GDATA_IS_CALENDAR_EVENT (new_event));
g_clear_error (&error);
@@ -561,7 +564,9 @@ G_STMT_START {
g_object_unref (when);
/* Insert the event */
- gdata_calendar_service_insert_event_async (GDATA_CALENDAR_SERVICE (service), event, cancellable, async_ready_callback, async_data);
+ gdata_calendar_service_insert_calendar_event_async (GDATA_CALENDAR_SERVICE (service),
+ data->parent.calendar, event, cancellable,
+ async_ready_callback, async_data);
g_object_unref (event);
} G_STMT_END,
@@ -578,7 +583,7 @@ G_STMT_START {
} G_STMT_END);
static void
-test_event_xml (void)
+test_event_json (void)
{
GDataCalendarEvent *event;
GDataGDWhere *where;
@@ -604,174 +609,203 @@ test_event_xml (void)
gdata_calendar_event_add_time (event, when);
g_object_unref (when);
- /* Check the XML */
- gdata_test_assert_xml (event,
- "<?xml version='1.0' encoding='UTF-8'?>"
- "<entry xmlns='http://www.w3.org/2005/Atom' "
- "xmlns:gd='http://schemas.google.com/g/2005' "
- "xmlns:gCal='http://schemas.google.com/gCal/2005' "
- "xmlns:app='http://www.w3.org/2007/app'>"
- "<title type='text'>Tennis with Beth</title>"
- "<content type='text'>Meet for a quick lesson.</content>"
- "<category term='http://schemas.google.com/g/2005#event' scheme='http://schemas.google.com/g/2005#kind'/>"
- "<gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'/>"
- "<gd:transparency value='http://schemas.google.com/g/2005#event.opaque'/>"
- "<gCal:guestsCanModify value='false'/>"
- "<gCal:guestsCanInviteOthers value='false'/>"
- "<gCal:guestsCanSeeGuests value='false'/>"
- "<gCal:anyoneCanAddSelf value='false'/>"
- "<gd:when startTime='2009-04-17T15:00:00Z' endTime='2009-04-17T17:00:00Z'/>"
- "<gd:who email='john.smith@example.com' "
- "rel='http://schemas.google.com/g/2005#event.organizer' "
- "valueString='John Smith\342\200\275'/>"
- "<gd:where valueString='Rolling Lawn Courts'/>"
- "</entry>");
+ /* Check the JSON */
+ gdata_test_assert_json (event, "{"
+ "'summary': 'Tennis with Beth',"
+ "'description': 'Meet for a quick lesson.',"
+ "'kind': 'calendar#event',"
+ "'status': 'http://schemas.google.com/g/2005#event.confirmed',"
+ "'transparency': 'http://schemas.google.com/g/2005#event.opaque',"
+ "'guestsCanModify': false,"
+ "'guestsCanInviteOthers': false,"
+ "'guestsCanSeeOtherGuests': false,"
+ "'anyoneCanAddSelf': false,"
+ "'start': {"
+ "'dateTime': '2009-04-17T15:00:00Z',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'end': {"
+ "'dateTime': '2009-04-17T17:00:00Z',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'attendees': ["
+ "{"
+ "'email': 'john.smith@example.com',"
+ "'displayName': 'John Smith‽',"
+ "'organizer': true"
+ "}"
+ "],"
+ "'organizer': {"
+ "'email': 'john.smith@example.com',"
+ "'displayName': 'John Smith‽'"
+ "},"
+ "'location': 'Rolling Lawn Courts'"
+ "}");
}
static void
-test_event_xml_dates (void)
+test_event_json_dates (void)
{
- GDataCalendarEvent *event;
- GList *i;
- GDataGDWhen *when;
- gint64 _time;
- GError *error = NULL;
-
- event = GDATA_CALENDAR_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_CALENDAR_EVENT,
- "<entry xmlns='http://www.w3.org/2005/Atom' "
- "xmlns:gd='http://schemas.google.com/g/2005' "
- "xmlns:gCal='http://schemas.google.com/gCal/2005' "
- "xmlns:app='http://www.w3.org/2007/app'>"
- "<title type='text'>Tennis with Beth</title>"
- "<content type='text'>Meet for a quick lesson.</content>"
- "<category term='http://schemas.google.com/g/2005#event' scheme='http://schemas.google.com/g/2005#kind'/>"
- "<gd:when startTime='2009-04-17'/>"
- "<gd:when startTime='2009-04-17T15:00:00Z'/>"
- "<gd:when startTime='2009-04-27' endTime='20090506'/>"
- "</entry>", -1, &error));
- g_assert_no_error (error);
- g_assert (GDATA_IS_ENTRY (event));
- g_clear_error (&error);
-
- /* Check the times */
- i = gdata_calendar_event_get_times (event);
-
- /* First time */
- when = GDATA_GD_WHEN (i->data);
- g_assert (i->next != NULL);
- g_assert (gdata_gd_when_is_date (when) == TRUE);
- _time = gdata_gd_when_get_start_time (when);
- g_assert_cmpint (_time, ==, 1239926400);
- _time = gdata_gd_when_get_end_time (when);
- g_assert_cmpint (_time, ==, -1);
- g_assert (gdata_gd_when_get_value_string (when) == NULL);
- g_assert (gdata_gd_when_get_reminders (when) == NULL);
-
- /* Second time */
- i = i->next;
- when = GDATA_GD_WHEN (i->data);
- g_assert (i->next != NULL);
- g_assert (gdata_gd_when_is_date (when) == FALSE);
- _time = gdata_gd_when_get_start_time (when);
- g_assert_cmpint (_time, ==, 1239926400 + 54000);
- _time = gdata_gd_when_get_end_time (when);
- g_assert_cmpint (_time, ==, -1);
- g_assert (gdata_gd_when_get_value_string (when) == NULL);
- g_assert (gdata_gd_when_get_reminders (when) == NULL);
-
- /* Third time */
- i = i->next;
- when = GDATA_GD_WHEN (i->data);
- g_assert (i->next == NULL);
- g_assert (gdata_gd_when_is_date (when) == TRUE);
- _time = gdata_gd_when_get_start_time (when);
- g_assert_cmpint (_time, ==, 1239926400 + 864000);
- _time = gdata_gd_when_get_end_time (when);
- g_assert_cmpint (_time, ==, 1241568000);
- g_assert (gdata_gd_when_get_value_string (when) == NULL);
- g_assert (gdata_gd_when_get_reminders (when) == NULL);
-
- /* Check the XML */
- gdata_test_assert_xml (event,
- "<?xml version='1.0' encoding='UTF-8'?>"
- "<entry xmlns='http://www.w3.org/2005/Atom' "
- "xmlns:gd='http://schemas.google.com/g/2005' "
- "xmlns:gCal='http://schemas.google.com/gCal/2005' "
- "xmlns:app='http://www.w3.org/2007/app'>"
- "<title type='text'>Tennis with Beth</title>"
- "<content type='text'>Meet for a quick lesson.</content>"
- "<category term='http://schemas.google.com/g/2005#event' scheme='http://schemas.google.com/g/2005#kind'/>"
- "<gCal:guestsCanModify value='false'/>"
- "<gCal:guestsCanInviteOthers value='false'/>"
- "<gCal:guestsCanSeeGuests value='false'/>"
- "<gCal:anyoneCanAddSelf value='false'/>"
- "<gd:when startTime='2009-04-17'/>"
- "<gd:when startTime='2009-04-17T15:00:00Z'/>"
- "<gd:when startTime='2009-04-27' endTime='2009-05-06'/>"
- "</entry>");
-
- g_object_unref (event);
+ guint i;
+
+ const struct {
+ const gchar *json;
+ gboolean is_date;
+ gint64 start_time;
+ gint64 end_time;
+ const gchar *output_json; /* NULL if equal to @json */
+ } test_vectors[] = {
+ /* Plain date, single day. */
+ { "'start': {"
+ "'date': '2009-04-17',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'end': {"
+ "'date': '2009-04-18',"
+ "'timeZone': 'UTC'"
+ "}", TRUE, 1239926400, 1239926400 + 86400, NULL },
+ /* Full date and time. */
+ { "'start': {"
+ "'dateTime': '2009-04-17T15:00:00Z',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'end': {"
+ "'dateTime': '2009-04-17T16:00:00Z',"
+ "'timeZone': 'UTC'"
+ "}", FALSE, 1239926400 + 54000, 1239926400 + 54000 + 3600, NULL },
+ /* Start and end time. */
+ { "'start': {"
+ "'date': '2009-04-27',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'end': {"
+ "'date': '20090506',"
+ "'timeZone': 'UTC'"
+ "}", TRUE, 1239926400 + 864000, 1241568000, "'start': {"
+ "'date': '2009-04-27',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'end': {"
+ "'date': '2009-05-06',"
+ "'timeZone': 'UTC'"
+ "}" },
+ };
+
+ for (i = 0; i < G_N_ELEMENTS (test_vectors); i++) {
+ gchar *json = NULL, *output_json = NULL; /* owned */
+ GDataCalendarEvent *event;
+ GList *j;
+ GDataGDWhen *when;
+ gint64 _time;
+ GError *error = NULL;
+
+ json = g_strdup_printf ("{"
+ "'summary': 'Tennis with Beth',"
+ "'description': 'Meet for a quick lesson.',"
+ "'kind': 'calendar#event',"
+ "%s"
+ "}", test_vectors[i].json);
+ output_json = g_strdup_printf ("{"
+ "'summary': 'Tennis with Beth',"
+ "'description': 'Meet for a quick lesson.',"
+ "'kind': 'calendar#event',"
+ "'guestsCanModify': false,"
+ "'guestsCanInviteOthers': false,"
+ "'guestsCanSeeOtherGuests': false,"
+ "'anyoneCanAddSelf': false,"
+ "'attendees': [],"
+ "%s"
+ "}", (test_vectors[i].output_json != NULL) ? test_vectors[i].output_json : test_vectors[i].json);
+
+ event = GDATA_CALENDAR_EVENT (gdata_parsable_new_from_json (GDATA_TYPE_CALENDAR_EVENT, json, -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_ENTRY (event));
+ g_clear_error (&error);
+
+ /* Check the times */
+ j = gdata_calendar_event_get_times (event);
+ g_assert (j != NULL);
+
+ when = GDATA_GD_WHEN (j->data);
+ g_assert (gdata_gd_when_is_date (when) == test_vectors[i].is_date);
+ _time = gdata_gd_when_get_start_time (when);
+ g_assert_cmpint (_time, ==, test_vectors[i].start_time);
+ _time = gdata_gd_when_get_end_time (when);
+ g_assert_cmpint (_time, ==, test_vectors[i].end_time);
+ g_assert (gdata_gd_when_get_value_string (when) == NULL);
+ g_assert (gdata_gd_when_get_reminders (when) == NULL);
+
+ /* Should be no other times. */
+ g_assert (j->next == NULL);
+
+ /* Check the JSON */
+ gdata_test_assert_json (event, output_json);
+
+ g_object_unref (event);
+ g_free (output_json);
+ g_free (json);
+ }
}
static void
-test_event_xml_recurrence (void)
+test_event_json_recurrence (void)
{
GDataCalendarEvent *event;
GError *error = NULL;
gchar *id, *uri;
- event = GDATA_CALENDAR_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_CALENDAR_EVENT,
- "<entry xmlns='http://www.w3.org/2005/Atom' "
- "xmlns:gd='http://schemas.google.com/g/2005' "
- "xmlns:gCal='http://schemas.google.com/gCal/2005' "
- "xmlns:app='http://www.w3.org/2007/app'>"
- "<id>http://www.google.com/calendar/feeds/libgdata.test@googlemail.com/events/g5928e82rrch95b25f8ud0dlsg_20090429T153000Z</id>"
- "<published>2009-04-25T15:22:47.000Z</published>"
- "<updated>2009-04-27T17:54:10.000Z</updated>"
- "<app:edited xmlns:app='http://www.w3.org/2007/app'>2009-04-27T17:54:10.000Z</app:edited>"
- "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'/>"
- "<title>Test daily instance event</title>"
- "<content></content>"
- "<link rel='http://www.iana.org/assignments/relation/alternate' type='text/html' "
- "href='http://www.google.com/calendar/event?"
- "eid=ZzU5MjhlODJycmNoOTViMjVmOHVkMGRsc2dfMjAwOTA0MjlUMTUzMDAwWiBsaWJnZGF0YS50ZXN0QGdvb2dsZW1haWwuY29t' "
- "title='alternate'/>"
- "<link rel='http://www.iana.org/assignments/relation/self' type='application/atom+xml' "
- "href='http://www.google.com/calendar/feeds/libgdata.test@googlemail.com/private/full/"
- "g5928e82rrch95b25f8ud0dlsg_20090429T153000Z'/>"
- "<link rel='http://www.iana.org/assignments/relation/edit' type='application/atom+xml' "
- "href='http://www.google.com/calendar/feeds/libgdata.test@googlemail.com/private/full/"
- "g5928e82rrch95b25f8ud0dlsg_20090429T153000Z'/>"
- "<author>"
- "<name>GData Test</name>"
- "<email>libgdata.test@googlemail.com</email>"
- "</author>"
- "<gd:originalEvent id='g5928e82rrch95b25f8ud0dlsg' "
- "href='http://www.google.com/calendar/feeds/libgdata.test@googlemail.com/private/full/"
- "g5928e82rrch95b25f8ud0dlsg'>"
- "<gd:when startTime='2009-04-29T16:30:00.000+01:00'/>"
- "</gd:originalEvent>"
- "<gCal:guestsCanModify value='false'/>"
- "<gCal:guestsCanInviteOthers value='false'/>"
- "<gCal:guestsCanSeeGuests value='false'/>"
- "<gCal:anyoneCanAddSelf value='false'/>"
- "<gd:comments>"
- "<gd:feedLink href='http://www.google.com/calendar/feeds/libgdata.test@googlemail.com/private/full/"
- "g5928e82rrch95b25f8ud0dlsg_20090429T153000Z/comments'/>"
- "</gd:comments>"
- "<gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'/>"
- "<gd:visibility value='http://schemas.google.com/g/2005#event.private'/>"
- "<gd:transparency value='http://schemas.google.com/g/2005#event.opaque'/>"
- "<gCal:uid value='g5928e82rrch95b25f8ud0dlsg@google.com'/>"
- "<gCal:sequence value='0'/>"
- "<gd:when startTime='2009-04-29T17:30:00.000+01:00' endTime='2009-04-29T17:30:00.000+01:00'>"
- "<gd:reminder minutes='10' method='email'/>"
- "<gd:reminder minutes='10' method='alert'/>"
- "</gd:when>"
- "<gd:who rel='http://schemas.google.com/g/2005#event.organizer' valueString='GData Test' "
- "email='libgdata.test@googlemail.com'/>"
- "<gd:where valueString=''/>"
- "</entry>", -1, &error));
+ event = GDATA_CALENDAR_EVENT (gdata_parsable_new_from_json (GDATA_TYPE_CALENDAR_EVENT, "{"
+ "'id': 'https://www.googleapis.com/calendar/v3/calendars/libgdata.test@googlemail.com/events/g5928e82rrch95b25f8ud0dlsg_20090429T153000Z',"
+ "'updated': '2009-04-27T17:54:10.000Z',"
+ "'summary': 'Test daily instance event',"
+ "'kind': 'calendar#event',"
+ "'creator': {"
+ "'displayName': 'GData Test',"
+ "'email': 'libgdata.test@googlemail.com'"
+ "},"
+ "'recurringEventId': 'g5928e82rrch95b25f8ud0dlsg',"
+ "'originalStartTime': {"
+ "'dateTime': '2009-04-29T16:30:00.000+01:00',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'guestsCanModify': false,"
+ "'guestsCanInviteOthers': false,"
+ "'guestsCanSeeOtherGuests': false,"
+ "'anyoneCanAddSelf': false,"
+ "'status': 'http://schemas.google.com/g/2005#event.confirmed',"
+ "'visibility': 'http://schemas.google.com/g/2005#event.private',"
+ "'transparency': 'http://schemas.google.com/g/2005#event.opaque',"
+ "'iCalUID': 'g5928e82rrch95b25f8ud0dlsg@google.com',"
+ "'sequence': '0',"
+ "'start': {"
+ "'dateTime': '2009-04-29T17:30:00.000+01:00',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'end': {"
+ "'dateTime': '2009-04-29T17:30:00.000+01:00',"
+ "'timeZone': 'UTC'"
+ "},"
+ "'reminders': {"
+ "'overrides': [{"
+ "'method': 'email',"
+ "'minutes': 10"
+ "}, {"
+ "'method': 'popup',"
+ "'minutes': 10"
+ "}]"
+ "},"
+ "'attendees': ["
+ "{"
+ "'email': 'libgdata.test@googlemail.com',"
+ "'displayName': 'GData Test',"
+ "'organizer': true"
+ "}"
+ "],"
+ "'organizer': {"
+ "'email': 'libgdata.test@googlemail.com',"
+ "'displayName': 'GData Test'"
+ "}"
+ "}", -1, &error));
g_assert_no_error (error);
g_assert (GDATA_IS_ENTRY (event));
g_clear_error (&error);
@@ -781,7 +815,9 @@ test_event_xml_recurrence (void)
gdata_calendar_event_get_original_event_details (event, &id, &uri);
g_assert_cmpstr (id, ==, "g5928e82rrch95b25f8ud0dlsg");
- g_assert_cmpstr (uri, ==, "http://www.google.com/calendar/feeds/libgdata.test@googlemail.com/private/full/g5928e82rrch95b25f8ud0dlsg");
+ /* TODO
+ g_assert_cmpstr (uri, ==, "https://www.googleapis.com/calendar/v3/calendars/libgdata.test@googlemail.com/events/g5928e82rrch95b25f8ud0dlsg");
+ */
g_free (id);
g_free (uri);
@@ -796,8 +832,8 @@ test_calendar_escaping (void)
calendar = gdata_calendar_calendar_new (NULL);
gdata_calendar_calendar_set_timezone (calendar, "<timezone>");
- /* Check the outputted XML is escaped properly */
- gdata_test_assert_xml (calendar,
+ /* Check the outputted JSON is escaped properly */
+ gdata_test_assert_json (calendar,
"<?xml version='1.0' encoding='UTF-8'?>"
"<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005' "
"xmlns:gCal='http://schemas.google.com/gCal/2005' xmlns:app='http://www.w3.org/2007/app'>"
@@ -817,29 +853,102 @@ test_event_escaping (void)
GDataCalendarEvent *event;
event = gdata_calendar_event_new (NULL);
- gdata_calendar_event_set_status (event, "<status>");
- gdata_calendar_event_set_visibility (event, "<visibility>");
- gdata_calendar_event_set_transparency (event, "<transparency>");
- gdata_calendar_event_set_uid (event, "<uid>");
- gdata_calendar_event_set_recurrence (event, "<recurrence>");
-
- /* Check the outputted XML is escaped properly */
- gdata_test_assert_xml (event,
- "<?xml version='1.0' encoding='UTF-8'?>"
- "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005' "
- "xmlns:gCal='http://schemas.google.com/gCal/2005' xmlns:app='http://www.w3.org/2007/app'>"
- "<title type='text'></title>"
- "<category term='http://schemas.google.com/g/2005#event' scheme='http://schemas.google.com/g/2005#kind'/>"
- "<gd:eventStatus value='&lt;status&gt;'/>"
- "<gd:visibility value='&lt;visibility&gt;'/>"
- "<gd:transparency value='&lt;transparency&gt;'/>"
- "<gCal:uid value='&lt;uid&gt;'/>"
- "<gCal:guestsCanModify value='false'/>"
- "<gCal:guestsCanInviteOthers value='false'/>"
- "<gCal:guestsCanSeeGuests value='false'/>"
- "<gCal:anyoneCanAddSelf value='false'/>"
- "<gd:recurrence>&lt;recurrence&gt;</gd:recurrence>"
- "</entry>");
+ gdata_calendar_event_set_status (event, "\"status\"");
+ gdata_calendar_event_set_visibility (event, "\"visibility\"");
+ gdata_calendar_event_set_transparency (event, "\"transparency\"");
+ gdata_calendar_event_set_uid (event, "\"uid\"");
+ gdata_calendar_event_set_recurrence (event, "\"recurrence\"");
+
+ /* Check the outputted JSON is escaped properly */
+ gdata_test_assert_json (event, "{"
+ "'kind': 'calendar#event',"
+ "'status': '\"status\"',"
+ "'transparency': '\"transparency\"',"
+ "'visibility': '\"visibility\"',"
+ "'iCalUID': '\"uid\"',"
+ "'recurrence': [ '\"recurrence\"' ],"
+ "'guestsCanModify': false,"
+ "'guestsCanInviteOthers': false,"
+ "'guestsCanSeeOtherGuests': false,"
+ "'anyoneCanAddSelf': false,"
+ "'attendees': []"
+ "}");
+ g_object_unref (event);
+}
+
+/* Test the event parser with the minimal number of properties specified. */
+static void
+test_calendar_event_parser_minimal (void)
+{
+ GDataCalendarEvent *event = NULL; /* owned */
+ GDataEntry *entry; /* unowned */
+ GDataLink *self_link; /* unowned */
+ GError *error = NULL;
+
+ event = GDATA_CALENDAR_EVENT (gdata_parsable_new_from_json (GDATA_TYPE_CALENDAR_EVENT,
+ "{"
+ "\"kind\": \"calendar#event\","
+ "\"etag\": \"\\\"2838230136828000\\\"\","
+ "\"id\": \"hsfgtc50u68vdai81t6634u7lg\","
+ "\"status\": \"confirmed\","
+ "\"htmlLink\": \"https://www.google.com/calendar/event?eid=aHNmZ3RjNTB1Njh2ZGFpODF0NjYzNHU3bGcgODk5MWkzNjM0YzRzN3Nwa3NrcjNjZjVuanNAZw\","
+ "\"created\": \"2014-12-20T22:37:48.000Z\","
+ "\"updated\": \"2014-12-20T22:37:48.414Z\","
+ "\"summary\": \"Test Event 1\","
+ "\"creator\": {"
+ "\"email\": \"libgdata.test@googlemail.com\","
+ "\"displayName\": \"GData Test\""
+ "},"
+ "\"organizer\": {"
+ "\"email\": \"8991i3634c4s7spkskr3cf5njs@group.calendar.google.com\","
+ "\"displayName\": \"Temp Test Calendar\","
+ "\"self\": true"
+ "},"
+ "\"start\": {"
+ "\"dateTime\": \"2014-12-20T22:15:27Z\","
+ "\"timeZone\": \"UTC\""
+ "},"
+ "\"end\": {"
+ "\"dateTime\": \"2014-12-20T22:15:28Z\","
+ "\"timeZone\": \"UTC\""
+ "},"
+ "\"iCalUID\": \"hsfgtc50u68vdai81t6634u7lg@google.com\","
+ "\"sequence\": 0,"
+ "\"guestsCanInviteOthers\": false,"
+ "\"guestsCanSeeOtherGuests\": false,"
+ "\"reminders\": {"
+ "\"useDefault\": true"
+ "}"
+ "}", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_CALENDAR_EVENT (event));
+ gdata_test_compare_kind (GDATA_ENTRY (event), "calendar#event", NULL);
+
+ entry = GDATA_ENTRY (event);
+
+ /* Check the event’s properties. */
+ g_assert_cmpstr (gdata_entry_get_id (entry), ==,
+ "hsfgtc50u68vdai81t6634u7lg");
+ g_assert_cmpstr (gdata_entry_get_etag (entry), ==,
+ "\"2838230136828000\"");
+ g_assert_cmpstr (gdata_entry_get_title (entry), ==,
+ "Test Event 1");
+ g_assert_cmpint (gdata_entry_get_updated (entry), ==, 1419115068);
+
+ /* TODO: check everything else */
+
+ self_link = gdata_entry_look_up_link (entry, GDATA_LINK_SELF);
+ g_assert (GDATA_IS_LINK (self_link));
+ g_assert_cmpstr (gdata_link_get_uri (self_link), ==,
+ "https://www.googleapis.com/calendar/v3/calendars/"
+ "hsfgtc50u68vdai81t6634u7lg");
+ g_assert_cmpstr (gdata_link_get_relation_type (self_link), ==,
+ GDATA_LINK_SELF);
+ g_assert_cmpstr (gdata_link_get_content_type (self_link), ==, NULL);
+ g_assert_cmpstr (gdata_link_get_language (self_link), ==, NULL);
+ g_assert_cmpstr (gdata_link_get_title (self_link), ==, NULL);
+ g_assert_cmpint (gdata_link_get_length (self_link), ==, -1);
+
g_object_unref (event);
}
@@ -861,7 +970,7 @@ test_access_rule_properties (void)
}
static void
-test_access_rule_xml (void)
+test_access_rule_json (void)
{
GDataAccessRule *rule;
@@ -870,8 +979,8 @@ test_access_rule_xml (void)
gdata_access_rule_set_role (rule, GDATA_CALENDAR_ACCESS_ROLE_EDITOR);
gdata_access_rule_set_scope (rule, GDATA_ACCESS_SCOPE_USER, "darcy@gmail.com");
- /* Check the XML */
- gdata_test_assert_xml (rule,
+ /* Check the JSON */
+ gdata_test_assert_json (rule,
"<?xml version='1.0' encoding='UTF-8'?>"
"<entry xmlns='http://www.w3.org/2005/Atom' "
"xmlns:gd='http://schemas.google.com/g/2005' "
@@ -1442,7 +1551,66 @@ mock_server_notify_resolver_cb (GObject *object, GParamSpec *pspec, gpointer use
const gchar *ip_address = uhm_server_get_address (server);
uhm_resolver_add_A (resolver, "www.google.com", ip_address);
+ uhm_resolver_add_A (resolver, "www.googleapis.com", ip_address);
+ uhm_resolver_add_A (resolver,
+ "accounts.google.com", ip_address);
+ }
+}
+
+/* Set up a global GDataAuthorizer to be used for all the tests. Unfortunately,
+ * the Google Calendar API is limited to OAuth1 and OAuth2 authorisation, so
+ * this requires user interaction when online.
+ *
+ * If not online, use a dummy authoriser. */
+static GDataAuthorizer *
+create_global_authorizer (void)
+{
+ GDataOAuth2Authorizer *authorizer = NULL; /* owned */
+ gchar *authentication_uri, *authorisation_code;
+ GError *error = NULL;
+
+ /* If not online, just return a dummy authoriser. */
+ if (!uhm_server_get_enable_online (mock_server)) {
+ return GDATA_AUTHORIZER (gdata_dummy_authorizer_new (GDATA_TYPE_CALENDAR_SERVICE));
+ }
+
+ /* Otherwise, go through the interactive OAuth dance. */
+ gdata_test_mock_server_start_trace (mock_server, "global-authentication");
+ authorizer = gdata_oauth2_authorizer_new (CLIENT_ID, CLIENT_SECRET,
+ REDIRECT_URI,
+ GDATA_TYPE_CALENDAR_SERVICE);
+
+ /* Get an authentication URI */
+ authentication_uri = gdata_oauth2_authorizer_build_authentication_uri (authorizer, NULL, FALSE);
+ g_assert (authentication_uri != NULL);
+
+ /* Get the authorisation code off the user. */
+ if (uhm_server_get_enable_online (mock_server)) {
+ authorisation_code = gdata_test_query_user_for_verifier (authentication_uri);
+ } else {
+ /* Hard coded, extracted from the trace file. TODO */
+ authorisation_code = g_strdup ("4/hmXZtrXmXMqK1hwiWPZs9F_N6DK-.Ap4OICAUIe0WoiIBeO6P2m8IDoMxkQI");
+ }
+
+ g_free (authentication_uri);
+
+ if (authorisation_code == NULL) {
+ /* Skip tests. */
+ g_object_unref (authorizer);
+ authorizer = NULL;
+ goto skip_test;
}
+
+ /* Authorise the token */
+ g_assert (gdata_oauth2_authorizer_request_authorization (authorizer, authorisation_code, NULL, &error));
+ g_assert_no_error (error);
+
+skip_test:
+ g_free (authorisation_code);
+
+ uhm_server_end_trace (mock_server);
+
+ return GDATA_AUTHORIZER (authorizer);
}
int
@@ -1460,19 +1628,12 @@ main (int argc, char *argv[])
trace_directory = g_file_new_for_path (TEST_FILE_DIR "traces/calendar");
uhm_server_set_trace_directory (mock_server, trace_directory);
g_object_unref (trace_directory);
-
- gdata_test_mock_server_start_trace (mock_server, "global-authentication");
- authorizer = GDATA_AUTHORIZER (gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_CALENDAR_SERVICE));
- gdata_client_login_authorizer_authenticate (GDATA_CLIENT_LOGIN_AUTHORIZER (authorizer), USERNAME, PASSWORD, NULL, NULL);
- uhm_server_end_trace (mock_server);
+#if 0
+ authorizer = create_global_authorizer ();
service = GDATA_SERVICE (gdata_calendar_service_new (authorizer));
g_test_add_func ("/calendar/authentication", test_authentication);
- g_test_add ("/calendar/authentication/async", GDataAsyncTestData, NULL, gdata_set_up_async_test_data, test_authentication_async,
- gdata_tear_down_async_test_data);
- g_test_add ("/calendar/authentication/async/cancellation", GDataAsyncTestData, NULL, gdata_set_up_async_test_data,
- test_authentication_async_cancellation, gdata_tear_down_async_test_data);
g_test_add ("/calendar/query/all_calendars", QueryCalendarsData, service, set_up_query_calendars, test_query_all_calendars,
tear_down_query_calendars);
@@ -1514,21 +1675,26 @@ main (int argc, char *argv[])
tear_down_temp_calendar_acls);
g_test_add ("/calendar/access-rule/delete", TempCalendarAclsData, service, set_up_temp_calendar_acls, test_access_rule_delete,
tear_down_temp_calendar_acls);
-
+#endif
+#if 0
+TODO
g_test_add_data_func ("/calendar/batch", service, test_batch);
g_test_add ("/calendar/batch/async", BatchAsyncData, service, setup_batch_async, test_batch_async, teardown_batch_async);
g_test_add ("/calendar/batch/async/cancellation", BatchAsyncData, service, setup_batch_async, test_batch_async_cancellation,
teardown_batch_async);
+#endif
- g_test_add_func ("/calendar/event/xml", test_event_xml);
- g_test_add_func ("/calendar/event/xml/dates", test_event_xml_dates);
- g_test_add_func ("/calendar/event/xml/recurrence", test_event_xml_recurrence);
+ g_test_add_func ("/calendar/event/json", test_event_json);
+ g_test_add_func ("/calendar/event/json/dates", test_event_json_dates);
+ g_test_add_func ("/calendar/event/json/recurrence", test_event_json_recurrence);
g_test_add_func ("/calendar/event/escaping", test_event_escaping);
+ g_test_add_func ("/calendar/event/parser/minimal",
+ test_calendar_event_parser_minimal);
g_test_add_func ("/calendar/calendar/escaping", test_calendar_escaping);
g_test_add_func ("/calendar/access-rule/properties", test_access_rule_properties);
- g_test_add_func ("/calendar/access-rule/xml", test_access_rule_xml);
+ g_test_add_func ("/calendar/access-rule/json", test_access_rule_json);
g_test_add_func ("/calendar/query/uri", test_query_uri);
g_test_add_func ("/calendar/query/etag", test_query_etag);