diff options
author | James Lal <james@lightsofapollo.com> | 2012-09-30 14:24:23 -0700 |
---|---|---|
committer | James Lal <james@lightsofapollo.com> | 2012-09-30 14:24:23 -0700 |
commit | b73354819340234dbf20d47c1777e4b486e1195c (patch) | |
tree | 3170a45ff1a84621ec1ab44bb1647cb9d7701a92 | |
parent | adcd11c2570d2e11eb201ce7d216b5067521afe0 (diff) | |
download | jsCalDAV-b73354819340234dbf20d47c1777e4b486e1195c.tar.gz |
added Caldav.Request.Asset for adding/deleting/updating single assets
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | caldav.js | 146 | ||||
-rw-r--r-- | lib/caldav/request/asset.js | 141 | ||||
-rw-r--r-- | lib/caldav/request/index.js | 3 | ||||
-rw-r--r-- | lib/caldav/sax/dav_response.js | 2 | ||||
-rw-r--r-- | test/caldav/request/asset_test.js | 108 |
6 files changed, 397 insertions, 4 deletions
@@ -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) @@ -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); + }); + +}); |