From 41f2089f959e3da3226b97019966cb72fd9e8ffe Mon Sep 17 00:00:00 2001 From: pangratz Date: Tue, 22 Dec 2015 20:36:14 +0100 Subject: [PATCH] [FEATURE ds-improved-ajax] Finer control over customizing a request Though `ajax()` (and `ajaxOptions()`) of `DS.RESTAdapter` are marked as private, they have been overwritten in many applications, since there is currently no other way to customize the request. This feature adds new public methods to `DS.RESTAdapter`, which allow to customize the properties of a request: - `methodForRequest` to get the HTTP verb - `urlForRequest` to get the URL - `headersForRequest` to get the headers - `dataForRequest` to get the data (query params or request body) The `params` hash passed to those methods has all the properties with which the corresponding `find`, `createRecord`, `findQuery`, ... call have been invoked: store, type, snapshot(s), id(s) and query. The `requestType` property indicates which method is requested; the possible values are: - `createRecord` - `updateRecord` - `deleteRecord` - `query` - `queryRecord` - `findRecord` - `findAll` - `findMany` - `findHasMany` - `findBelongsTo` Performing the actual AJAX request is handled by the `_makeRequest` method, which is similar to the existing `ajax` method: it makes the request using `jQuery.ajax` and attaches success and failure handlers. --- Say your API handles creation of resources via PUT, this can now be customized as follows: ``` js // adapters/application.js import DS from 'ember-data'; export DS.RESTAdapter.extend({ methodForRequest: function(params) { if (params.requestType === 'createRecord') { return "PUT"; } return this._super(...arguments); } }); ``` --- FEATURES.md | 6 + addon/adapters/json-api.js | 85 +++- addon/adapters/rest.js | 425 ++++++++++++++++-- config/features.json | 1 + .../adapter/build-url-mixin-test.js | 19 +- .../adapter/json-api-adapter-test.js | 27 +- .../integration/adapter/rest-adapter-test.js | 24 +- tests/integration/store-test.js | 20 +- tests/unit/adapters/rest-adapter/ajax-test.js | 40 +- .../group-records-for-find-many-test.js | 53 ++- tests/unit/model/rollback-attributes-test.js | 101 +++-- 11 files changed, 673 insertions(+), 128 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index d9129ee65ba..b929d18f8ea 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -23,6 +23,12 @@ entry in `config/features.json`. `store.findRecord()` and `store.findAll()` as described in [RFC 99](https://github.com/emberjs/rfcs/pull/99) +- `ds-improved-ajax` + + This feature allows to customize how a request is formed by overwriting + `methodForRequest`, `urlForRequest`, `headersForRequest` and `bodyForRequest` + in the `DS.RESTAdapter`. + - `ds-references` Adds references as described in [RFC 57](https://github.com/emberjs/rfcs/pull/57) diff --git a/addon/adapters/json-api.js b/addon/adapters/json-api.js index 97923564492..7fd59392376 100644 --- a/addon/adapters/json-api.js +++ b/addon/adapters/json-api.js @@ -4,6 +4,7 @@ import Ember from 'ember'; import RESTAdapter from "ember-data/adapters/rest"; +import isEnabled from 'ember-data/-private/features'; /** @class JSONAPIAdapter @@ -11,7 +12,7 @@ import RESTAdapter from "ember-data/adapters/rest"; @namespace DS @extends DS.RESTAdapter */ -export default RESTAdapter.extend({ +var JSONAPIAdapter = RESTAdapter.extend({ defaultSerializer: '-json-api', /** @@ -98,8 +99,12 @@ export default RESTAdapter.extend({ @return {Promise} promise */ findMany(store, type, ids, snapshots) { - var url = this.buildURL(type.modelName, ids, snapshots, 'findMany'); - return this.ajax(url, 'GET', { data: { filter: { id: ids.join(',') } } }); + if (isEnabled('ds-improved-ajax')) { + return this._super(...arguments); + } else { + var url = this.buildURL(type.modelName, ids, snapshots, 'findMany'); + return this.ajax(url, 'GET', { data: { filter: { id: ids.join(',') } } }); + } }, /** @@ -121,14 +126,76 @@ export default RESTAdapter.extend({ @return {Promise} promise */ updateRecord(store, type, snapshot) { - var data = {}; - var serializer = store.serializerFor(type.modelName); + if (isEnabled('ds-improved-ajax')) { + return this._super(...arguments); + } else { + var data = {}; + var serializer = store.serializerFor(type.modelName); - serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); + serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); - var id = snapshot.id; - var url = this.buildURL(type.modelName, id, snapshot, 'updateRecord'); + var id = snapshot.id; + var url = this.buildURL(type.modelName, id, snapshot, 'updateRecord'); - return this.ajax(url, 'PATCH', { data: data }); + return this.ajax(url, 'PATCH', { data: data }); + } } }); + +if (isEnabled('ds-improved-ajax')) { + + JSONAPIAdapter.reopen({ + + methodForRequest(params) { + if (params.requestType === 'updateRecord') { + return 'PATCH'; + } + + return this._super(...arguments); + }, + + dataForRequest(params) { + const { requestType, ids } = params; + + if (requestType === 'findMany') { + return { + filter: { id: ids.join(',') } + }; + } + + if (requestType === 'updateRecord') { + const { store, type, snapshot } = params; + const data = {}; + const serializer = store.serializerFor(type.modelName); + + serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); + + return data; + } + + return this._super(...arguments); + }, + + headersForRequest() { + const headers = this._super(...arguments) || {}; + + headers['Accept'] = 'application/vnd.api+json'; + + return headers; + }, + + _requestToJQueryAjaxHash() { + const hash = this._super(...arguments); + + if (hash.contentType) { + hash.contentType = 'application/vnd.api+json'; + } + + return hash; + } + + }); + +} + +export default JSONAPIAdapter; diff --git a/addon/adapters/rest.js b/addon/adapters/rest.js index 7cded79adb2..438b33aa94b 100644 --- a/addon/adapters/rest.js +++ b/addon/adapters/rest.js @@ -193,7 +193,7 @@ const { @extends DS.Adapter @uses DS.BuildURLMixin */ -export default Adapter.extend(BuildURLMixin, { +var RESTAdapter = Adapter.extend(BuildURLMixin, { defaultSerializer: '-rest', /** @@ -375,10 +375,19 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ findRecord(store, type, id, snapshot) { - const url = this.buildURL(type.modelName, id, snapshot, 'findRecord'); - const query = this.buildQuery(snapshot); + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, id, snapshot, + requestType: 'findRecord' + }); + + return this._makeRequest(request); + } else { + const url = this.buildURL(type.modelName, id, snapshot, 'findRecord'); + const query = this.buildQuery(snapshot); - return this.ajax(url, 'GET', { data: query }); + return this.ajax(url, 'GET', { data: query }); + } }, /** @@ -396,14 +405,25 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ findAll(store, type, sinceToken, snapshotRecordArray) { - const url = this.buildURL(type.modelName, null, null, 'findAll'); const query = this.buildQuery(snapshotRecordArray); - if (sinceToken) { - query.since = sinceToken; - } + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, sinceToken, query, + snapshots: snapshotRecordArray, + requestType: 'findAll' + }); + + return this._makeRequest(request); + } else { + const url = this.buildURL(type.modelName, null, null, 'findAll'); + + if (sinceToken) { + query.since = sinceToken; + } - return this.ajax(url, 'GET', { data: query }); + return this.ajax(url, 'GET', { data: query }); + } }, /** @@ -424,13 +444,22 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ query(store, type, query) { - var url = this.buildURL(type.modelName, null, null, 'query', query); + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, query, + requestType: 'query' + }); - if (this.sortQueryParams) { - query = this.sortQueryParams(query); - } + return this._makeRequest(request); + } else { + var url = this.buildURL(type.modelName, null, null, 'query', query); - return this.ajax(url, 'GET', { data: query }); + if (this.sortQueryParams) { + query = this.sortQueryParams(query); + } + + return this.ajax(url, 'GET', { data: query }); + } }, /** @@ -451,13 +480,22 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ queryRecord(store, type, query) { - var url = this.buildURL(type.modelName, null, null, 'queryRecord', query); + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, query, + requestType: 'queryRecord' + }); - if (this.sortQueryParams) { - query = this.sortQueryParams(query); - } + return this._makeRequest(request); + } else { + var url = this.buildURL(type.modelName, null, null, 'queryRecord', query); + + if (this.sortQueryParams) { + query = this.sortQueryParams(query); + } - return this.ajax(url, 'GET', { data: query }); + return this.ajax(url, 'GET', { data: query }); + } }, /** @@ -494,8 +532,17 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ findMany(store, type, ids, snapshots) { - var url = this.buildURL(type.modelName, ids, snapshots, 'findMany'); - return this.ajax(url, 'GET', { data: { ids: ids } }); + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, ids, snapshots, + requestType: 'findMany' + }); + + return this._makeRequest(request); + } else { + var url = this.buildURL(type.modelName, ids, snapshots, 'findMany'); + return this.ajax(url, 'GET', { data: { ids: ids } }); + } }, /** @@ -534,12 +581,21 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ findHasMany(store, snapshot, url, relationship) { - var id = snapshot.id; - var type = snapshot.modelName; + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, snapshot, url, relationship, + requestType: 'findHasMany' + }); - url = this.urlPrefix(url, this.buildURL(type, id, null, 'findHasMany')); + return this._makeRequest(request); + } else { + var id = snapshot.id; + var type = snapshot.modelName; - return this.ajax(url, 'GET'); + url = this.urlPrefix(url, this.buildURL(type, id, null, 'findHasMany')); + + return this.ajax(url, 'GET'); + } }, /** @@ -578,11 +634,20 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ findBelongsTo(store, snapshot, url, relationship) { - var id = snapshot.id; - var type = snapshot.modelName; + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, snapshot, url, relationship, + requestType: 'findBelongsTo' + }); + + return this._makeRequest(request); + } else { + var id = snapshot.id; + var type = snapshot.modelName; - url = this.urlPrefix(url, this.buildURL(type, id, null, 'findBelongsTo')); - return this.ajax(url, 'GET'); + url = this.urlPrefix(url, this.buildURL(type, id, null, 'findBelongsTo')); + return this.ajax(url, 'GET'); + } }, /** @@ -602,13 +667,22 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ createRecord(store, type, snapshot) { - var data = {}; - var serializer = store.serializerFor(type.modelName); - var url = this.buildURL(type.modelName, null, snapshot, 'createRecord'); + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, snapshot, + requestType: 'createRecord' + }); + + return this._makeRequest(request); + } else { + var data = {}; + var serializer = store.serializerFor(type.modelName); + var url = this.buildURL(type.modelName, null, snapshot, 'createRecord'); - serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); + serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); - return this.ajax(url, "POST", { data: data }); + return this.ajax(url, "POST", { data: data }); + } }, /** @@ -628,15 +702,24 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ updateRecord(store, type, snapshot) { - var data = {}; - var serializer = store.serializerFor(type.modelName); + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, snapshot, + requestType: 'updateRecord' + }); - serializer.serializeIntoHash(data, type, snapshot); + return this._makeRequest(request); + } else { + var data = {}; + var serializer = store.serializerFor(type.modelName); - var id = snapshot.id; - var url = this.buildURL(type.modelName, id, snapshot, 'updateRecord'); + serializer.serializeIntoHash(data, type, snapshot); + + var id = snapshot.id; + var url = this.buildURL(type.modelName, id, snapshot, 'updateRecord'); - return this.ajax(url, "PUT", { data: data }); + return this.ajax(url, "PUT", { data: data }); + } }, /** @@ -651,9 +734,18 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ deleteRecord(store, type, snapshot) { - var id = snapshot.id; + if (isEnabled('ds-improved-ajax')) { + const request = this._requestFor({ + store, type, snapshot, + requestType: 'deleteRecord' + }); + + return this._makeRequest(request); + } else { + var id = snapshot.id; - return this.ajax(this.buildURL(type.modelName, id, snapshot, 'deleteRecord'), "DELETE"); + return this.ajax(this.buildURL(type.modelName, id, snapshot, 'deleteRecord'), "DELETE"); + } }, _stripIDFromURL(store, snapshot) { @@ -1009,6 +1101,253 @@ export default Adapter.extend(BuildURLMixin, { } }); +if (isEnabled('ds-improved-ajax')) { + + RESTAdapter.reopen({ + + /** + * Get the data (body or query params) for a request. + * + * @public + * @method dataForRequest + * @param {Object} params + * @return {Object} data + */ + dataForRequest(params) { + var { store, type, snapshot, requestType, query } = params; + + // type is not passed to findBelongsTo and findHasMany + type = type || (snapshot && snapshot.type); + + var serializer = store.serializerFor(type.modelName); + var data = {}; + + switch (requestType) { + case 'createRecord': + serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); + break; + + case 'updateRecord': + serializer.serializeIntoHash(data, type, snapshot); + break; + + case 'findRecord': + data = this.buildQuery(snapshot); + break; + + case 'findAll': + if (params.sinceToken) { + query = query || {}; + query.since = params.sinceToken; + } + data = query; + break; + + case 'query': + case 'queryRecord': + if (this.sortQueryParams) { + query = this.sortQueryParams(query); + } + data = query; + break; + + case 'findMany': + data = { ids: params.ids }; + break; + + default: + data = undefined; + break; + } + + return data; + }, + + /** + * Get the HTTP method for a request. + * + * @public + * @method methodForRequest + * @param {Object} params + * @return {String} HTTP method + */ + methodForRequest(params) { + const { requestType } = params; + + switch (requestType) { + case 'createRecord': return 'POST'; + case 'updateRecord': return 'PUT'; + case 'deleteRecord': return 'DELETE'; + } + + return 'GET'; + }, + + /** + * Get the URL for a request. + * + * @public + * @method urlForRequest + * @param {Object} params + * @return {String} URL + */ + urlForRequest(params) { + var { type, id, ids, snapshot, snapshots, requestType, query } = params; + + // type and id are not passed from updateRecord and deleteRecord, hence they + // are defined if not set + type = type || (snapshot && snapshot.type); + id = id || (snapshot && snapshot.id); + + switch (requestType) { + case 'findAll': + return this.buildURL(type.modelName, null, null, requestType); + + case 'query': + case 'queryRecord': + return this.buildURL(type.modelName, null, null, requestType, query); + + case 'findMany': + return this.buildURL(type.modelName, ids, snapshots, requestType); + + case 'findHasMany': + case 'findBelongsTo': + let url = this.buildURL(type.modelName, id, null, requestType); + return this.urlPrefix(params.url, url); + } + + return this.buildURL(type.modelName, id, snapshot, requestType, query); + }, + + /** + * Get the headers for a request. + * + * By default the value of the `headers` property of the adapter is + * returned. + * + * @public + * @method headersForRequest + * @param {Object} params + * @return {Object} headers + */ + headersForRequest(params) { + return this.get('headers'); + }, + + /** + * Get an object which contains all properties for a request which should + * be made. + * + * @private + * @method _requestFor + * @param {Object} params + * @return {Object} request object + */ + _requestFor(params) { + const method = this.methodForRequest(params); + const url = this.urlForRequest(params); + const headers = this.headersForRequest(params); + const data = this.dataForRequest(params); + + return { method, url, headers, data }; + }, + + /** + * Convert a request object into a hash which can be passed to `jQuery.ajax`. + * + * @private + * @method _requestToJQueryAjaxHash + * @param {Object} request + * @return {Object} jQuery ajax hash + */ + _requestToJQueryAjaxHash(request) { + const hash = {}; + + hash.type = request.method; + hash.url = request.url; + hash.dataType = 'json'; + hash.context = this; + + if (request.data) { + if (request.type !== 'GET') { + hash.contentType = 'application/json; charset=utf-8'; + hash.data = JSON.stringify(request.data); + } else { + hash.data = request.data; + } + } + + var headers = request.headers; + if (headers !== undefined) { + hash.beforeSend = function(xhr) { + Object.keys(headers).forEach((key) => xhr.setRequestHeader(key, headers[key])); + }; + } + + return hash; + }, + + /** + * Make a request using `jQuery.ajax`. + * + * @private + * @method _makeRequest + * @param {Object} request + * @return {Promise} promise + */ + _makeRequest(request) { + const adapter = this; + const hash = this._requestToJQueryAjaxHash(request); + + const { method, url } = request; + const requestData = { method, url }; + + return new Ember.RSVP.Promise((resolve, reject) => { + + hash.success = function(payload, textStatus, jqXHR) { + let response = adapter.handleResponse( + jqXHR.status, + parseResponseHeaders(jqXHR.getAllResponseHeaders()), + payload, + requestData + ); + + if (response instanceof AdapterError) { + Ember.run.join(null, reject, response); + } else { + Ember.run.join(null, resolve, response); + } + }; + + hash.error = function(jqXHR, textStatus, errorThrown) { + let error; + + if (errorThrown instanceof Error) { + error = errorThrown; + } else if (textStatus === 'timeout') { + error = new TimeoutError(); + } else if (textStatus === 'abort') { + error = new AbortError(); + } else { + error = adapter.handleResponse( + jqXHR.status, + parseResponseHeaders(jqXHR.getAllResponseHeaders()), + adapter.parseErrorResponse(jqXHR.responseText) || errorThrown, + requestData + ); + } + + Ember.run.join(null, reject, error); + }; + + adapter._ajaxRequest(hash); + + }, `DS: RESTAdapter#makeRequest: ${method} ${url}`); + } + }); + +} + //From http://stackoverflow.com/questions/280634/endswith-in-javascript function endsWith(string, suffix) { if (typeof String.prototype.endsWith !== 'function') { @@ -1017,3 +1356,5 @@ function endsWith(string, suffix) { return string.endsWith(suffix); } } + +export default RESTAdapter; diff --git a/config/features.json b/config/features.json index 1ef9bd5a04b..4bb5e64d30b 100644 --- a/config/features.json +++ b/config/features.json @@ -1,6 +1,7 @@ { "ds-boolean-transform-allow-null": null, "ds-finder-include": null, + "ds-improved-ajax": null, "ds-references": null, "ds-transform-pass-options": null, "ds-pushpayload-return": null, diff --git a/tests/integration/adapter/build-url-mixin-test.js b/tests/integration/adapter/build-url-mixin-test.js index cb5ce7f4ae9..c77edd82c23 100644 --- a/tests/integration/adapter/build-url-mixin-test.js +++ b/tests/integration/adapter/build-url-mixin-test.js @@ -1,5 +1,6 @@ import setupStore from 'dummy/tests/helpers/store'; import Ember from 'ember'; +import isEnabled from 'ember-data/-private/features'; import {module, test} from 'qunit'; @@ -40,11 +41,19 @@ module("integration/adapter/build-url-mixin - BuildURLMixin with RESTAdapter", { }); function ajaxResponse(value) { - adapter.ajax = function(url, verb, hash) { - passedUrl = url; - - return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); - }; + if (isEnabled('ds-improved-ajax')) { + adapter._makeRequest = function(request) { + passedUrl = request.url; + + return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); + }; + } else { + adapter.ajax = function(url, verb, hash) { + passedUrl = url; + + return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); + }; + } } diff --git a/tests/integration/adapter/json-api-adapter-test.js b/tests/integration/adapter/json-api-adapter-test.js index 4012a4b2d2f..1ddff308851 100644 --- a/tests/integration/adapter/json-api-adapter-test.js +++ b/tests/integration/adapter/json-api-adapter-test.js @@ -4,6 +4,7 @@ import Ember from 'ember'; import {module, test} from 'qunit'; import DS from 'ember-data'; +import isEnabled from 'ember-data/-private/features'; var env, store, adapter; var passedUrl, passedVerb, passedHash; @@ -89,15 +90,27 @@ function ajaxResponse(responses) { passedVerb = []; passedHash = []; - adapter.ajax = function(url, verb, hash) { - index = counter++; + if (isEnabled('ds-improved-ajax')) { + adapter._makeRequest = function(request) { + index = counter++; - passedUrl[index] = url; - passedVerb[index] = verb; - passedHash[index] = hash; + passedUrl[index] = request.url; + passedVerb[index] = request.method; + passedHash[index] = request.data ? { data: request.data } : undefined; - return run(Ember.RSVP, 'resolve', responses[index]); - }; + return run(Ember.RSVP, 'resolve', responses[index]); + }; + } else { + adapter.ajax = function(url, verb, hash) { + index = counter++; + + passedUrl[index] = url; + passedVerb[index] = verb; + passedHash[index] = hash; + + return run(Ember.RSVP, 'resolve', responses[index]); + }; + } } test('find a single record', function(assert) { diff --git a/tests/integration/adapter/rest-adapter-test.js b/tests/integration/adapter/rest-adapter-test.js index 92350f1770f..0b98572ff3e 100644 --- a/tests/integration/adapter/rest-adapter-test.js +++ b/tests/integration/adapter/rest-adapter-test.js @@ -39,13 +39,23 @@ module("integration/adapter/rest_adapter - REST Adapter", { }); function ajaxResponse(value) { - adapter.ajax = function(url, verb, hash) { - passedUrl = url; - passedVerb = verb; - passedHash = hash; - - return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); - }; + if (isEnabled('ds-improved-ajax')) { + adapter._makeRequest = function(request) { + passedUrl = request.url; + passedVerb = request.method; + passedHash = request.data ? { data: request.data } : undefined; + + return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); + }; + } else { + adapter.ajax = function(url, verb, hash) { + passedUrl = url; + passedVerb = verb; + passedHash = hash; + + return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); + }; + } } test("findRecord - basic payload", function(assert) { diff --git a/tests/integration/store-test.js b/tests/integration/store-test.js index e3c98b1c68e..d6c7785a4e0 100644 --- a/tests/integration/store-test.js +++ b/tests/integration/store-test.js @@ -5,6 +5,7 @@ import testInDebug from 'dummy/tests/helpers/test-in-debug'; import {module, test} from 'qunit'; import DS from 'ember-data'; +import isEnabled from 'ember-data/-private/features'; var store, env; @@ -202,13 +203,20 @@ test("destroying the store correctly cleans everything up", function(assert) { function ajaxResponse(value) { var passedUrl, passedVerb, passedHash; - env.adapter.ajax = function(url, verb, hash) { - passedUrl = url; - passedVerb = verb; - passedHash = hash; - return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); - }; + if (isEnabled('ds-improved-ajax')) { + env.adapter._makeRequest = function() { + return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); + }; + } else { + env.adapter.ajax = function(url, verb, hash) { + passedUrl = url; + passedVerb = verb; + passedHash = hash; + + return run(Ember.RSVP, 'resolve', Ember.copy(value, true)); + }; + } } diff --git a/tests/unit/adapters/rest-adapter/ajax-test.js b/tests/unit/adapters/rest-adapter/ajax-test.js index f699282cf67..1dc80a2c662 100644 --- a/tests/unit/adapters/rest-adapter/ajax-test.js +++ b/tests/unit/adapters/rest-adapter/ajax-test.js @@ -4,6 +4,7 @@ import Ember from 'ember'; import {module, test} from 'qunit'; import DS from 'ember-data'; +import isEnabled from 'ember-data/-private/features'; var Person, Place, store, adapter, env; var run = Ember.run; @@ -28,12 +29,23 @@ module("unit/adapters/rest-adapter/ajax - building requests", { test("When an id is searched, the correct url should be generated", function(assert) { assert.expect(2); var count = 0; - adapter.ajax = function(url, method) { - if (count === 0) { assert.equal(url, '/people/1', "should create the correct url"); } - if (count === 1) { assert.equal(url, '/places/1', "should create the correct url"); } - count++; - return Ember.RSVP.resolve(); - }; + + if (isEnabled('ds-improved-ajax')) { + adapter._makeRequest = function(request) { + if (count === 0) { assert.equal(request.url, '/people/1', "should create the correct url"); } + if (count === 1) { assert.equal(request.url, '/places/1', "should create the correct url"); } + count++; + return Ember.RSVP.resolve(); + }; + } else { + adapter.ajax = function(url, method) { + if (count === 0) { assert.equal(url, '/people/1', "should create the correct url"); } + if (count === 1) { assert.equal(url, '/places/1', "should create the correct url"); } + count++; + return Ember.RSVP.resolve(); + }; + } + run(function() { adapter.findRecord(store, Person, 1, {}); adapter.findRecord(store, Place, 1, {}); @@ -42,10 +54,18 @@ test("When an id is searched, the correct url should be generated", function(ass test("id's should be sanatized", function(assert) { assert.expect(1); - adapter.ajax = function(url, method) { - assert.equal(url, '/people/..%2Fplace%2F1', "should create the correct url"); - return Ember.RSVP.resolve(); - }; + + if (isEnabled('ds-improved-ajax')) { + adapter._makeRequest = function(request) { + assert.equal(request.url, '/people/..%2Fplace%2F1', "should create the correct url"); + return Ember.RSVP.resolve(); + }; + } else { + adapter.ajax = function(url, method) { + assert.equal(url, '/people/..%2Fplace%2F1', "should create the correct url"); + return Ember.RSVP.resolve(); + }; + } run(function() { adapter.findRecord(store, Person, '../place/1', {}); }); diff --git a/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js b/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js index 49baf6d173b..87d68414090 100644 --- a/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js +++ b/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js @@ -4,6 +4,7 @@ import Ember from 'ember'; import {module, test} from 'qunit'; import DS from 'ember-data'; +import isEnabled from 'ember-data/-private/features'; var GroupsAdapter, Store; var maxLength = -1; @@ -17,25 +18,45 @@ module("unit/adapters/rest_adapter/group_records_for_find_many_test - DS.RESTAda findRecord(store, type, id, snapshot) { return Ember.RSVP.Promise.resolve({ id: id }); - }, - - ajax(url, type, options) { - var queryString = options.data.ids.map(function(i) { - return encodeURIComponent('ids[]') + '=' + encodeURIComponent(i); - }).join('&'); - var fullUrl = url + '?' + queryString; - - maxLength = this.get('maxURLLength'); - lengths.push(fullUrl.length); - - var testRecords = options.data.ids.map(function(id) { - return { id: id }; - }); - return Ember.RSVP.Promise.resolve({ 'testRecords' : testRecords }); } - }); + if (isEnabled('ds-improved-ajax')) { + GroupsAdapter.reopen({ + _makeRequest(request) { + var queryString = request.data.ids.map(function(i) { + return encodeURIComponent('ids[]') + '=' + encodeURIComponent(i); + }).join('&'); + var fullUrl = request.url + '?' + queryString; + + maxLength = this.get('maxURLLength'); + lengths.push(fullUrl.length); + + var testRecords = request.data.ids.map(function(id) { + return { id: id }; + }); + return Ember.RSVP.Promise.resolve({ 'testRecords' : testRecords }); + } + }); + } else { + GroupsAdapter.reopen({ + ajax(url, type, options) { + var queryString = options.data.ids.map(function(i) { + return encodeURIComponent('ids[]') + '=' + encodeURIComponent(i); + }).join('&'); + var fullUrl = url + '?' + queryString; + + maxLength = this.get('maxURLLength'); + lengths.push(fullUrl.length); + + var testRecords = options.data.ids.map(function(id) { + return { id: id }; + }); + return Ember.RSVP.Promise.resolve({ 'testRecords' : testRecords }); + } + }); + } + Store = createStore({ adapter: GroupsAdapter, testRecord: DS.Model.extend() diff --git a/tests/unit/model/rollback-attributes-test.js b/tests/unit/model/rollback-attributes-test.js index 1014af6ec95..9c58865b958 100644 --- a/tests/unit/model/rollback-attributes-test.js +++ b/tests/unit/model/rollback-attributes-test.js @@ -4,6 +4,7 @@ import Ember from 'ember'; import {module, test} from 'qunit'; import DS from 'ember-data'; +import isEnabled from 'ember-data/-private/features'; var env, store, Person, Dog; var run = Ember.run; @@ -224,11 +225,21 @@ test("invalid new record's attributes can be rollbacked", function(assert) { source: { pointer: 'data/attributes/name' } } ]); - var adapter = DS.RESTAdapter.extend({ - ajax(url, type, hash) { - return Ember.RSVP.reject(error); - } - }); + + var adapter; + if (isEnabled('ds-improved-ajax')) { + adapter = DS.RESTAdapter.extend({ + _makeRequest() { + return Ember.RSVP.reject(error); + } + }); + } else { + adapter = DS.RESTAdapter.extend({ + ajax(url, type, hash) { + return Ember.RSVP.reject(error); + } + }); + } env = setupStore({ person: Person, adapter: adapter }); @@ -254,12 +265,22 @@ test("invalid new record's attributes can be rollbacked", function(assert) { test("invalid record's attributes can be rollbacked after multiple failed calls - #3677", function(assert) { var person; - var adapter = DS.RESTAdapter.extend({ - ajax(url, type, hash) { - var error = new DS.InvalidError(); - return Ember.RSVP.reject(error); - } - }); + var adapter; + if (isEnabled('ds-improved-ajax')) { + adapter = DS.RESTAdapter.extend({ + _makeRequest() { + var error = new DS.InvalidError(); + return Ember.RSVP.reject(error); + } + }); + } else { + adapter = DS.RESTAdapter.extend({ + ajax(url, type, hash) { + var error = new DS.InvalidError(); + return Ember.RSVP.reject(error); + } + }); + } env = setupStore({ person: Person, adapter: adapter }); @@ -335,11 +356,21 @@ test("invalid record's attributes can be rollbacked", function(assert) { } ]); - var adapter = DS.RESTAdapter.extend({ - ajax(url, type, hash) { - return Ember.RSVP.reject(error); - } - }); + var adapter; + if (isEnabled('ds-improved-ajax')) { + adapter = DS.RESTAdapter.extend({ + _makeRequest() { + return Ember.RSVP.reject(error); + } + }); + } else { + adapter = DS.RESTAdapter.extend({ + ajax(url, type, hash) { + return Ember.RSVP.reject(error); + } + }); + } + env = setupStore({ dog: Dog, adapter: adapter }); var dog; @@ -396,11 +427,20 @@ test("invalid record's attributes rolled back to correct state after set", funct } ]); - var adapter = DS.RESTAdapter.extend({ - ajax(url, type, hash) { - return Ember.RSVP.reject(error); - } - }); + var adapter; + if (isEnabled('ds-improved-ajax')) { + adapter = DS.RESTAdapter.extend({ + _makeRequest() { + return Ember.RSVP.reject(error); + } + }); + } else { + adapter = DS.RESTAdapter.extend({ + ajax(url, type, hash) { + return Ember.RSVP.reject(error); + } + }); + } env = setupStore({ dog: Dog, adapter: adapter }); var dog; @@ -463,11 +503,20 @@ test("when destroying a record setup the record state to invalid, the record's a } ]); - var adapter = DS.RESTAdapter.extend({ - ajax(url, type, hash) { - return Ember.RSVP.reject(error); - } - }); + var adapter; + if (isEnabled('ds-improved-ajax')) { + adapter = DS.RESTAdapter.extend({ + _makeRequest() { + return Ember.RSVP.reject(error); + } + }); + } else { + adapter = DS.RESTAdapter.extend({ + ajax(url, type, hash) { + return Ember.RSVP.reject(error); + } + }); + } env = setupStore({ dog: Dog, adapter: adapter }); var dog;