From f1362c06dd58baf5c2b0687513426becc04a1f66 Mon Sep 17 00:00:00 2001 From: borzav Date: Tue, 25 Nov 2014 18:00:46 +0100 Subject: [PATCH 1/6] fix: indirectly attached entity without changes wont send empty update, better discover entity state change: ValidationErrors property on $data.Entity instance not added to changeProperties add: angular module toLiveArrayEx for new angular fix: unit test --- JayDataModules/angular.js | 36 +++++++++++++++++++++++++++++++++ Types/Entity.js | 2 +- Types/EntityContext.js | 7 +++++-- UnitTests/dataDistributeTest.js | 10 ++++----- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/JayDataModules/angular.js b/JayDataModules/angular.js index f071be9b..899e2ad9 100644 --- a/JayDataModules/angular.js +++ b/JayDataModules/angular.js @@ -44,6 +44,42 @@ angular.module('jaydata', ['ng', ['$provide', function ($provide) { return Object.prototype.hasOwnProperty.apply(this, arguments); } + + $data.Queryable.prototype.toLiveArrayEx = function (options, resultHolder) { + if (Array.isArray(options)) { + resultHolder = options; + otions = undefined; + } + resultHolder = resultHolder || []; + options = options || {}; + var self = this, scope = options.scope || $rootScope; + + function thunk(newDefer) { + self.toArray() + .then(function (items) { + resultHolder.length = 0; + items.forEach(function (item) { + resultHolder.push(item); + }) + newDefer.resolve(resultHolder); + }) + .fail(newDefer.reject) + .then(function () { + scope.$apply(); + }); + } + + function refresh() { + var defer = $.Deferred(thunk); + defer.promise(resultHolder); + return resultHolder; + } + resultHolder.refresh = refresh; + + return refresh(); + } + + $data.Queryable.prototype.toLiveArray = function (cb) { var _this = this; diff --git a/Types/Entity.js b/Types/Entity.js index fbd5865f..9389c618 100644 --- a/Types/Entity.js +++ b/Types/Entity.js @@ -339,7 +339,7 @@ $data.Entity = Entity = $data.Class.define("$data.Entity", null, null, { } }, _setPropertyChanged: function (memberDefinition) { - if (memberDefinition.monitorChanges != false) { + if (memberDefinition.monitorChanges != false && memberDefinition.name != "ValidationErrors") { if (!this.changedProperties) { this.changedProperties = []; } diff --git a/Types/EntityContext.js b/Types/EntityContext.js index 22b327f5..6123abee 100644 --- a/Types/EntityContext.js +++ b/Types/EntityContext.js @@ -1051,6 +1051,9 @@ $data.Class.define('$data.EntityContext', null, null, entityCachedItem.skipSave = true; skipItems.push(entityCachedItem.data); } + } else { + entityCachedItem.skipSave = true; + skipItems.push(entityCachedItem.data); } } } @@ -1424,9 +1427,9 @@ $data.Class.define('$data.EntityContext', null, null, }, discoverDependentItemEntityState: function (data) { if (data.storeToken === this.storeToken) { - data.entityState = $data.EntityState.Modified; + data.entityState = (data.changedProperties && data.changedProperties.length) ? $data.EntityState.Modified : $data.EntityState.Unchanged; } else if (data.storeToken && this.storeToken && data.storeToken.typeName === this.storeToken.typeName && JSON.stringify(data.storeToken.args) === JSON.stringify(this.storeToken.args)) { - data.entityState = $data.EntityState.Modified; + data.entityState = (data.changedProperties && data.changedProperties.length) ? $data.EntityState.Modified : $data.EntityState.Unchanged; } else { data.entityState = $data.EntityState.Added; } diff --git a/UnitTests/dataDistributeTest.js b/UnitTests/dataDistributeTest.js index eac73771..33519423 100644 --- a/UnitTests/dataDistributeTest.js +++ b/UnitTests/dataDistributeTest.js @@ -10,7 +10,7 @@ equal(data.some(function (e) { return e.entityState == $data.EntityState.Added }), true, "exists new element"); if(data.every(function (e) { return e.entityState == $data.EntityState.Added })){ - equal(data.length, 2, 'changed data count'); + equal(data.length, 2, 'changed Added data count'); } else { @@ -49,12 +49,12 @@ start(); equal(item.length, 2, "new items added"); - item[0].fld3 = "updateFld"; db.Table1Items.attach(item[0]); - item[0].entityState = $data.EntityState.Modified; - item[1].fld4 = "updateFld2"; + item[0].fld3 = "updateFld"; + db.Table1Items.attach(item[1]); - item[1].entityState = $data.EntityState.Modified; + item[1].fld4 = "updateFld2"; + db.Table1Items.add(new db.Table1Items.createNew({ fld2: 'prop32', fld3: 'prop33', fld4: 'prop34' })); db.saveChanges(function () { From da85f0bcab42fb144ff61a4ab69f808e6e1b7d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1z=C3=A1r=20Viktor?= Date: Fri, 12 Dec 2014 12:28:12 +0100 Subject: [PATCH 2/6] OData compiler remove trailing comma --- Types/StorageProviders/oData/oDataCompiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Types/StorageProviders/oData/oDataCompiler.js b/Types/StorageProviders/oData/oDataCompiler.js index 1fd3c2df..d65aee76 100644 --- a/Types/StorageProviders/oData/oDataCompiler.js +++ b/Types/StorageProviders/oData/oDataCompiler.js @@ -235,5 +235,5 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress headers: compiled.headers }); } - }, + } }, {}); \ No newline at end of file From e15f970cfe90b6ea3f1fe87b41713bf6452e2856 Mon Sep 17 00:00:00 2001 From: borzav Date: Fri, 12 Dec 2014 19:28:55 +0100 Subject: [PATCH 3/6] fix: batch execute query error on query result fix: batch execute query invalid batch response count add: unit tests --- Types/EntityContext.js | 6 +- Types/StorageProviders/oData/oDataProvider.js | 4 + UnitTests/T4.js | 95 +++++++++++++++---- 3 files changed, 83 insertions(+), 22 deletions(-) diff --git a/Types/EntityContext.js b/Types/EntityContext.js index 6123abee..2949d6c3 100644 --- a/Types/EntityContext.js +++ b/Types/EntityContext.js @@ -945,19 +945,23 @@ $data.Class.define('$data.EntityContext', null, null, success: function (results) { var batchResult = []; var hasError = false; + var errorValue = null; for (var i = 0; i < results.length && !hasError; i++) { var query = results[i]; self.executeQuerySuccess(self, returnTransaction, { success: function (result) { batchResult.push(result); }, - error: function () { + error: function (err) { hasError = true; + errorValue = err; } })(query); } if (!hasError) { self._applyTransaction(cbWrapper, cbWrapper.success, [batchResult], batchExecuteQuery.transaction, returnTransaction); + } else { + cbWrapper.error(errorValue); } }, diff --git a/Types/StorageProviders/oData/oDataProvider.js b/Types/StorageProviders/oData/oDataProvider.js index 46de0f9d..881de807 100644 --- a/Types/StorageProviders/oData/oDataProvider.js +++ b/Types/StorageProviders/oData/oDataProvider.js @@ -194,6 +194,10 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null } if (sql.isBatchExecuteQuery) { + if (data.__batchResponses.length !== sql.subQueries.length) { + return callBack.error(new Exception("Batch response count failed", "Http request failed!", textStatus)); + } + query.rawDataList = sql.subQueries; for (var i = 0; i < data.__batchResponses.length; i++) { var resp = data.__batchResponses[i]; diff --git a/UnitTests/T4.js b/UnitTests/T4.js index 4d3dd6de..dea4151e 100644 --- a/UnitTests/T4.js +++ b/UnitTests/T4.js @@ -889,32 +889,85 @@ function BatchExecuteQueryTests(providerConfig, msg) { }); }); - //test("toLiveArray", 5, function () { - // stop(); + test('batch req error test', 1, function () { + if (providerConfig.name !== 'oData') { expect(1); ok(true, 'not supported'); return; } + stop(); - // (new $news.Types.NewsContext(providerConfig)).onReady(function (context) { - // $news.Types.NewsContext.generateTestData(context, function () { + (new $news.Types.NewsContext(providerConfig)).onReady(function (context) { + $news.Types.NewsContext.generateTestData(context, function () { + context.prepareRequest = function (request) { + request[0].data.__batchRequests[2].requestUri = request[0].data.__batchRequests[2].requestUri.replace('Title', 'Title2'); + }; - // var result = context.Articles.toLiveArray(); - // result.then(function (r) { - // equal(result.length, 26, 'result is not empty'); - // ok(result === r, 'result is equal with promise result'); + context.batchExecuteQuery([ + [context.Articles], + { queryable: context.Articles.take(5) }, + { queryable: context.Categories.filter("it.Title != null"), method: "first" }, + [context.Categories, "count"] + ]) + .fail(function (err) { + ok(true, 'Request error'); + }) + .always(function (err) { - // result.length = 5; - // equal(result.length, 5, 'result is changed'); + _finishCb(context); + }); + }); + }); + }); - // result.refresh(); - // result.then(function () { - // equal(result.length, 26, 'result is not empty 2'); - // }).then(function () { - // equal(result.length, 26, 'result is not empty 3'); + test('batch result error test', 1, function () { + if (providerConfig.name !== 'oData') { expect(1); ok(true, 'not supported'); return; } + stop(); - // _finishCb(context); - // }); - // }); - // }); - // }); - //}); + (new $news.Types.NewsContext(providerConfig)).onReady(function (context) { + $news.Types.NewsContext.generateTestData(context, function () { + context.prepareRequest = function (request) { + var origSuccess = request[1]; + request[1] = function (data) { + data.__batchResponses = []; + origSuccess.apply(this, arguments); + } + }; + + context.batchExecuteQuery([ + [context.Articles], + { queryable: context.Articles.take(5) }, + { queryable: context.Categories, method: "first" }, + [context.Categories, "count"] + ]) + .fail(function (err) { + equal(err.message, "Batch response count failed", 'Request error'); + }) + .always(function (err) { + + _finishCb(context); + }); + }); + }); + }); + + test('batch result item error test', 1, function () { + stop(); + + (new $news.Types.NewsContext(providerConfig)).onReady(function (context) { + $news.Types.NewsContext.generateTestData(context, function () { + context.batchExecuteQuery([ + [context.Articles], + { queryable: context.Articles.take(5) }, + { queryable: context.Categories.filter("it.Title == 'invalid category value'"), method: "single" }, + [context.Categories, "count"] + ]) + .fail(function (err) { + equal(err.message, "result count failed", 'Result error'); + }) + .always(function (err) { + + _finishCb(context); + }); + }); + }); + }); } From 552abcfa018b7e3e3c66af1a9ce593ee41516bb9 Mon Sep 17 00:00:00 2001 From: borzav Date: Thu, 18 Dec 2014 16:05:19 +0100 Subject: [PATCH 4/6] ADD: OData V4 alpha ADD: withCount alias for withInlineCount on Queryable --- JaySvcUtil/JaySvcUtil.js | 11 +- Types/Queryable.js | 3 + .../modelBinderConfigCompiler.js | 2 +- Types/StorageProviders/oData/oDataCompiler.js | 8 +- Types/StorageProviders/oData/oDataProvider.js | 119 +++++++++++++----- UnitTests/oDataProviderTests.js | 84 ++++++------- 6 files changed, 146 insertions(+), 81 deletions(-) diff --git a/JaySvcUtil/JaySvcUtil.js b/JaySvcUtil/JaySvcUtil.js index a441aeef..205fa99a 100644 --- a/JaySvcUtil/JaySvcUtil.js +++ b/JaySvcUtil/JaySvcUtil.js @@ -612,7 +612,8 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { "http://schemas.microsoft.com/ado/2008/09/edm": "V2", "http://schemas.microsoft.com/ado/2009/11/edm": "V3", "http://schemas.microsoft.com/ado/2007/05/edm": "V11", - "http://schemas.microsoft.com/ado/2009/08/edm": "V22" + "http://schemas.microsoft.com/ado/2009/08/edm": "V22", + "http://docs.oasis-open.org/odata/ns/edm": "V4" } }, _maxDataServiceVersions: { @@ -621,7 +622,8 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { "http://schemas.microsoft.com/ado/2008/09/edm": "2.0", "http://schemas.microsoft.com/ado/2009/11/edm": "3.0", "http://schemas.microsoft.com/ado/2007/05/edm": "2.0", - "http://schemas.microsoft.com/ado/2009/08/edm": "2.0" + "http://schemas.microsoft.com/ado/2009/08/edm": "2.0", + "http://docs.oasis-open.org/odata/ns/edm": "4.0" } }, _supportedODataVersionXSLT: { @@ -1109,6 +1111,7 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { " \r\n" + " true\r\n" + " \r\n" + + " '$$unbound'\r\n" + " '': { '$':\r\n" + " , \r\n" + " \r\n" + @@ -1121,6 +1124,10 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { " \r\n" + " \r\n" + "\r\n" + + " \r\n" + + " ''\r\n" + + " \r\n" + + "\r\n" + " \r\n" + " \r\n" + " \r\n" + diff --git a/Types/Queryable.js b/Types/Queryable.js index bbe3a93e..7e036b6b 100644 --- a/Types/Queryable.js +++ b/Types/Queryable.js @@ -760,6 +760,9 @@ $data.Class.define('$data.Queryable', null, null, var inlineCountExp = Container.createInlineCountExpression(this.expression, constExp); return Container.createQueryable(this, inlineCountExp); }, + withCount: function (selector) { + return this.withInlineCount(selector); + }, removeAll: function (onResult, transaction) { /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. diff --git a/Types/StorageProviders/modelBinderConfigCompiler.js b/Types/StorageProviders/modelBinderConfigCompiler.js index 41b61b88..429ef361 100644 --- a/Types/StorageProviders/modelBinderConfigCompiler.js +++ b/Types/StorageProviders/modelBinderConfigCompiler.js @@ -93,7 +93,7 @@ $C('$data.modelBinder.ModelBinderConfigCompiler', $data.Expressions.EntityExpres var builder = Container.createqueryBuilder(); builder.modelBinderConfig['$type'] = $data.Array; if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:d.results', 'json:d', 'json:results']; + builder.modelBinderConfig['$selector'] = ['json:d.results', 'json:d', 'json:results', 'json:value']; } builder.modelBinderConfig['$item'] = {}; builder.selectModelBinderProperty('$item'); diff --git a/Types/StorageProviders/oData/oDataCompiler.js b/Types/StorageProviders/oData/oDataCompiler.js index d65aee76..0060421c 100644 --- a/Types/StorageProviders/oData/oDataCompiler.js +++ b/Types/StorageProviders/oData/oDataCompiler.js @@ -43,7 +43,7 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress query.postData = queryFragments.postData; var result = { queryText: queryText, - withInlineCount: '$inlinecount' in queryFragments, + withInlineCount: '$inlinecount' in queryFragments || '$count' in queryFragments, method: queryFragments.method || 'GET', postData: queryFragments.postData, isBatchExecuteQuery: queryFragments._isBatchExecuteQuery, @@ -144,7 +144,11 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress }, VisitInlineCountExpression: function (expression, context) { this.Visit(expression.source, context); - context["$inlinecount"] = expression.selector.value; + if (this.provider.providerConfiguration.maxDataServiceVersion === "4.0") { + context["$count"] = expression.selector.value === 'allpages'; + } else { + context["$inlinecount"] = expression.selector.value; + } }, VisitEntitySetExpression: function (expression, context) { context.urlText += "/" + expression.instance.tableName; diff --git a/Types/StorageProviders/oData/oDataProvider.js b/Types/StorageProviders/oData/oDataProvider.js index 881de807..612bb0e3 100644 --- a/Types/StorageProviders/oData/oDataProvider.js +++ b/Types/StorageProviders/oData/oDataProvider.js @@ -1,6 +1,6 @@ var datajsPatch; -datajsPatch = function () { +datajsPatch = function (OData) { // just datajs-1.1.0 if (OData && OData.jsonHandler && 'useJsonLight' in OData.jsonHandler && typeof datajs === 'object' && !datajs.version) { $data.Trace.log('!!!!!!! - patch datajs 1.1.0'); @@ -25,11 +25,6 @@ datajsPatch = function () { $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null, { constructor: function (cfg, ctx) { - if (typeof OData === 'undefined') { - Guard.raise(new Exception('datajs is required', 'Not Found!')); - } - datajsPatch(); - this.SqlCommands = []; this.context = ctx; this.providerConfiguration = $data.typeSystem.extend({ @@ -48,6 +43,21 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null UpdateMethod: 'PATCH' }, cfg); + if (this.providerConfiguration.maxDataServiceVersion === "4.0") { + if (typeof odatajs === 'undefined' || typeof odatajs.oData === 'undefined') { + Guard.raise(new Exception('odatajs is required', 'Not Found!')); + } else { + this.oData = odatajs.oData + } + } else { + if (typeof OData === 'undefined') { + Guard.raise(new Exception('datajs is required', 'Not Found!')); + } else { + this.oData = OData; + datajsPatch(this.oData); + } + } + this.fixkDataServiceVersions(cfg); if (this.context && this.context._buildDbType_generateConvertToFunction && this.buildDbType_generateConvertToFunction) { @@ -94,7 +104,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null //} this.context.prepareRequest.call(this, requestData); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); } else { callBack.success(that.context); } @@ -174,30 +184,31 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null var schema = this.context; var that = this; + var countProperty = "__count"; + if (this.providerConfiguration.maxDataServiceVersion === "4.0") { + countProperty = "@odata.count"; + } + var requestData = [ { requestUri: this.providerConfiguration.oDataServiceHost + sql.queryText, method: sql.method, data: sql.postData, headers: { - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion } }, function (data, textStatus, jqXHR) { + if (!data && textStatus.body && !sql.isBatchExecuteQuery) data = JSON.parse(textStatus.body); if (callBack.success) { var processSuccess = function (query, data, sql) { query.rawDataList = typeof data === 'string' ? [{ cnt: Container.convertTo(data, $data.Integer) }] : data; - if (sql.withInlineCount && typeof data === 'object' && (typeof data.__count !== 'undefined' || ('d' in data && typeof data.d.__count !== 'undefined'))) { - query.__count = new Number(typeof data.__count !== 'undefined' ? data.__count : data.d.__count).valueOf(); + if (sql.withInlineCount && typeof data === 'object' && (typeof data[countProperty] !== 'undefined' || ('d' in data && typeof data.d[countProperty] !== 'undefined'))) { + query.__count = new Number(typeof data[countProperty] !== 'undefined' ? data[countProperty] : data.d[countProperty]).valueOf(); } } if (sql.isBatchExecuteQuery) { - if (data.__batchResponses.length !== sql.subQueries.length) { - return callBack.error(new Exception("Batch response count failed", "Http request failed!", textStatus)); - } - query.rawDataList = sql.subQueries; for (var i = 0; i < data.__batchResponses.length; i++) { var resp = data.__batchResponses[i]; @@ -223,10 +234,14 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null function (error) { callBack.error(that.parseError(error, arguments)); }, - sql.isBatchExecuteQuery ? OData.batchHandler : undefined + sql.isBatchExecuteQuery ? this.oData.batchHandler : undefined ]; - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { + requestData[0].headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { requestData[0].headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } @@ -246,7 +261,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null this.context.prepareRequest.call(this, requestData); //$data.ajax(requestData); //OData.request(requestData, requestData.success, requestData.error); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); }, _compile: function (queryable, params) { var compiler = new $data.storageProviders.oData.oDataCompiler(); @@ -285,10 +300,12 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null request = { requestUri: this.providerConfiguration.oDataServiceHost + '/', headers: { - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion } }; - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { + request.headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { request.headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } if (typeof this.providerConfiguration.useJsonLight !== 'undefined') { @@ -355,7 +372,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null //} this.context.prepareRequest.call(this, requestData); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); }, _saveBatch: function (independentBlocks, index2, callBack) { var batchRequests = []; @@ -365,9 +382,11 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null convertedItem.push(independentBlocks[index][i].data); var request = {}; request.headers = { - "Content-Id": convertedItem.length, - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion + "Content-Id": convertedItem.length }; + if (this.providerConfiguration.maxDataServiceVersion != "4.0") { + request.headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } switch (independentBlocks[index][i].data.entityState) { case $data.EntityState.Unchanged: continue; break; case $data.EntityState.Added: @@ -391,7 +410,10 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null default: Guard.raise(new Exception("Not supported Entity state")); } - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { + request.headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { request.headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } batchRequests.push(request); @@ -406,7 +428,6 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null __batchRequests: [{ __changeRequests: batchRequests }] }, headers: { - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion } }, function (data, response) { if (response.statusCode == 202) { @@ -446,9 +467,12 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null }, function (e) { callBack.error(that.parseError(e)); - }, OData.batchHandler]; + }, this.oData.batchHandler]; - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion != "4.0") { + requestData[0].headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion != "4.0") { requestData[0].headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } if (typeof this.providerConfiguration.useJsonLight !== 'undefined') { @@ -462,7 +486,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null //} this.context.prepareRequest.call(this, requestData); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); }, reload_fromResponse: function (item, data, response) { var that = this; @@ -510,16 +534,43 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null }, this); }, - save_getInitData: function (item, convertedItems) { + //save_getInitData: function (item, convertedItems) { + // var self = this; + // item.physicalData = this.context._storageModel.getStorageModel(item.data.getType()).PhysicalType.convertTo(item.data, convertedItems); + // var serializableObject = {} + // item.physicalData.getType().memberDefinitions.asArray().forEach(function (memdef) { + // if (memdef.kind == $data.MemberTypes.navProperty || memdef.kind == $data.MemberTypes.complexProperty || (memdef.kind == $data.MemberTypes.property && !memdef.notMapped)) { + // if (typeof memdef.concurrencyMode === 'undefined' && (memdef.key === true || item.data.entityState === $data.EntityState.Added || item.data.changedProperties.some(function (def) { return def.name === memdef.name; }))) { + // var typeName = Container.resolveName(memdef.type); + // var converter = self.fieldConverter.toDb[typeName]; + // serializableObject[memdef.name] = converter ? converter(item.physicalData[memdef.name]) : item.physicalData[memdef.name]; + // } + // } + // }, this); + // return serializableObject; + //}, + save_getInitData: function (item, convertedItems, isComplex, isDeep) { var self = this; - item.physicalData = this.context._storageModel.getStorageModel(item.data.getType()).PhysicalType.convertTo(item.data, convertedItems); + if (!isComplex) { + item.physicalData = this.context._storageModel.getStorageModel(item.data.getType()).PhysicalType.convertTo(item.data, convertedItems); + } else { + item.physicalData = item.data; + } var serializableObject = {} item.physicalData.getType().memberDefinitions.asArray().forEach(function (memdef) { - if (memdef.kind == $data.MemberTypes.navProperty || memdef.kind == $data.MemberTypes.complexProperty || (memdef.kind == $data.MemberTypes.property && !memdef.notMapped)) { - if (typeof memdef.concurrencyMode === 'undefined' && (memdef.key === true || item.data.entityState === $data.EntityState.Added || item.data.changedProperties.some(function (def) { return def.name === memdef.name; }))) { - var typeName = Container.resolveName(memdef.type); - var converter = self.fieldConverter.toDb[typeName]; - serializableObject[memdef.name] = converter ? converter(item.physicalData[memdef.name]) : item.physicalData[memdef.name]; + if (memdef.kind == $data.MemberTypes.complexProperty && item.physicalData[memdef.name]) { + serializableObject[memdef.name] = self.save_getInitData({ data: item.physicalData[memdef.name] }, convertedItems, true, true); + } + else if (memdef.kind == $data.MemberTypes.navProperty || (memdef.kind == $data.MemberTypes.property && !memdef.notMapped)) { + if (isDeep || typeof memdef.concurrencyMode === 'undefined' && (memdef.key === true || item.data.entityState === $data.EntityState.Added || (item.data.changedProperties && item.data.changedProperties.some(function (def) { return def.name === memdef.name; })))) { + + if (memdef.kind == $data.MemberTypes.navProperty && item.physicalData[memdef.name] && this.providerConfiguration.maxDataServiceVersion === "4.0") { + serializableObject[memdef.name + "@odata.bind"] = item.physicalData[memdef.name].__metadata.uri; + } else { + var typeName = Container.resolveName(memdef.type); + var converter = self.fieldConverter.toDb[typeName]; + serializableObject[memdef.name] = converter ? converter(item.physicalData[memdef.name]) : item.physicalData[memdef.name]; + } } } }, this); diff --git a/UnitTests/oDataProviderTests.js b/UnitTests/oDataProviderTests.js index a29628dd..828bf3a1 100644 --- a/UnitTests/oDataProviderTests.js +++ b/UnitTests/oDataProviderTests.js @@ -21,7 +21,7 @@ equal(q.queryText, "/Users", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -42,7 +42,7 @@ equal(q.queryText, "/Users?$orderby=LoginName", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -64,7 +64,7 @@ equal(q.queryText, "/Users?$orderby=Email,Id,LoginName", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -86,7 +86,7 @@ equal(q.queryText, "/Users?$orderby=Email desc,Id desc,LoginName desc", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -108,7 +108,7 @@ equal(q.queryText, "/Users?$orderby=Email,Id desc,LoginName", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -130,7 +130,7 @@ equal(q.queryText, "/Users?$orderby=Email desc,Id,LoginName desc", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -151,7 +151,7 @@ equal(q.queryText, "/Users?$top=10", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -171,7 +171,7 @@ equal(q.queryText, "/Users?$skip=11", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -191,7 +191,7 @@ equal(q.queryText, "/Users?$skip=11&$top=10", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -212,7 +212,7 @@ equal(q.queryText, "/Users?$filter=(LoginName eq 'alma')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -232,7 +232,7 @@ equal(q.queryText, "/Users?$filter=(LoginName eq 'alma')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -252,7 +252,7 @@ equal(q.queryText, "/Users?$filter=(Id eq 15)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -272,7 +272,7 @@ equal(q.queryText, "/Users?$filter=(Id eq 15)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -302,7 +302,7 @@ equal(q.queryText, "/Articles?$filter=(CreateDate gt datetime'" + dateString + "')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -327,7 +327,7 @@ equal(q.queryText, "/TestTable?$filter=(n0 gt 4.5)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -351,7 +351,7 @@ equal(q.queryText, "/TestTable?$filter=(n0 gt 4.5)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -375,7 +375,7 @@ equal(q.queryText, "/TestTable?$filter=(b0 eq true)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -399,7 +399,7 @@ equal(q.queryText, "/TestTable?$filter=(b0 eq true)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -423,7 +423,7 @@ equal(q.queryText, "/Users?$filter=((LoginName eq 'alma') and (Email eq 'email@company.com'))", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -444,7 +444,7 @@ equal(q.queryText, "/Articles?$filter=((Title eq 'alma') and (CreateDate eq datetime'" + dateString + "'))", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -473,7 +473,7 @@ equal(q.queryText, "/Articles?$filter=((Title eq 'alma') and (CreateDate eq datetime'" + dateString + "'))&$orderby=Title", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -500,7 +500,7 @@ equal(q.queryText, "/Users?$filter=substringof('aj',LoginName)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -520,7 +520,7 @@ equal(q.queryText, "/Users?$filter=startswith(LoginName,'aj')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -540,7 +540,7 @@ equal(q.queryText, "/Users?$filter=endswith(LoginName,'aj')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -562,7 +562,7 @@ equal(q.queryText, "/Articles?$expand=Category", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -594,7 +594,7 @@ equal(q.queryText, "/Articles?$expand=Category,Author", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -634,7 +634,7 @@ equal(q.queryText, "/Articles?$expand=Category,Author/Profile", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -693,7 +693,7 @@ equal(q.queryText, "/Categories?$expand=Articles,Articles/Author", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Category, $keys: ['Id'], @@ -738,7 +738,7 @@ equal(q.queryText, "/Articles?$filter=(Category/Title eq 'Sport')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -764,7 +764,7 @@ equal(q.queryText, "/Articles?$filter=(Author/Profile/Bio eq 'Bio1')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -789,7 +789,7 @@ equal(q.queryText, "/Users?$filter=(Profile/Bio eq 'Bio2')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -820,7 +820,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Integer, $source: 'Id' @@ -838,7 +838,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $type: $data.String, @@ -874,7 +874,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $source: 'Title' }, @@ -893,7 +893,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -912,7 +912,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -940,7 +940,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -972,7 +972,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -1008,7 +1008,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { @@ -1033,7 +1033,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { @@ -1070,7 +1070,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { @@ -1125,7 +1125,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.UserProfile, $selector: ['json:Reviewer.Profile.results', 'json:Reviewer.Profile'], From 712d4d4dc37ffdbe4f8b9822b3bbd07d471715f6 Mon Sep 17 00:00:00 2001 From: kildem Date: Thu, 26 Feb 2015 16:59:01 +0100 Subject: [PATCH 5/6] fix compatibility with node v0.12 --- JaySvcUtil/JaySvcUtil.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/JaySvcUtil/JaySvcUtil.js b/JaySvcUtil/JaySvcUtil.js index 205fa99a..6e605df4 100644 --- a/JaySvcUtil/JaySvcUtil.js +++ b/JaySvcUtil/JaySvcUtil.js @@ -275,21 +275,26 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { return resultDocument.textContent; } else if (typeof module !== 'undefined' && typeof require !== 'undefined') { - var xslt = require('node_xslt'); - - return xslt.transform(xslt.readXsltString(transformXslt), xslt.readXmlString(metadata), [ - 'SerivceUri', "'" + cnf.SerivceUri + "'", - 'EntityBaseClass', "'" + cnf.EntityBaseClass + "'", - 'ContextBaseClass', "'" + cnf.ContextBaseClass + "'", - 'AutoCreateContext', "'" + cnf.AutoCreateContext + "'", - 'ContextInstanceName', "'" + cnf.ContextInstanceName + "'", - 'EntitySetBaseClass', "'" + cnf.EntitySetBaseClass + "'", - 'CollectionBaseClass', "'" + cnf.CollectionBaseClass + "'", - 'DefaultNamespace', "'" + cnf.DefaultNamespace + "'", - 'MaxDataserviceVersion', "'" + (versionInfo.maxVersion || '3.0') + "'", - 'AllowedTypesList', "'" + cnf.typeFilter + "'", - 'GenerateNavigationProperties', "'" + cnf.navigation + "'" - ]); + var xslt4node = require('xslt4node'); + var config = { + xslt: transformXslt, + source: metadata, + result: String, + params: { + 'SerivceUri': cnf.SerivceUri, + 'EntityBaseClass': cnf.EntityBaseClass , + 'ContextBaseClass': cnf.ContextBaseClass , + 'AutoCreateContext': cnf.AutoCreateContext , + 'ContextInstanceName': cnf.ContextInstanceName , + 'EntitySetBaseClass': cnf.EntitySetBaseClass , + 'CollectionBaseClass': cnf.CollectionBaseClass , + 'DefaultNamespace': cnf.DefaultNamespace , + 'MaxDataserviceVersion': (versionInfo.maxVersion || '3.0') , + 'AllowedTypesList': cnf.typeFilter , + 'GenerateNavigationProperties': cnf.navigation + } + }; + return xslt4node.transformSync(config); } }, _prepareTypeFilter: function (doc, versionInfo, cnf) { From 9e5c56480e5a4a0daee41c6c5eeb73092c6efb12 Mon Sep 17 00:00:00 2001 From: sheam Date: Fri, 29 May 2015 10:41:09 -0600 Subject: [PATCH 6/6] Allow insight into the exception thrown by the odata controller. --- Types/StorageProviders/oData/oDataProvider.js | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/Types/StorageProviders/oData/oDataProvider.js b/Types/StorageProviders/oData/oDataProvider.js index 612bb0e3..e98d99bd 100644 --- a/Types/StorageProviders/oData/oDataProvider.js +++ b/Types/StorageProviders/oData/oDataProvider.js @@ -947,20 +947,37 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null } } */ - parseError: function(error, data){ - + parseError: function(error, data) + { var message = (error.response || error || {}).body || ''; - try { - if(message.indexOf('{') === 0){ + try + { + if (message.indexOf('{') === 0) + { var errorObj = JSON.parse(message); - errorObj = errorObj['odata.error'] || errorObj.error || errorObj; - if (errorObj.message) { + if (errorObj['odata.error']) + { + errorObj = errorObj['odata.error'] + if(errorObj.innererror) + { + errorObj = errorObj.innererror; + } + } + else + { + errorObj = errorObj.error || errorObj; + } + if (errorObj.message) + { message = errorObj.message.value || errorObj.message; } } - } catch (e) {} + } + catch (e) + { + } - return new Exception(message, error.message, data || error); + return new Exception(message, error.message, data || errorObj); }, appendBasicAuth: function (request, user, password, withCredentials) { request.headers = request.headers || {};