arcgis-server-store
Advanced tools
Comparing version 1.0.5 to 1.1.0
@@ -7,2 +7,3 @@ define([ | ||
'dojo/Deferred', | ||
'dojo/promise/all', | ||
'dojo/store/util/QueryResults', | ||
@@ -15,3 +16,3 @@ 'dojo/when', | ||
array, declare, lang, | ||
Deferred, QueryResults, when, | ||
Deferred, all, QueryResults, when, | ||
esriRequest, Query | ||
@@ -23,2 +24,6 @@ ) { | ||
var args = arguments; | ||
if (context._transaction) { | ||
args[1] = lang.mixin({_transaction: context._transaction}, args[1]); | ||
args.length = Math.max(args.length, 2); | ||
} | ||
return deferred.then(function() { | ||
@@ -51,2 +56,139 @@ return callback.apply(context, args); | ||
var _loadTransactionWrapper = function(deferred, callback, context) { | ||
return function() { | ||
callback.apply(context, arguments); | ||
return { | ||
commit: cleanup(function() { | ||
var transaction = this; | ||
return deferred.then(function() { | ||
return commit.apply(transaction); | ||
}); | ||
}, context, context._transaction), | ||
abort: cleanup(function() { | ||
var transaction = this; | ||
return deferred.then(function() { | ||
return abort.apply(transaction); | ||
}); | ||
}, context, context._transaction) | ||
}; | ||
}; | ||
}; | ||
var abort = function(message) { | ||
var dfd = new Deferred(); | ||
if (this._aborted) { | ||
dfd.cancel('Transaction aborted.'); | ||
} else if (this._committed) { | ||
dfd.reject('Transaction committed.'); | ||
} else { | ||
this._aborted = true; | ||
var transactions = [].concat(this._promises, this.puts, this.adds, this.removes); | ||
array.forEach(transactions, function(transaction) { | ||
if (transaction) { | ||
transaction = transaction.deferred || transaction; | ||
if (lang.isFunction(transaction.cancel)) { | ||
transaction.cancel(message || 'Transaction aborted.'); | ||
} | ||
} | ||
}); | ||
dfd.resolve(); | ||
} | ||
return dfd.promise; | ||
}; | ||
var commit = function() { | ||
var dfd = new Deferred(); | ||
if (this._aborted) { | ||
dfd.reject('Transaction aborted.'); | ||
} else if (this._committed) { | ||
dfd.cancel('Transaction committed.'); | ||
} else { | ||
this._committed = true; | ||
return all(this._promises || []).then(lang.hitch(this, function() { | ||
var puts = array.map(this.puts || [], function(put) { | ||
return put.feature; | ||
}); | ||
var adds = array.map(this.adds || [], function(add) { | ||
return add.feature; | ||
}); | ||
var removes = []; | ||
array.forEach(this.removes || [], function(remove) { | ||
removes = removes.concat(remove.ids); | ||
}); | ||
return esriRequest({ | ||
url: this._store.url + '/applyEdits', | ||
content: { | ||
f: 'json', | ||
updates: JSON.stringify(puts), | ||
adds: JSON.stringify(adds), | ||
deletes: removes.join(',') | ||
}, | ||
handleAs: 'json', | ||
callbackParamName: 'callback' | ||
}, { | ||
usePost: true | ||
}).then(lang.hitch(this, function(response) { | ||
array.forEach(this.puts, lang.hitch(this, function(put, i) { | ||
var updateResult = response.updateResults[i]; | ||
if (updateResult.success) { | ||
if (this._store.idProperty === this._store._serviceInfo.objectIdField) { | ||
put.deferred.resolve(updateResult.objectId); | ||
} else { | ||
put.deferred.resolve(put.id); | ||
} | ||
} else { | ||
put.deferred.reject(); | ||
} | ||
})); | ||
array.forEach(this.adds, lang.hitch(this, function(add, i) { | ||
var addResult = response.addResults[i]; | ||
if (addResult.success) { | ||
if (this._store.idProperty === this._store._serviceInfo.objectIdField) { | ||
add.deferred.resolve(addResult.objectId); | ||
} else { | ||
add.deferred.resolve(typeof add.id != 'undefined' ? add.id : null); | ||
} | ||
} else { | ||
add.deferred.reject(); | ||
} | ||
})); | ||
array.forEach(this.removes, function(remove) { | ||
remove.ids = array.filter(remove.ids, function(id) { | ||
var success = array.some(response.deleteResults, function(deleteResult) { | ||
return deleteResult.objectId === id && deleteResult.success; | ||
}); | ||
return !success; | ||
}); | ||
if (!remove.ids.length) { | ||
remove.deferred.resolve(true); | ||
} else { | ||
remove.deferred.reject(); | ||
} | ||
}); | ||
}), lang.hitch(this, function(error) { | ||
this._committed = false; | ||
abort.apply(this, arguments); | ||
})); | ||
}), lang.hitch(this, function() { | ||
this._committed = false; | ||
abort.apply(this, arguments); | ||
})); | ||
} | ||
return dfd.promise; | ||
}; | ||
var cleanup = function(action, store, transaction) { | ||
return function() { | ||
if (store._transaction === transaction) { | ||
delete store._transaction; | ||
} | ||
return action.apply(transaction); | ||
}; | ||
}; | ||
return declare(null, { | ||
@@ -107,2 +249,3 @@ /** | ||
var query = this.query; | ||
var transaction = this.transaction; | ||
@@ -115,2 +258,3 @@ loadDfd.then(lang.hitch(this, function() { | ||
this.query = query; | ||
this.transaction = transaction; | ||
})); | ||
@@ -123,2 +267,3 @@ | ||
this.query = _loadQueryWrapper(loadDfd, this.query, this); | ||
this.transaction = _loadTransactionWrapper(loadDfd, this.transaction, this); | ||
}, | ||
@@ -179,3 +324,8 @@ /** | ||
var dfd = new Deferred(); | ||
when(options.overwrite || this.get(id)).then(lang.hitch(this, function(existing) { | ||
var promise = when(options.overwrite || this.get(id)); | ||
var transaction = options._transaction || this._transaction; | ||
if (transaction) { | ||
transaction._promises.push(promise); | ||
} | ||
promise.then(lang.hitch(this, function(existing) { | ||
if (existing) { | ||
@@ -185,17 +335,29 @@ if (this.capabilities.Update) { | ||
lang.setObject('attributes.' + this.idProperty, id, object); | ||
esriRequest({ | ||
url: this.url + '/updateFeatures', | ||
content: { | ||
f: 'json', | ||
features: JSON.stringify([object]) | ||
}, | ||
handleAs: 'json', | ||
callbackParamName: 'callback' | ||
}, { | ||
usePost: true | ||
}).then(function(response) { | ||
if (response.updateResults && response.updateResults.length) { | ||
dfd.resolve(response.updateResults[0].success ? response.updateResults[0].objectId : undefined); | ||
} | ||
}, dfd.reject); | ||
if (transaction) { | ||
transaction.puts.push({ | ||
deferred: dfd, | ||
feature: object, | ||
id: id | ||
}); | ||
} else { | ||
esriRequest({ | ||
url: this.url + '/updateFeatures', | ||
content: { | ||
f: 'json', | ||
features: JSON.stringify([object]) | ||
}, | ||
handleAs: 'json', | ||
callbackParamName: 'callback' | ||
}, { | ||
usePost: true | ||
}).then(lang.hitch(this, function(response) { | ||
if (response.updateResults && response.updateResults.length) { | ||
if (this.idProperty === this._serviceInfo.objectIdField) { | ||
dfd.resolve(response.updateResults[0].success ? response.updateResults[0].objectId : undefined); | ||
} else { | ||
dfd.resolve(response.updateResults[0].success ? id : undefined); | ||
} | ||
} | ||
}), dfd.reject); | ||
} | ||
} else { | ||
@@ -205,2 +367,6 @@ dfd.reject(new Error('Update not supported.')); | ||
} else { | ||
if (transaction && !options._transaction) { | ||
options = lang.clone(options); | ||
options._transaction = transaction; | ||
} | ||
when(this.add(object, options)).then(dfd.resolve, dfd.reject); | ||
@@ -225,2 +391,3 @@ } | ||
if (this.capabilities.Create) { | ||
var dfd = new Deferred(); | ||
var id = ('id' in options) ? options.id : this.getIdentity(object); | ||
@@ -232,25 +399,37 @@ var clone = this._unflatten(lang.clone(object)); | ||
console.warn('Cannot set id on new object.'); | ||
id = undefined; | ||
} | ||
return esriRequest({ | ||
url: this.url + '/addFeatures', | ||
content: { | ||
f: 'json', | ||
features: JSON.stringify([clone]) | ||
}, | ||
handleAs: 'json', | ||
callbackParamName: 'callback' | ||
}, { | ||
usePost: true | ||
}).then(lang.hitch(this, function(response) { | ||
if (response.addResults && response.addResults.length) { | ||
if (this.idProperty === this._serviceInfo.objectIdField) { | ||
var oid = response.addResults[0].success ? response.addResults[0].objectId : undefined; | ||
lang.setObject((this.flatten ? '' : 'attributes.') + this.idProperty, oid, object); | ||
return oid; | ||
} else { | ||
return response.addResults[0].success ? id : undefined; | ||
var transaction = options._transaction || this._transaction; | ||
if (transaction) { | ||
transaction.adds.push({ | ||
deferred: dfd, | ||
feature: clone, | ||
id: id | ||
}); | ||
} else { | ||
esriRequest({ | ||
url: this.url + '/addFeatures', | ||
content: { | ||
f: 'json', | ||
features: JSON.stringify([clone]) | ||
}, | ||
handleAs: 'json', | ||
callbackParamName: 'callback' | ||
}, { | ||
usePost: true | ||
}).then(lang.hitch(this, function(response) { | ||
if (response.addResults && response.addResults.length) { | ||
if (this.idProperty === this._serviceInfo.objectIdField) { | ||
var oid = response.addResults[0].success ? response.addResults[0].objectId : undefined; | ||
lang.setObject((this.flatten ? '' : 'attributes.') + this.idProperty, oid, object); | ||
dfd.resolve(oid); | ||
} else { | ||
dfd.resolve(response.addResults[0].success ? id : undefined); | ||
} | ||
} | ||
} | ||
})); | ||
}), dfd.reject); | ||
} | ||
return dfd.promise; | ||
} else { | ||
@@ -264,3 +443,4 @@ throw new Error('Add not supported.'); | ||
*/ | ||
remove: function(id) { | ||
remove: function(id, options) { | ||
options = options || {}; | ||
if (this.capabilities.Delete) { | ||
@@ -274,15 +454,47 @@ var where = ''; | ||
return esriRequest({ | ||
url: this.url + '/deleteFeatures', | ||
content: { | ||
f: 'json', | ||
where: where | ||
}, | ||
handleAs: 'json', | ||
callbackParamName: 'callback' | ||
}, { | ||
usePost: true | ||
}).then(function(response) { | ||
return !!(response && response.success); | ||
}); | ||
var dfd = new Deferred(); | ||
var transaction = options._transaction || this._transaction; | ||
if (transaction) { | ||
if (this.idProperty === this._serviceInfo.objectIdField) { | ||
transaction.removes.push({ | ||
deferred: dfd, | ||
ids: [id] | ||
}); | ||
} else { | ||
var promise = esriRequest({ | ||
url: this.url + '/query', | ||
content: { | ||
f: 'json', | ||
where: where, | ||
returnIdsOnly: true | ||
}, | ||
handleAs: 'json', | ||
callbackParamaName: 'callback' | ||
}).then(lang.hitch(this, function(response) { | ||
if (response.objectIds) { | ||
transaction.removes.push({ | ||
deferred: dfd, | ||
ids: response.objectIds | ||
}); | ||
} | ||
}), dfd.reject); | ||
transaction._promises.push(promise); | ||
} | ||
} else { | ||
esriRequest({ | ||
url: this.url + '/deleteFeatures', | ||
content: { | ||
f: 'json', | ||
where: where | ||
}, | ||
handleAs: 'json', | ||
callbackParamName: 'callback' | ||
}, { | ||
usePost: true | ||
}).then(function(response) { | ||
dfd.resolve(!!(response && response.success)); | ||
}, dfd.reject); | ||
} | ||
return dfd.promise; | ||
} else { | ||
@@ -417,2 +629,22 @@ throw new Error('Remove not supported.'); | ||
/** | ||
* Starts a new transaction. | ||
* @return {Object} Store transaction | ||
*/ | ||
transaction: function() { | ||
var transaction = { | ||
_store: this, | ||
_promises: [], | ||
puts: [], | ||
adds: [], | ||
removes: [] | ||
}; | ||
this._transaction = transaction; | ||
return { | ||
commit: cleanup(commit, this, transaction), | ||
abort: cleanup(abort, this, transaction) | ||
}; | ||
}, | ||
/** | ||
* Flatten attributes to top-level object | ||
@@ -419,0 +651,0 @@ * @param {Object} object Object to flatten |
{ | ||
"name": "arcgis-server-store", | ||
"version": "1.0.5", | ||
"version": "1.1.0", | ||
"description": "An implementation of the dojo/store API for ArcGIS Server REST services", | ||
@@ -5,0 +5,0 @@ "keywords": ["Esri", "ArcGIS", "Dojo", "store"], |
@@ -29,2 +29,3 @@ # ArcGISServerStore | ||
| `remove(id)` | This deletes an object, using the identity to indicate which object to delete. This returns a promise that resolves to a boolean value indicating whether the object was successfully removed. | | ||
| `query(query, [options])` | This queries the store for objects. The query can be an object specifying attribute values, or an `esri\tasks\query` object. This returns a promise that resolves to the results of the query, extended with iterative methods. | | ||
| `query(query, [options])` | This queries the store for objects. The query can be an object specifying attribute values, or an `esri\tasks\query` object. This returns a promise that resolves to the results of the query, extended with iterative methods. | | ||
| `transaction()` | This creates a new transaction on the store. This returns a transaction object with `commit` and `abort` methods. | |
{ | ||
"name": "arcgis-server-store", | ||
"version": "v1.0.5", | ||
"version": "v1.1.0", | ||
"description": "An implementation of the dojo/store API for ArcGIS Server REST services", | ||
@@ -5,0 +5,0 @@ "keywords": ["Esri", "ArcGIS", "Dojo", "store"], |
@@ -114,2 +114,8 @@ #arcgis-server-store | ||
store.remove(1); | ||
// Batch updates | ||
var trans = store.transaction(); | ||
store.add({NAME: 'New', CATEGORY: 'Sample Transaction'}); | ||
store.put({OBJECTID: 2, NAME: 'Edited', CATEGORY: 'Transaction'}); | ||
trans.commit(); | ||
``` | ||
@@ -116,0 +122,0 @@ |
@@ -1239,2 +1239,251 @@ define([ | ||
}); | ||
registerSuite({ | ||
name: 'transaction', | ||
setup: function() { | ||
MockMapService.start(); | ||
MockFeatureService.start(); | ||
}, | ||
teardown: function() { | ||
MockMapService.stop(); | ||
MockFeatureService.stop(); | ||
}, | ||
beforeEach: function() { | ||
MockFeatureService.reset(); | ||
}, | ||
'transaction no capability': function() { | ||
// Setup | ||
var dfd = this.async(1000); | ||
var store = new ArcGISServerStore({ | ||
url: mapService | ||
}); | ||
var putObject = { | ||
ESRI_OID: 1, | ||
NAME: 'Put Object' | ||
}; | ||
var addObject = { | ||
NAME: 'Add Object' | ||
}; | ||
var oid = 1; | ||
// Test | ||
var transaction = store.transaction(); | ||
var put = when(store.put(putObject)); | ||
var add = when(store.add(addObject)); | ||
var remove = when(store.remove(oid)); | ||
put.then(dfd.reject.bind(dfd), function(putError) { | ||
add.then(dfd.reject.bind(dfd), function(addError) { | ||
remove.then(dfd.reject.bind(dfd), dfd.callback(function(removeError) { | ||
assert.instanceOf(putError, Error, 'Map service does not support Update capability. Should receive an error'); | ||
assert.strictEqual(putError.message, 'Update not supported.', 'Should receive a custom error message'); | ||
assert.instanceOf(addError, Error, 'Map service does not support Delete capability. Should receive an error'); | ||
assert.strictEqual(addError.message, 'Add not supported.', 'Should receive a custom error message'); | ||
assert.instanceOf(removeError, Error, 'Map service does not support Add capability. Should receive an error'); | ||
assert.strictEqual(removeError.message, 'Remove not supported.', 'Should receive a custom error message'); | ||
})); | ||
}); | ||
}); | ||
transaction.commit(); | ||
}, | ||
'transaction abort': function() { | ||
// Setup | ||
var dfd = this.async(1000); | ||
var store = new ArcGISServerStore({ | ||
url: featureService | ||
}); | ||
var putObject = { | ||
ESRI_OID: 1, | ||
NAME: 'Put Object' | ||
}; | ||
var addObject = { | ||
NAME: 'Add Object' | ||
}; | ||
var oid = 1; | ||
// Test | ||
var transaction = store.transaction(); | ||
var put = when(store.put(putObject)); | ||
var add = when(store.add(addObject)); | ||
var remove = when(store.remove(oid)); | ||
put.then(dfd.reject.bind(dfd), function(putError) { | ||
add.then(dfd.reject.bind(dfd), function(addError) { | ||
remove.then(dfd.reject.bind(dfd), dfd.callback(function(removeError) { | ||
assert.strictEqual(putError, 'Transaction aborted.', 'Should receive a custom error message'); | ||
assert.strictEqual(addError, 'Transaction aborted.', 'Should receive a custom error message'); | ||
assert.strictEqual(removeError, 'Transaction aborted.', 'Should receive a custom error message'); | ||
})); | ||
}); | ||
}); | ||
transaction.abort(); | ||
}, | ||
'transaction commit': function() { | ||
// Setup | ||
var dfd = this.async(1000); | ||
var store = new ArcGISServerStore({ | ||
url: featureService, | ||
returnGeometry: false | ||
}); | ||
var putObject = { | ||
NAME: 'Put Object', | ||
ESRI_OID: 5, | ||
DETAILS: 'Something new' | ||
}; | ||
var addObject = { | ||
NAME: 'Add Object', | ||
DETAILS: 'Mocking Add', | ||
CATEGORY: 4321 | ||
}; | ||
var oid = 1; | ||
// Test | ||
var transaction = store.transaction(); | ||
all({ | ||
put: when(store.put(lang.clone(putObject))), | ||
add: when(store.add(lang.clone(addObject))), | ||
remove: when(store.remove(oid)) | ||
}).then(function(results) { | ||
addObject.ESRI_OID = results.add; | ||
all({ | ||
put: when(store.get(results.put)), | ||
add: when(store.get(results.add)), | ||
remove: when(store.get(oid)) | ||
}).then(dfd.callback(function(getResults) { | ||
var updated = getResults.put; | ||
assert.isDefined(updated.CATEGORY, 'Put should only overwrite fields.'); | ||
assert.deepEqual(updated, lang.mixin(lang.clone(updated), putObject), 'Put should update object with values.'); | ||
var added = getResults.add; | ||
assert.isObject(added, 'Object should be added to store'); | ||
assert.strictEqual(results.add, store.getIdentity(added), 'Add should return new id'); | ||
assert.deepEqual(added, addObject, 'Object should be added to store without modifying properties'); | ||
var removed = getResults.remove; | ||
assert.isTrue(results.remove, 'Existing id should remove successfully.'); | ||
assert.isUndefined(removed, 'Removed id should no longer exist in store.'); | ||
}), dfd.reject.bind(dfd)); | ||
}, dfd.reject.bind(dfd)); | ||
transaction.commit(); | ||
}, | ||
'multiple transactions': function() { | ||
// Setup | ||
var dfd = this.async(1000); | ||
var store = new ArcGISServerStore({ | ||
url: featureService, | ||
idProperty: 'NAME', | ||
returnGeometry: false | ||
}); | ||
var putObject = { | ||
NAME: 'Mock Test Point 1', | ||
ESRI_OID: 1, | ||
DETAILS: 'Something new' | ||
}; | ||
var addObject = { | ||
NAME: 'custID-1234', | ||
DETAILS: 'Mocking Add', | ||
CATEGORY: 0 | ||
}; | ||
var removeId = 'Mock Test Point 2'; | ||
// Test | ||
var transaction = store.transaction(); | ||
all({ | ||
put: when(store.put(lang.clone(putObject))), | ||
add: when(store.add(lang.clone(addObject))), | ||
remove: when(store.remove(removeId)) | ||
}).then(function(results) { | ||
addObject[store.idProperty] = results.add; | ||
all({ | ||
put: when(store.get(results.put)), | ||
add: when(store.get(results.add)), | ||
remove: when(store.get(removeId)) | ||
}).then(dfd.callback(function(getResults) { | ||
var updated = getResults.put; | ||
assert.isDefined(updated.CATEGORY, 'Put should only overwrite fields.'); | ||
assert.deepEqual(updated, lang.mixin(lang.clone(updated), putObject), 'Put should update object with values.'); | ||
var added = getResults.add; | ||
delete added.ESRI_OID; | ||
assert.isObject(added, 'Object should be added to store'); | ||
assert.strictEqual(results.add, store.getIdentity(added), 'Add should return new id'); | ||
assert.deepEqual(added, addObject, 'Object should be added to store without modifying properties'); | ||
var removed = getResults.remove; | ||
assert.isTrue(results.remove, 'Existing id should remove successfully.'); | ||
assert.isUndefined(removed, 'Removed id should no longer exist in store.'); | ||
}), dfd.reject.bind(dfd)); | ||
}, dfd.reject.bind(dfd)); | ||
var otherTransaction = store.transaction(); | ||
store.add(lang.clone(addObject)); | ||
store.put({failure: true}); | ||
otherTransaction.commit(); | ||
transaction.commit(); | ||
} | ||
}); | ||
registerSuite({ | ||
name: 'live', | ||
setup: function() { | ||
}, | ||
teardown: function() { | ||
}, | ||
military: function() { | ||
var store = new ArcGISServerStore({ | ||
url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/Military/FeatureServer/9' | ||
}); | ||
var transaction = store.transaction(); | ||
store.put({ | ||
objectid: 1410598, | ||
name: 'store transaction' | ||
}).then(function(result) { | ||
console.log('put 1', result); | ||
}, function(err) { | ||
console.log('put 1', err); | ||
}); | ||
store.put({ | ||
objectid: 1407804, | ||
ruleid: 2 | ||
}).then(function(result) { | ||
console.log('put 2', result); | ||
}, function(err) { | ||
console.log('put 2', err); | ||
}); | ||
store.add({ | ||
ruleid: 4994, | ||
name: 'store transaction' | ||
}).then(function(result) { | ||
console.log('add', result); | ||
}, function(err) { | ||
console.log('add', err); | ||
}); | ||
store.remove(1407805).then(function(result) { | ||
console.log('remove', result); | ||
}, function(err) { | ||
console.log('remove', err); | ||
}); | ||
transaction.abort(); | ||
} | ||
}); | ||
}); |
@@ -21,2 +21,42 @@ define([ | ||
) { | ||
var validateField = function(field, feature) { | ||
var value = lang.getObject('attributes.' + field.name, false, feature); | ||
if (value !== undefined) { | ||
switch (field.type) { | ||
case 'esriFieldTypeSmallInteger': | ||
if (isNaN(value) || value % 1 !== 0 || value < -32768 || value > 32767) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeInteger': | ||
if (isNaN(value) || value % 1 !== 0 || value < -2147483648 || value > 2147483647) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeDouble': | ||
if (isNaN(value) || value % 1 === 0 || value < -2.2 * Math.pow(10, 308) || value > 1.8 * Math.pow(10, 308)) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeDate': | ||
value = Date.parse(value); | ||
if (isNaN(value)) { | ||
throw new Error('Parser'); | ||
} else { | ||
value = new Date(value); | ||
} | ||
break; | ||
case 'esriFieldTypeString': | ||
if (typeof value !== 'string') { | ||
throw new Error('Parser'); | ||
} else if (value.length > field.length) { | ||
value = value.substr(0, field.length); | ||
} | ||
break; | ||
} | ||
} | ||
return value; | ||
}; | ||
var FeatureService = declare(null, { | ||
@@ -195,2 +235,6 @@ mocking: false, | ||
// Apply Edits | ||
var applyEdits = registry.register(/Mock\/FeatureServer\/[0-9]+\/applyEdits$/, lang.hitch(this, 'applyEdits')); | ||
this.handles.push(applyEdits); | ||
// Root Info / Unknown Endpoints | ||
@@ -367,44 +411,7 @@ var info = registry.register(/Mock\/FeatureServer\/[0-9]+.*$/, lang.hitch(this, 'info')); | ||
} | ||
var val = lang.getObject('attributes.' + field.name, false, feature); | ||
if (val !== undefined) { | ||
switch (field.type) { | ||
case 'esriFieldTypeSmallInteger': | ||
if (isNaN(val) || val % 1 !== 0 || val < -32768 || val > 32767) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeInteger': | ||
if (isNaN(val) || val % 1 !== 0 || val < -2147483648 || val > 2147483647) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeDouble': | ||
if (isNaN(val) || val % 1 === 0 || val < -2.2 * Math.pow(10, 308) || val > 1.8 * Math.pow(10, 308)) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeDate': | ||
val = Date.parse(val); | ||
if (isNaN(val)) { | ||
throw new Error('Parser'); | ||
} else { | ||
val = new Date(val); | ||
} | ||
break; | ||
case 'esriFieldTypeString': | ||
if (typeof val !== 'string') { | ||
throw new Error('Parser'); | ||
} else if (val.length > field.length) { | ||
val = val.substr(0, field.length); | ||
} | ||
break; | ||
default: | ||
add.attributes[field.name] = null; | ||
} | ||
add.attributes[field.name] = val; | ||
} else { | ||
add.attributes[field.name] = null; | ||
var value = validateField(field, feature); | ||
if (value === undefined) { | ||
value = null; | ||
} | ||
add.attributes[field.name] = value; | ||
}); | ||
@@ -474,39 +481,5 @@ | ||
} | ||
var val = lang.getObject('attributes.' + field.name, false, feature); | ||
if (val !== undefined) { | ||
switch (field.type) { | ||
case 'esriFieldTypeSmallInteger': | ||
if (isNaN(val) || val % 1 !== 0 || val < -32768 || val > 32767) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeInteger': | ||
if (isNaN(val) || val % 1 !== 0 || val < -2147483648 || val > 2147483647) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeDouble': | ||
if (isNaN(val) || val % 1 === 0 || val < -2.2 * Math.pow(10, 308) || val > 1.8 * Math.pow(10, 308)) { | ||
throw new Error('Parser'); | ||
} | ||
break; | ||
case 'esriFieldTypeDate': | ||
val = Date.parse(val); | ||
if (isNaN(val)) { | ||
throw new Error('Parser'); | ||
} else { | ||
val = new Date(val); | ||
} | ||
break; | ||
case 'esriFieldTypeString': | ||
if (typeof val !== 'string') { | ||
throw new Error('Parser'); | ||
} else if (val.length > field.length) { | ||
val = val.substr(0, field.length); | ||
} | ||
break; | ||
} | ||
update.attributes[field.name] = val; | ||
var value = validateField(field, feature); | ||
if (value !== undefined) { | ||
update.attributes[field.name] = value; | ||
} | ||
@@ -600,2 +573,160 @@ }); | ||
return when(dfd.promise); | ||
}, | ||
applyEdits: function(url, query) { | ||
var error, dfd = new Deferred(); | ||
if (array.indexOf(this.serviceDefinition.capabilities.split(','), 'Editing') !== -1) { | ||
try { | ||
query.adds = JSON.parse(query.adds || '[]'); | ||
query.updates = JSON.parse(query.updates || '[]'); | ||
query.deletes = (query.deletes || '').split(','); | ||
// Add | ||
if (query.adds.length) { | ||
if (array.indexOf(this.serviceDefinition.capabilities.split(','), 'Create') !== -1) { | ||
query.adds = array.map(query.adds, lang.hitch(this, function(feature) { | ||
var add = { | ||
attributes: {} | ||
}; | ||
// Validate fields | ||
try { | ||
array.forEach(this.serviceDefinition.fields, function(field) { | ||
if (field.type === 'esriFieldTypeOID' || field.type === 'esriFieldTypeGeometry') { | ||
return; | ||
} | ||
var value = validateField(field, feature); | ||
if (value === undefined) { | ||
value = null; | ||
} | ||
add.attributes[field.name] = value; | ||
}); | ||
} catch (e) { | ||
return; | ||
} | ||
// Validate geometry | ||
if (this.serviceDefinition.type === 'Feature Layer' && feature.geometry) { | ||
var geometry = geometryJsonUtils.fromJson(feature.geometry); | ||
if (geometry && geometryJsonUtils.getJsonType(geometry) === this.serviceDefinition.geometryType) { | ||
add.geometry = geometry; | ||
} else { | ||
throw new Error(); | ||
} | ||
} | ||
return add; | ||
})); | ||
query.adds = array.filter(query.adds, function(feature) { | ||
return feature; | ||
}); | ||
} else { | ||
throw new Error('Add not supported.'); | ||
} | ||
} | ||
// Update | ||
if (query.updates.length) { | ||
if (array.indexOf(this.serviceDefinition.capabilities.split(','), 'Update') !== -1) { | ||
query.updates = array.map(query.updates, lang.hitch(this, function(feature) { | ||
var objectId = lang.getObject('attributes.' + this.serviceDefinition.objectIdField, false, feature); | ||
var update = this.store.get(objectId); | ||
if (!objectId || !update) { | ||
throw new Error('Parser'); | ||
} | ||
// Validate fields | ||
array.forEach(this.serviceDefinition.fields, function(field) { | ||
if (field.type === 'esriFieldTypeOID' || field.type === 'esriFieldTypeGeometry') { | ||
return; | ||
} | ||
var value = validateField(field, feature); | ||
if (value !== undefined) { | ||
update.attributes[field.name] = value; | ||
} | ||
}); | ||
// Validate geometry | ||
if (this.serviceDefinition.type === 'Feature Layer' && this.serviceDefinition.allowGeometryUpdates && feature.geometry) { | ||
var geometry = geometryJsonUtils.fromJson(feature.geometry); | ||
if (geometry && geometryJsonUtils.getJsonType(geometry) === this.serviceDefinition.geometryType) { | ||
update.geometry = geometry; | ||
} else { | ||
throw new Error(); | ||
} | ||
} | ||
return update; | ||
})); | ||
} else { | ||
throw new Error('Update not supported.'); | ||
} | ||
} | ||
// Delete | ||
if (query.deletes.length) { | ||
if (array.indexOf(this.serviceDefinition.capabilities.split(','), 'Delete') !== -1) { | ||
query.deletes = array.map(query.deletes, lang.hitch(this, function(id) { | ||
return parseInt(id, 10); | ||
})); | ||
query.deletes = array.filter(query.deletes, lang.hitch(this, function(id) { | ||
return id && this.store.get(id); | ||
})); | ||
} else { | ||
throw new Error('Delete not supported.'); | ||
} | ||
} | ||
if (query.adds.length + query.updates.length + query.deletes.length) { | ||
var addResults = array.map(query.adds, lang.hitch(this, function(feature) { | ||
var id = this.store.add(feature); | ||
return { | ||
objectId: id, | ||
success: true | ||
}; | ||
})); | ||
var updateResults = array.map(query.updates, lang.hitch(this, function(feature) { | ||
var id = this.store.put(feature); | ||
return { | ||
objectId: id, | ||
success: true | ||
}; | ||
})); | ||
var deleteResults = array.map(query.deletes, lang.hitch(this, function(id) { | ||
this.store.remove(id); | ||
return { | ||
objectId: id, | ||
success: true | ||
}; | ||
})); | ||
dfd.resolve({ | ||
addResults: addResults, | ||
updateResults: updateResults, | ||
deleteResults: deleteResults | ||
}); | ||
} else { | ||
error = new Error('Unable to complete operation.'); | ||
error.code = 500; | ||
error.details = ['No edits (\'adds\', \'updates\', or \'deletes\') were specified.']; | ||
dfd.reject(error); | ||
} | ||
} catch (e) { | ||
error = new Error('Unable to complete operation.'); | ||
error.code = e.message ? 500 : 400; | ||
error.details = []; | ||
dfd.reject(error); | ||
} | ||
} else { | ||
error = new Error('Requested operation is not supported by this service.'); | ||
error.code = 400; | ||
error.details = []; | ||
dfd.reject(error); | ||
} | ||
return when(dfd.promise); | ||
} | ||
@@ -602,0 +733,0 @@ }); |
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 7 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
5563779
561
81498
170
2
90
5
69
47