Adding HTTP protocol. Give a vector layer the HTTP protocol to create, read, update, and delete features via HTTP. This can be subclassed by protocols that extend HTTP. Thanks for the collaboration (and great tests) on this elemoine - good working with you guys. r=elemoine,me (closes #1652)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@7940 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
Tim Schaub
2008-09-03 19:10:47 +00:00
parent c12cb25aee
commit 6277216053
5 changed files with 1174 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>OpenLayers Vector Behavior Example</title>
<link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
<link rel="stylesheet" href="style.css" type="text/css" />
<script src="../lib/OpenLayers.js"></script>
<script type="text/javascript">
var map;
function init(){
map = new OpenLayers.Map('map');
var wms = new OpenLayers.Layer.WMS(
"OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0",
{layers: 'basic'}
);
var layer = new OpenLayers.Layer.Vector("GML", {
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "gml/polygon.xml",
format: new OpenLayers.Format.GML()
}),
});
map.addLayers([wms, layer]);
map.zoomToExtent(new OpenLayers.Bounds(
-3.92, 44.34, 4.87, 49.55
));
}
</script>
</head>
<body onload="init()">
<h1 id="title">Vector Behavior Example (Fixed/HTTP/GML)</h1>
<p id="shortdesc">
Vector layer with a Fixed strategy, HTTP protocol, and GML format.
</p>
<div id="map" class="smallmap"></div>
<div id="docs">
The vector layer shown uses the Fixed strategy, the HTTP protocol,
and the GML format.
The Fixed strategy is a simple strategy that fetches features once
and never re-requests new data.
The HTTP protocol makes requests using HTTP verbs. It should be
constructed with a url that corresponds to a collection of features
(a resource on some server).
The GML format is used to serialize features.
</div>
</body>
</html>

View File

@@ -184,6 +184,7 @@
"OpenLayers/Strategy.js",
"OpenLayers/Strategy/Fixed.js",
"OpenLayers/Protocol.js",
"OpenLayers/Protocol/HTTP.js",
"OpenLayers/Layer/PointTrack.js",
"OpenLayers/Layer/GML.js",
"OpenLayers/Style.js",

View File

@@ -0,0 +1,454 @@
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
* full text of the license. */
/**
* @requires OpenLayers/Protocol.js
* @requires OpenLayers/Feature/Vector.js
*/
/**
* Class: OpenLayers.Protocol.HTTP
* A basic HTTP protocol for vector layers. Create a new instance with the
* <OpenLayers.Protocol.HTTP> constructor.
*
* Inherits from:
* - <OpenLayers.Protocol>
*/
OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
/**
* Property: url
* {String} - Service URL, read-only, set through the options
* passed to constructor.
*/
url: null,
/**
* Property: headers
* {Object} - HTTP request headers, read-only, set through the options
* passed to the constructor,
* Example: {'Content-Type': 'plain/text'}
*/
headers: null,
/**
* Property: params
* {Object} - Parameters of GET requests, read-only, set through the options
* passed to the constructor,
* Example: {'bbox': '5,5,5,5'}
*/
params: null,
/**
* Property: format
* {<OpenLayers.Format>} Parser for reading and writing features.
*/
format: null,
/**
* Property: callback
* {Object} - Function to be called when the <read>, <create>,
* <update>, <delete> or <commit> operation completes, read-only,
* set through the options passed to the constructor.
*/
callback: null,
/**
* Property: scope
* {Object} - Callback execution scope, read-only, set through the
* options passed to the constructor.
*/
scope: null,
/**
* Constructor: OpenLayers.Protocol.HTTP
* A class for giving layers generic HTTP protocol.
*
* Parameters:
* options - {Object} Optional object whose properties will be set on the
* instance.
*
* Valid options include:
* url - {String}
* headers - {Object}
* params - {Object}
* format - {<OpenLayers.Format>}
* callback - {Function}
* scope - {Object}
*/
initialize: function(options) {
this.params = {};
this.headers = {};
OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
},
/**
* APIMethod: destroy
* Clean up the protocol.
*/
destroy: function() {
this.params = null;
this.headers = null;
OpenLayers.Protocol.prototype.destroy.apply(this);
},
/**
* Method: createCallback
* Returns a function that applies the given public method with resp and
* options arguments.
*
* Parameters:
* method - {Function} The method to be applied by the callback.
* response - {<OpenLayers.Protocol.Response>} The protocol response object.
* options - {Object} Options sent to the protocol method (read, create,
* update, or delete).
*/
createCallback: function(method, response, options) {
return OpenLayers.Function.bind(function() {
method.apply(this, [response, options]);
}, this);
},
/**
* Method: read
* Construct a request for reading new features.
*
* Parameters:
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
* Returns:
* {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
* references the HTTP request, this object is also passed to the
* callback function when the request completes, its "features" property
* is then populated with the the features received from the server.
*/
read: function(options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = new OpenLayers.Protocol.Response({requestType: "read"});
resp.priv = OpenLayers.Request.GET({
url: options.url,
callback: this.createCallback(this.handleRead, resp, options),
params: options.params,
headers: options.headers
});
return resp;
},
/**
* Method: handleRead
* Individual callbacks are created for read, create and update, should
* a subclass need to override each one separately.
*
* Parameters:
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
* the user callback.
* options - {Object} The user options passed to the read call.
*/
handleRead: function(resp, options) {
this.handleResponse(resp, options);
},
/**
* Method: create
* Construct a request for writing newly created features.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>}
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object, whose "priv" property references the HTTP request, this
* object is also passed to the callback function when the request
* completes, its "features" property is then populated with the
* the features received from the server.
*/
create: function(features, options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = new OpenLayers.Protocol.Response({
reqFeatures: features,
requestType: "create"
});
resp.priv = OpenLayers.Request.POST({
url: options.url,
callback: this.createCallback(this.handleCreate, resp, options),
headers: options.headers,
data: this.format.write(features)
});
return resp;
},
/**
* Method: handleCreate
* Called the the request issued by <create> is complete. May be overridden
* by subclasses.
*
* Parameters:
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
* any user callback.
* options - {Object} The user options passed to the create call.
*/
handleCreate: function(resp, options) {
this.handleResponse(resp, options);
},
/**
* Method: update
* Construct a request updating modified feature.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object, whose "priv" property references the HTTP request, this
* object is also passed to the callback function when the request
* completes, its "features" property is then populated with the
* the feature received from the server.
*/
update: function(feature, options) {
var url = options.url || feature.url || this.options.url;
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = new OpenLayers.Protocol.Response({
reqFeatures: feature,
requestType: "update"
});
resp.priv = OpenLayers.Request.PUT({
url: url,
callback: this.createCallback(this.handleUpdate, resp, options),
headers: options.headers,
data: this.format.write(feature)
});
return resp;
},
/**
* Method: handleUpdate
* Called the the request issued by <update> is complete. May be overridden
* by subclasses.
*
* Parameters:
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
* any user callback.
* options - {Object} The user options passed to the update call.
*/
handleUpdate: function(resp, options) {
this.handleResponse(resp, options);
},
/**
* Method: delete
* Construct a request deleting a removed feature.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object, whose "priv" property references the HTTP request, this
* object is also passed to the callback function when the request
* completes.
*/
"delete": function(feature, options) {
var url = options.url || feature.url || this.options.url;
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = new OpenLayers.Protocol.Response({
reqFeatures: feature,
requestType: "delete"
});
resp.priv = OpenLayers.Request.DELETE({
url: url,
callback: this.createCallback(this.handleDelete, resp, options),
headers: options.headers
});
return resp;
},
/**
* Method: handleDelete
* Called the the request issued by <delete> is complete. May be overridden
* by subclasses.
*
* Parameters:
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
* any user callback.
* options - {Object} The user options passed to the delete call.
*/
handleDelete: function(resp, options) {
this.handleResponse(resp, options);
},
/**
* Method: handleResponse
* Called by CRUD specific handlers.
*
* Parameters:
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
* any user callback.
* options - {Object} The user options passed to the create, read, update,
* or delete call.
*/
handleResponse: function(resp, options) {
var request = resp.priv;
if(options.callback) {
if(request.status >= 200 && request.status < 300) {
// success
if(resp.requestType != "delete") {
resp.features = this.parseFeatures(request);
}
resp.code = OpenLayers.Protocol.Response.SUCCESS;
} else {
// failure
resp.code = OpenLayers.Protocol.Response.FAILURE;
}
options.callback.call(options.scope, resp);
}
},
/**
* Method: parseFeatures
* Read HTTP response body and return features.
*
* Parameters:
* request - {XMLHttpRequest} The request object
*
* Returns:
* {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>} Array of features or a single feature.
*/
parseFeatures: function(request) {
var doc = request.responseXML;
if (!doc || !doc.documentElement) {
doc = request.responseText;
}
if (!doc || doc.length <= 0) {
return null;
}
return this.format.read(doc);
},
/**
* Method: commit
* Iterate over each feature and take action based on the feature state.
* Possible actions are create, update and delete.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})}
* options - {Object} Optional object for setting up intermediate commit
* callbacks.
*
* Valid options:
* create - {Object} Optional object to be passed to the <create> method.
* update - {Object} Optional object to be passed to the <update> method.
* delete - {Object} Optional object to be passed to the <delete> method.
* callback - {Function} Optional function to be called when the commit
* is complete.
* scope - {Object} Optional object to be set as the scope of the callback.
*
* Returns:
* {Array(<OpenLayers.Protocol.Response>)} An array of response objects,
* one per request made to the server, each object's "priv" property
* references the corresponding HTTP request.
*/
commit: function(features, options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = [], nResponses = 0;
// Divide up features before issuing any requests. This properly
// counts requests in the event that any responses come in before
// all requests have been issued.
var types = {};
types[OpenLayers.State.INSERT] = [];
types[OpenLayers.State.UPDATE] = [];
types[OpenLayers.State.DELETE] = [];
var feature, list;
for(var i=0, len=features.length; i<len; ++i) {
feature = features[i];
list = types[feature.state];
if(list) {
list.push(feature);
}
}
// tally up number of requests
var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
types[OpenLayers.State.UPDATE].length +
types[OpenLayers.State.DELETE].length;
function callback(response) {
nResponses++;
response.last = (nResponses >= nRequests);
this.callUserCallback(response, options);
}
// start issuing requests
var queue = types[OpenLayers.State.INSERT];
if(queue.length > 0) {
resp.push(this.create(
queue, OpenLayers.Util.applyDefaults(
{callback: callback, scope: this},
options.create || {} // remove || when #1716 is resolved
)
));
}
queue = types[OpenLayers.State.UPDATE];
for(var i=queue.length-1; i>=0; --i) {
resp.push(this.update(
queue[i], OpenLayers.Util.applyDefaults(
{callback: callback, scope: this},
options.update || {} // remove || when #1716 is resolved
))
);
}
queue = types[OpenLayers.State.DELETE];
for(var i=queue.length-1; i>=0; --i) {
resp.push(this["delete"](
queue[i], OpenLayers.Util.applyDefaults(
{callback: callback, scope: this},
options["delete"] || {} // remove || when #1716 is resolved
))
);
}
return resp;
},
/**
* Method: callUserCallback
* This method is used from within the commit method each time an
* an HTTP response is received from the server, it is responsible
* for calling the user-supplied callbacks.
*
* Parameters:
* resp - {<OpenLayers.Protocol.Response>}
* options - {Object} The map of options passed to the commit call.
*/
callUserCallback: function(resp, options) {
var opt = options[resp.requestType];
if(opt && opt.callback) {
opt.callback.call(opt.scope, resp);
}
if(resp.last && options.callback) {
options.callback.call(options.scope);
}
},
CLASS_NAME: "OpenLayers.Protocol.HTTP"
});

669
tests/Protocol/HTTP.html Normal file
View File

@@ -0,0 +1,669 @@
<html>
<head>
<script src="../../lib/OpenLayers.js"></script>
<script type="text/javascript">
function test_constructor(t) {
t.plan(8);
var a = new OpenLayers.Protocol.HTTP({
url: "foo"
});
// 4 tests
t.eq(a.url, "foo", "constructor sets url");
t.eq(a.options.url, a.url, "constructor copies url to options.url");
t.eq(a.params, {}, "constructor sets params");
t.eq(a.options.params, undefined, "constructor do not copy params to options.params");
var params = {hello: "world"};
var b = new OpenLayers.Protocol.HTTP({
url: "bar",
params: params
});
// 4 tests
t.eq(b.url, "bar", "constructor sets url");
t.eq(b.options.url, b.url, "constructor copies url to options.url");
t.eq(b.params, params, "constructor sets params");
t.eq(b.options.params, b.params, "constructor copies params to options.params");
}
function test_destroy(t) {
t.plan(3);
var protocol = new OpenLayers.Protocol.HTTP({
url: "bar",
params: {hello: "world"}
});
protocol.destroy();
t.eq(protocol.options, null, "destroy nullifies options");
t.eq(protocol.params, null, "destroy nullifies params");
t.eq(protocol.headers, null, "destroy nullifies headers");
}
function test_read(t) {
t.plan(10);
var protocol = new OpenLayers.Protocol.HTTP({
'url': 'foo_url',
'params': {'k': 'foo_param'}
});
// fake XHR request object
var request = {'status': 200};
// options to pass to read
var readOptions = {
'url': 'bar_url',
'params': {'k': 'bar_param'},
'headers': {'k': 'bar_header'},
'scope': {'hello': 'world'},
'callback': function() {}
};
var response;
protocol.handleResponse = function(resp, opt) {
// 4 tests
var req = resp.priv;
t.ok(this == protocol,
'handleResponse called with correct scope');
t.ok(opt == readOptions,
'handleResponse called with correct options');
t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
'handleResponse called with a Response object');
t.eq(req, request,
'handleResponse called with correct request');
response = resp;
};
var _get = OpenLayers.Request.GET;
OpenLayers.Request.GET = function(options) {
// 5 tests
t.eq(options.url, readOptions.url,
'GET called with correct url in options');
t.eq(options.params['k'], readOptions.params['k'],
'GET called with correct params in options');
t.eq(options.headers['k'], readOptions.headers['k'],
'GET called with correct headers in options');
t.eq(options.scope, undefined,
'GET called with correct scope in options');
t.ok(typeof options.callback == 'function',
'GET called with a callback in options');
t.delay_call(0.1, function() {
options.callback(request);
t.ok(resp == response,
'read returns the expected response object');
// cleanup
protocol.destroy();
OpenLayers.Request.GET = _get;
});
return request;
};
var resp = protocol.read(readOptions);
}
function test_parseFeatures(t) {
t.plan(5);
var protocol = new OpenLayers.Protocol.HTTP();
// test responseXML - 2 tests
var request = {
'responseXML': {
'documentElement': 'xml'
}
};
protocol.format = {
'read': function(doc) {
t.eq(doc.documentElement, 'xml',
'format.read called with correct doc');
return doc.documentElement;
}
};
var ret = protocol.parseFeatures(request);
t.eq(ret, 'xml', 'parseFeatures returns expected value');
// test responseText - 2 tests
var request = {
'responseText': 'text'
};
protocol.format = {
'read': function(doc) {
t.eq(doc, 'text',
'format.read called with correct doc');
return doc;
}
};
var ret = protocol.parseFeatures(request);
t.eq(ret, 'text', 'parseFeatures returns expected value');
// test empty responseText - 1 test
var request = {
'responseText': ''
};
protocol.format = {
'read': function(doc) {
t.fail('format.read should not be called');
}
};
var ret = protocol.parseFeatures(request);
t.eq(ret, null, 'parseFeatures returns expected value');
}
function test_create(t) {
t.plan(10);
var protocol = new OpenLayers.Protocol.HTTP({
'url': 'foo_url',
'format': {'write': function() {}}
});
// fake XHR request object
var request = {'status': 200};
// features to pass to create
var features = ['feature'];
// options to pass to create
var createOptions = {
'url': 'bar_url',
'headers': {'k': 'bar_header'},
'scope': {'hello': 'world'},
'callback': function() {}
};
var response;
protocol.handleCreate = function(resp, opt) {
// 5 tests
var req = resp.priv;
t.ok(this == protocol,
'handleCreate called with correct scope');
t.ok(opt == createOptions,
'handleCreate called with correct options');
t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
'handleCreate called with a Response object');
t.ok(resp.reqFeatures == features,
'handleCreate called with correct requested features in response');
t.eq(req, request,
'handleCreate called with correct request');
response = resp;
};
var _post = OpenLayers.Request.POST;
OpenLayers.Request.POST = function(options) {
// 4 tests
t.eq(options.url, createOptions.url,
'POST called with correct url in options');
t.eq(options.headers['k'], createOptions.headers['k'],
'POST called with correct headers in options');
t.eq(options.scope, undefined,
'POST called with correct scope in options');
t.ok(typeof options.callback == 'function',
'POST called with a callback in options');
// call callback - delayed because this function has to return first
t.delay_call(0.1, function() {
options.callback(request);
t.ok(resp == response,
'create returns the expected response object');
// cleanup
protocol.destroy();
OpenLayers.Request.POST = _post;
});
return request;
};
var resp = protocol.create(features, createOptions);
}
function test_update(t) {
t.plan(10);
var protocol = new OpenLayers.Protocol.HTTP({
'url': 'foo_url',
'format': {'write': function() {}}
});
// fake XHR request object
var request = {'status': 200};
// feature to pass to update
var feature = {'feature':'feature'};
// options to pass to update
var updateOptions = {
'url': 'bar_url',
'headers': {'k': 'bar_header'},
'scope': {'hello': 'world'},
'callback': function() {}
};
var response;
protocol.handleUpdate = function(resp, opt) {
var req = resp.priv;
// 5 tests
t.ok(this == protocol,
'handleUpdate called with correct scope');
t.ok(opt == updateOptions,
'handleUpdate called with correct options');
t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
'handleUpdate called with a Response object');
t.ok(resp.reqFeatures == feature,
'handleUpdate called with correct requested feature in response');
t.eq(req, request,
'handleUpdate called with correct request');
response = resp;
};
var _put = OpenLayers.Request.PUT;
OpenLayers.Request.PUT = function(options) {
// 4 tests
t.eq(options.url, updateOptions.url,
'PUT called with correct url in options');
t.eq(options.headers['k'], updateOptions.headers['k'],
'PUT called with correct headers in options');
t.eq(options.scope, undefined,
'PUT called with correct scope in options');
t.ok(typeof options.callback == 'function',
'PUT called with a callback in options');
// call callback - delayed because this function has to return first
t.delay_call(0.1, function() {
options.callback(request);
t.ok(resp == response,
'update returns the expected response object');
// cleanup
protocol.destroy();
OpenLayers.Request.PUT = _put;
});
return request;
};
var resp = protocol.update(feature, updateOptions);
}
function test_handleResponse(t) {
t.plan(6);
var protocol = new OpenLayers.Protocol.HTTP();
var options, response, request, features;
// test options - 2 tests
var scope = {'fake': 'scope'};
options = {
'scope': scope,
'callback': function(resp) {
t.ok(this == scope,
'[no status] callback called with correct scope');
t.ok(resp == response,
'[no status] callback called with correct response');
}
};
response = {priv: {}};
protocol.handleResponse(response, options);
// test failure condition - 1 test
options = {
'callback': function(resp) {
t.eq(resp.code, OpenLayers.Protocol.Response.FAILURE,
'[status 400] callback called with correct response code');
}
};
response = {priv: {status: 400}};
protocol.handleResponse(response, options);
// test success condition - 3 tests
features = {'fake': 'features'};
options = {
'callback': function(resp) {
t.eq(resp.code, OpenLayers.Protocol.Response.SUCCESS,
'[status 200] callback called with correct response code');
t.eq(resp.features, features,
'[status 200] callback called with correct features in response');
}
};
response = {priv: {status: 200}};
protocol.parseFeatures = function(request) {
t.ok(request == response.priv,
'[status 200] parseFeatures called with correct request');
return features;
}
protocol.handleResponse(response, options);
// cleanup
protocol.destroy();
}
function test_delete(t) {
t.plan(10);
var protocol = new OpenLayers.Protocol.HTTP({
'url': 'foo_url'
});
// fake XHR request object
var request = {'status': 200};
// feature to pass to delete
var feature = {'url': 'bar_url'};
// options to pass to delete
var deleteOptions = {
'url': 'bar_url',
'headers': {'k': 'bar_header'},
'scope': {'hello': 'world'},
'callback': function() {}
};
var response;
protocol.handleDelete = function(resp, opt) {
// 5 tests
var req = resp.priv;
t.ok(this == protocol,
'handleDelete called with correct scope');
t.ok(opt == deleteOptions,
'handleDelete called with correct options');
t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
'handleDelete called with a Response object');
t.ok(resp.reqFeatures == feature,
'handleDelete called with correct requested feature in response');
t.eq(req, request,
'handleDelete called with correct request');
response = resp;
};
var _delete = OpenLayers.Request.DELETE;
OpenLayers.Request.DELETE = function(options) {
// 4 tests
t.eq(options.url, deleteOptions.url,
'DELETE called with correct url in options');
t.eq(options.headers['k'], deleteOptions.headers['k'],
'DELETE called with correct headers in options');
t.eq(options.scope, undefined,
'DELETE called with correct scope in options');
t.ok(typeof options.callback == 'function',
'DELETE called with a callback in options');
// call callback - delayed because this function has to return first
t.delay_call(0.1, function() {
options.callback(request);
t.ok(resp == response,
'read returns the expected response object');
// cleanup
protocol.destroy();
OpenLayers.Request.DELETE = _delete;
});
return request;
};
var resp = protocol['delete'](feature, deleteOptions);
}
function test_handleDelete(t) {
t.plan(4);
var protocol = new OpenLayers.Protocol.HTTP();
var options, response, request, features;
// test options - 2 tests
var scope = {'fake': 'scope'};
options = {
'scope': scope,
'callback': function(resp) {
t.ok(this == scope,
'callback called with correct scope');
t.ok(resp == response,
'callback called with correct response');
}
};
response = {priv: {}};
protocol.handleDelete(response, options);
// test failure condition - 1 test
options = {
'callback': function(resp) {
t.eq(resp.code, OpenLayers.Protocol.Response.FAILURE,
'callback called with correct response code');
}
};
response = {priv: {status: 400}};
protocol.handleDelete(response, options);
// test success condition - 1 test
options = {
'callback': function(resp) {
t.eq(resp.code, OpenLayers.Protocol.Response.SUCCESS,
'callback called with correct response code');
}
};
response = {priv: {status: 200}};
protocol.handleDelete(response, options);
// cleanup
protocol.destroy();
}
function test_commit(t) {
t.plan(17);
var protocol = new OpenLayers.Protocol.HTTP();
// 6 features
var features = [
{'state': OpenLayers.State.INSERT},
{'state': OpenLayers.State.INSERT},
{'state': OpenLayers.State.UPDATE},
{'state': OpenLayers.State.UPDATE},
{'state': OpenLayers.State.DELETE},
{'state': OpenLayers.State.DELETE}
];
var options = {
'create': {
'callback': function(resp) {
}
},
'update': {
'callback': function(resp) {
}
},
'delete': {
'callback': function(resp) {
}
}
};
var respCreate = new OpenLayers.Protocol.Response();
var respUpdate = new OpenLayers.Protocol.Response();
var respDelete = new OpenLayers.Protocol.Response();
// 2 tests
protocol['create'] = function(feature, options) {
t.ok(options.scope == protocol,
'create called with correct scope');
t.ok(typeof options.callback == 'function',
'create called with a callback in options');
options.callback.call(options.scope, respCreate);
return respCreate;
};
// 4 tests
protocol['update'] = function(feature, options) {
t.ok(options.scope == protocol,
'update called with correct scope');
t.ok(typeof options.callback == 'function',
'update called with a callback in options');
options.callback.call(options.scope, respUpdate);
return respUpdate;
};
// 4 tests
protocol['delete'] = function(feature, options) {
t.ok(options.scope == protocol,
'delete called with correct scope');
t.ok(typeof options.callback == 'function',
'delete called with a callback in options');
options.callback.call(options.scope, respDelete);
return respDelete;
};
var count = 0;
// 5 tests
protocol.callUserCallback = function(resp, opt) {
t.ok(opt == options,
'callUserCallback called with correction options map');
count++;
};
var resp = protocol.commit(features, options);
// 2 tests
t.eq(count, 5, 'callUserCallback called for each request');
t.eq(resp.length, 5, 'commit returns array with correct length');
// cleanup
protocol.destroy();
}
function test_callUserCallback(t) {
t.plan(6);
var protocol = new OpenLayers.Protocol.HTTP();
var options, resp;
var scope = {'fake': 'scope'};
// test commit callback
// 1 tests
options = {
'callback': function() {
t.ok(this == scope, 'callback called with correct scope');
},
'scope': scope
};
resp = {'requestType': 'create', 'last': true};
protocol.callUserCallback(resp, options);
// 0 test
resp = {'requestType': 'create', 'last': false};
protocol.callUserCallback(resp, options);
// test create callback
// 2 tests
options = {
'create': {
'callback': function(r) {
t.ok(this == scope, 'callback called with correct scope');
t.ok(r == resp, 'callback called with correct response');
},
'scope': scope
}
};
resp = {'requestType': 'create'};
protocol.callUserCallback(resp, options);
// test with both callbacks set
// 3 tests
options = {
'create': {
'callback': function(r) {
t.ok(this == scope, 'callback called with correct scope');
t.ok(r == resp, 'callback called with correct response');
},
'scope': scope
},
'callback': function() {
t.ok(this == scope, 'callback called with correct scope');
},
'scope': scope
};
resp = {'requestType': 'create', 'last': true};
protocol.callUserCallback(resp, options);
// no callback set
// 0 test
options = {
'delete': {
'callback': function(resp) {
t.fail('callback should not get called');
}
}
};
resp = {'requestType': 'create'};
protocol.callUserCallback(resp, options);
// cleanup
protocol.destroy();
}
function test_options(t) {
t.plan(7);
var _R = OpenLayers.Protocol.Response;
OpenLayers.Protocol.Response = function() {
this.priv = {status: 200};
};
// test that read with no options uses protocol options - 5 tests
var url = "foo";
var headers = {};
var params = {};
var scope = {};
var protocol = new OpenLayers.Protocol.HTTP({
format: new OpenLayers.Format({read: function(){}, write: function(){}}),
url: url,
headers: headers,
params: params,
callback: function() {
t.ok(true, "[read] Correct callback.");
t.eq(this, scope, "[read] Correct scope.");
},
scope: scope
});
var _issue = OpenLayers.Request.issue;
OpenLayers.Request.issue = function(config) {
t.eq(config.url, url, "[" + config.method + "] Correct url.");
t.eq(config.headers, headers, "[" + config.method + "] Correct headers.");
t.eq(config.params, params, "[" + config.method + "] Correct params.");
config.callback.call(config.scope);
};
protocol.read();
OpenLayers.Request.issue = _issue;
// test that commit with no options uses protocol options - 2 tests
_issue = OpenLayers.Request.issue;
OpenLayers.Request.issue = function(config) {
config.callback.call(config.scope);
};
var called = 0;
protocol.options.callback = function() {
called++;
t.eq(this, scope, "[commit] Correct scope.");
};
protocol.commit([
{state: OpenLayers.State.INSERT},
{state: OpenLayers.State.INSERT},
{state: OpenLayers.State.UPDATE},
{state: OpenLayers.State.UPDATE},
{state: OpenLayers.State.DELETE},
{state: OpenLayers.State.DELETE}
]);
t.eq(called, 1, "[commit] Callback called once.");
// cleanup
protocol.destroy();
OpenLayers.Protocol.Response = _R;
}
</script>
</head>
<body>
</body>
</html>

View File

@@ -113,6 +113,7 @@
<li>Popup/FramedCloud.html</li>
<li>Projection.html</li>
<li>Protocol.html</li>
<li>Protocol/HTTP.html</li>
<li>Renderer.html</li>
<li>Renderer/Canvas.html</li>
<li>Renderer/Elements.html</li>