aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Lal <james@lightsofapollo.com>2012-09-30 14:24:23 -0700
committerJames Lal <james@lightsofapollo.com>2012-09-30 14:24:23 -0700
commitb73354819340234dbf20d47c1777e4b486e1195c (patch)
tree3170a45ff1a84621ec1ab44bb1647cb9d7701a92
parentadcd11c2570d2e11eb201ce7d216b5067521afe0 (diff)
downloadjsCalDAV-b73354819340234dbf20d47c1777e4b486e1195c.tar.gz
added Caldav.Request.Asset for adding/deleting/updating single assets
-rw-r--r--Makefile1
-rw-r--r--caldav.js146
-rw-r--r--lib/caldav/request/asset.js141
-rw-r--r--lib/caldav/request/index.js3
-rw-r--r--lib/caldav/sax/dav_response.js2
-rw-r--r--test/caldav/request/asset_test.js108
6 files changed, 397 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 51d8eb2..2815f92 100644
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,7 @@ package: test-agent-config
cat $(LIB_ROOT)/sax/calendar_data_handler.js >> $(WEB_FILE)
cat $(LIB_ROOT)/sax/dav_response.js >> $(WEB_FILE)
cat $(LIB_ROOT)/request/abstract.js >> $(WEB_FILE)
+ cat $(LIB_ROOT)/request/asset.js >> $(WEB_FILE)
cat $(LIB_ROOT)/request/propfind.js >> $(WEB_FILE)
cat $(LIB_ROOT)/request/calendar_query.js >> $(WEB_FILE)
cat $(LIB_ROOT)/request/calendar_home.js >> $(WEB_FILE)
diff --git a/caldav.js b/caldav.js
index e075a8b..da63bda 100644
--- a/caldav.js
+++ b/caldav.js
@@ -2267,7 +2267,7 @@ function write (chunk) {
var tag = data[handler.tagField];
var last = this.tagStack[this.tagStack.length - 1];
- if (last.handler === handler) {
+ if (last.handler && last.handler === handler) {
this.stack.push(this.current);
this.current = this.current[tag] = [];
} else {
@@ -2473,6 +2473,147 @@ function write (chunk) {
));
(function(module, ns) {
+ var XHR = ns.require('xhr');
+
+ /**
+ * Creates an Http request for a single webdav resource.
+ * Thin wrapper over http/xhr each public method has the same
+ * signature with similar options:
+ *
+ * // the intent is that after a larger calendar query
+ * // the urls are stored and can be used to modify the
+ * // calendar resources.
+ * var asset = new Caldav.Request.Asset(con, 'someurl');
+ *
+ * asset.get({ etag: 'foo'}, function(err, data) {
+ * });
+ *
+ * asset.put({ etag: 'foo' }, body, function(err, data) {
+ *
+ * });
+ *
+ * asset.delete(function() {
+ *
+ * });
+ *
+ * @param {Caldav.Connection} connection connection details.
+ * @param {String} url assert url.
+ */
+ function Asset(connection, url) {
+ if (!connection) {
+ throw new Error('must pass connection object');
+ }
+ this.connection = connection;
+ this.url = url;
+ }
+
+ Asset.prototype = {
+
+ contentType: 'text/calendar',
+
+ _buildRequest: function(method, options) {
+ var headers = {
+ 'Content-Type': this.contentType
+ };
+
+ if (options && options.contentType) {
+ headers['Content-Type'] = options.contentType;
+ }
+
+ if (options && options.etag) {
+ headers['If-None-Match'] = options.etag;
+ }
+
+ return this.connection.request({
+ url: this.url,
+ headers: headers,
+ method: method
+ });
+ },
+
+ /**
+ * Find a single calendar asset.
+ * This method should only be used to either
+ * confirm a put or delete request.
+ *
+ * Calendar query is far more suited for fetching
+ * large amounts of calendar data.
+ *
+ * Options:
+ * - etag: used to issue a 'If-Not-Match'
+ *
+ * @param {Object} [options] calendar options.
+ * @param {Function} callback node style [err, data, xhr].
+ */
+ get: function(options, callback) {
+ if (typeof(options) === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ var req = this._buildRequest('GET', options);
+
+ req.send(function(err, xhr) {
+ callback(err, xhr.responseText, xhr);
+ });
+ },
+
+ /**
+ * Adds or modifies a single calendar resource.
+ *
+ * @param {Object} [options] see get.
+ * @param {String} data post content.
+ * @param {Function} callback node style [err, data, xhr].
+ */
+ put: function(options, data, callback) {
+ if (typeof(options) === 'string') {
+ data = options;
+ options = null;
+ }
+
+ if (typeof(data) === 'function') {
+ callback = data;
+ data = null;
+ }
+
+ var req = this._buildRequest('PUT', options);
+ req.data = data;
+
+ req.send(function(err, xhr) {
+ callback(err, xhr.responseText, xhr);
+ });
+ },
+
+ /**
+ * Deletes a calendar resource
+ *
+ * @param {Object} [options] see get.
+ * @param {Function} callback node style [err, data, xhr].
+ */
+ delete: function(options, callback) {
+ if (typeof(options) === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ var req = this._buildRequest('DELETE', options);
+
+ req.send(function(err, xhr) {
+ callback(err, xhr.responseText, xhr);
+ });
+ }
+ };
+
+ module.exports = Asset;
+
+}.apply(
+ this,
+ (this.Caldav) ?
+ [Caldav('request/asset'), Caldav] :
+ [module, require('../caldav')]
+));
+(function(module, ns) {
+
var Abstract = ns.require('request/abstract'),
Template = ns.require('template'),
DavResponse = ns.require('sax/dav_response');
@@ -2847,7 +2988,8 @@ function write (chunk) {
CalendarQuery: ns.require('request/calendar_query'),
Propfind: ns.require('request/propfind'),
CalendarHome: ns.require('request/calendar_home'),
- Resources: ns.require('request/resources')
+ Resources: ns.require('request/resources'),
+ Asset: ns.require('request/asset')
};
}.apply(
diff --git a/lib/caldav/request/asset.js b/lib/caldav/request/asset.js
new file mode 100644
index 0000000..b26e4f7
--- /dev/null
+++ b/lib/caldav/request/asset.js
@@ -0,0 +1,141 @@
+(function(module, ns) {
+
+ var XHR = ns.require('xhr');
+
+ /**
+ * Creates an Http request for a single webdav resource.
+ * Thin wrapper over http/xhr each public method has the same
+ * signature with similar options:
+ *
+ * // the intent is that after a larger calendar query
+ * // the urls are stored and can be used to modify the
+ * // calendar resources.
+ * var asset = new Caldav.Request.Asset(con, 'someurl');
+ *
+ * asset.get({ etag: 'foo'}, function(err, data) {
+ * });
+ *
+ * asset.put({ etag: 'foo' }, body, function(err, data) {
+ *
+ * });
+ *
+ * asset.delete(function() {
+ *
+ * });
+ *
+ * @param {Caldav.Connection} connection connection details.
+ * @param {String} url assert url.
+ */
+ function Asset(connection, url) {
+ if (!connection) {
+ throw new Error('must pass connection object');
+ }
+ this.connection = connection;
+ this.url = url;
+ }
+
+ Asset.prototype = {
+
+ contentType: 'text/calendar',
+
+ _buildRequest: function(method, options) {
+ var headers = {
+ 'Content-Type': this.contentType
+ };
+
+ if (options && options.contentType) {
+ headers['Content-Type'] = options.contentType;
+ }
+
+ if (options && options.etag) {
+ headers['If-None-Match'] = options.etag;
+ }
+
+ return this.connection.request({
+ url: this.url,
+ headers: headers,
+ method: method
+ });
+ },
+
+ /**
+ * Find a single calendar asset.
+ * This method should only be used to either
+ * confirm a put or delete request.
+ *
+ * Calendar query is far more suited for fetching
+ * large amounts of calendar data.
+ *
+ * Options:
+ * - etag: used to issue a 'If-Not-Match'
+ *
+ * @param {Object} [options] calendar options.
+ * @param {Function} callback node style [err, data, xhr].
+ */
+ get: function(options, callback) {
+ if (typeof(options) === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ var req = this._buildRequest('GET', options);
+
+ req.send(function(err, xhr) {
+ callback(err, xhr.responseText, xhr);
+ });
+ },
+
+ /**
+ * Adds or modifies a single calendar resource.
+ *
+ * @param {Object} [options] see get.
+ * @param {String} data post content.
+ * @param {Function} callback node style [err, data, xhr].
+ */
+ put: function(options, data, callback) {
+ if (typeof(options) === 'string') {
+ data = options;
+ options = null;
+ }
+
+ if (typeof(data) === 'function') {
+ callback = data;
+ data = null;
+ }
+
+ var req = this._buildRequest('PUT', options);
+ req.data = data;
+
+ req.send(function(err, xhr) {
+ callback(err, xhr.responseText, xhr);
+ });
+ },
+
+ /**
+ * Deletes a calendar resource
+ *
+ * @param {Object} [options] see get.
+ * @param {Function} callback node style [err, data, xhr].
+ */
+ delete: function(options, callback) {
+ if (typeof(options) === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ var req = this._buildRequest('DELETE', options);
+
+ req.send(function(err, xhr) {
+ callback(err, xhr.responseText, xhr);
+ });
+ }
+ };
+
+ module.exports = Asset;
+
+}.apply(
+ this,
+ (this.Caldav) ?
+ [Caldav('request/asset'), Caldav] :
+ [module, require('../caldav')]
+));
diff --git a/lib/caldav/request/index.js b/lib/caldav/request/index.js
index 6f8c23d..aad595e 100644
--- a/lib/caldav/request/index.js
+++ b/lib/caldav/request/index.js
@@ -5,7 +5,8 @@
CalendarQuery: ns.require('request/calendar_query'),
Propfind: ns.require('request/propfind'),
CalendarHome: ns.require('request/calendar_home'),
- Resources: ns.require('request/resources')
+ Resources: ns.require('request/resources'),
+ Asset: ns.require('request/asset')
};
}.apply(
diff --git a/lib/caldav/sax/dav_response.js b/lib/caldav/sax/dav_response.js
index e257027..6953161 100644
--- a/lib/caldav/sax/dav_response.js
+++ b/lib/caldav/sax/dav_response.js
@@ -108,7 +108,7 @@
var tag = data[handler.tagField];
var last = this.tagStack[this.tagStack.length - 1];
- if (last.handler === handler) {
+ if (last.handler && last.handler === handler) {
this.stack.push(this.current);
this.current = this.current[tag] = [];
} else {
diff --git a/test/caldav/request/asset_test.js b/test/caldav/request/asset_test.js
new file mode 100644
index 0000000..3f9ebcd
--- /dev/null
+++ b/test/caldav/request/asset_test.js
@@ -0,0 +1,108 @@
+requireRequest();
+testSupport.lib('request/asset');
+
+suite('caldav/request/asset.js', function() {
+
+ // classes
+ var Asset;
+ var Xhr;
+ var Connection;
+ var SAX;
+ var FakeXhr;
+
+ // instances
+ var subject;
+ var oldXhrClass;
+ var con;
+ var url = '/foo.ics';
+ var options = {
+ url: url,
+ configOpt: true
+ };
+
+ function lastXHR() {
+ return FakeXhr.instances.pop();
+ }
+
+ suiteSetup(function() {
+ Asset = Caldav.require('request/asset');
+ FakeXhr = Caldav.require('support/fake_xhr');
+ Xhr = Caldav.require('xhr');
+ Connection = Caldav.require('connection');
+
+ oldXhrClass = Xhr.prototype.xhrClass;
+ Xhr.prototype.xhrClass = FakeXhr;
+ });
+
+ suiteTeardown(function() {
+ Xhr.prototype.xhrClass = oldXhrClass;
+ });
+
+ setup(function() {
+ con = new Connection({
+ password: 'password',
+ user: 'user'
+ });
+
+ subject = new Asset(con, url);
+ FakeXhr.instances.length = 0;
+ });
+
+ test('#initializer', function() {
+ assert.equal(subject.connection, con);
+ assert.equal(subject.url, url);
+ });
+
+ suite('#get', function() {
+
+ test('with etag', function(done) {
+ subject.get({ etag: 'foo' }, function(err, data, xhr) {
+ assert.equal(xhr.headers['If-None-Match'], 'foo');
+ done();
+ });
+
+ var xhr = lastXHR();
+ xhr.respond('', 200);
+ });
+
+ test('no options', function(done) {
+ var content = 'content';
+
+ subject.get(function(err, data, xhr) {
+ assert.ok(!err);
+ assert.equal(data, content);
+ assert.equal(xhr.openArgs[0], 'GET');
+ assert.equal(xhr.headers['Content-Type'], subject.contentType);
+ done();
+ });
+
+ var xhr = lastXHR();
+ xhr.respond(content, 200);
+ });
+
+ });
+
+ test('#put', function(done) {
+ var content = 'foo';
+
+ subject.put({ etag: 'x' }, content, function(err, data, xhr) {
+ assert.equal(xhr.openArgs[0], 'PUT');
+ assert.equal(xhr.sendArgs[0], content);
+ done();
+ });
+
+ var xhr = lastXHR();
+ xhr.respond('', 201);
+ });
+
+ test('#delete', function(done) {
+ subject.delete({ etag: 'x' }, function(err, data, xhr) {
+ assert.equal(xhr.openArgs[0], 'DELETE');
+ done();
+ });
+
+ var xhr = lastXHR();
+ xhr.respond('', 201);
+ });
+
+});