From e24e85d94ad4bee63fc5584ff4edb9d61cc9a6db Mon Sep 17 00:00:00 2001 From: Austin Burdine Date: Mon, 18 Jan 2016 09:37:14 -0600 Subject: [PATCH] deps: ember-ajax@0.7.1 --- app/adapters/base.js | 5 +- app/components/gh-search-input.js | 8 +- app/components/modals/delete-all.js | 7 +- app/controllers/reset.js | 11 ++- app/controllers/settings/labs.js | 23 ++--- app/controllers/setup/two.js | 21 +++-- app/controllers/signin.js | 15 ++-- app/controllers/signup.js | 19 ++-- app/controllers/team/user.js | 5 +- app/instance-initializers/oauth-prefilter.js | 21 ----- app/models/user.js | 9 +- app/routes/about.js | 5 +- app/routes/setup.js | 21 ++--- app/routes/setup/one.js | 6 +- app/routes/signup.js | 8 +- app/services/ajax.js | 32 +++++++ app/services/notifications.js | 28 +++--- app/services/slug-generator.js | 4 +- package.json | 2 +- tests/integration/services/ajax-test.js | 92 ++++++++++++++++++++ tests/unit/services/notifications-test.js | 42 ++++----- 21 files changed, 246 insertions(+), 138 deletions(-) delete mode 100644 app/instance-initializers/oauth-prefilter.js create mode 100644 app/services/ajax.js create mode 100644 tests/integration/services/ajax-test.js diff --git a/app/adapters/base.js b/app/adapters/base.js index 6eb579fbd..886d08f37 100644 --- a/app/adapters/base.js +++ b/app/adapters/base.js @@ -1,11 +1,14 @@ import Ember from 'ember'; import DS from 'ember-data'; import ghostPaths from 'ghost/utils/ghost-paths'; +import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin'; const {inject} = Ember; const {RESTAdapter} = DS; -export default RESTAdapter.extend({ +export default RESTAdapter.extend(DataAdapterMixin, { + authorizer: 'authorizer:oauth2', + host: window.location.origin, namespace: ghostPaths().apiRoot.slice(1), diff --git a/app/components/gh-search-input.js b/app/components/gh-search-input.js index ab85e4d8d..ab4dfcef9 100644 --- a/app/components/gh-search-input.js +++ b/app/components/gh-search-input.js @@ -1,7 +1,6 @@ /* global key */ /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; const {$, Component, RSVP, computed, inject, observer} = Ember; const {filterBy} = computed; @@ -21,6 +20,7 @@ export default Component.extend({ _store: inject.service('store'), _routing: inject.service('-routing'), + ajax: inject.service(), _selectize: computed(function () { return this.$('select')[0].selectize; @@ -54,7 +54,7 @@ export default Component.extend({ let postsQuery = {fields: 'id,title,page', limit: 'all', status: 'all', staticPages: 'all'}; let content = this.get('content'); - return ajax(postsUrl, {data: postsQuery}).then((posts) => { + return this.get('ajax').request(postsUrl, {data: postsQuery}).then((posts) => { content.pushObjects(posts.posts.map((post) => { return { id: `post.${post.id}`, @@ -71,7 +71,7 @@ export default Component.extend({ let usersQuery = {fields: 'name,slug', limit: 'all'}; let content = this.get('content'); - return ajax(usersUrl, {data: usersQuery}).then((users) => { + return this.get('ajax').request(usersUrl, {data: usersQuery}).then((users) => { content.pushObjects(users.users.map((user) => { return { id: `user.${user.slug}`, @@ -88,7 +88,7 @@ export default Component.extend({ let tagsQuery = {fields: 'name,slug', limit: 'all'}; let content = this.get('content'); - return ajax(tagsUrl, {data: tagsQuery}).then((tags) => { + return this.get('ajax').request(tagsUrl, {data: tagsQuery}).then((tags) => { content.pushObjects(tags.tags.map((tag) => { return { id: `tag.${tag.slug}`, diff --git a/app/components/modals/delete-all.js b/app/components/modals/delete-all.js index 51d792ae6..3b7c05e95 100644 --- a/app/components/modals/delete-all.js +++ b/app/components/modals/delete-all.js @@ -1,6 +1,5 @@ import Ember from 'ember'; import ModalComponent from 'ghost/components/modals/base'; -import {request as ajax} from 'ic-ajax'; const {inject} = Ember; @@ -11,11 +10,11 @@ export default ModalComponent.extend({ ghostPaths: inject.service('ghost-paths'), notifications: inject.service(), store: inject.service(), + ajax: inject.service(), _deleteAll() { - return ajax(this.get('ghostPaths.url').api('db'), { - type: 'DELETE' - }); + let deleteUrl = this.get('ghostPaths.url').api('db'); + return this.get('ajax').del(deleteUrl); }, _unloadData() { diff --git a/app/controllers/reset.js b/app/controllers/reset.js index 29a2b9720..109859bdd 100644 --- a/app/controllers/reset.js +++ b/app/controllers/reset.js @@ -1,5 +1,4 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; import ValidationEngine from 'ghost/mixins/validation-engine'; const {Controller, computed, inject} = Ember; @@ -16,6 +15,7 @@ export default Controller.extend(ValidationEngine, { ghostPaths: inject.service('ghost-paths'), notifications: inject.service(), session: inject.service(), + ajax: inject.service(), email: computed('token', function () { // The token base64 encodes the email (and some other stuff), @@ -39,10 +39,9 @@ export default Controller.extend(ValidationEngine, { this.set('flowErrors', ''); this.get('hasValidated').addObjects(['newPassword', 'ne2Password']); this.validate().then(() => { + let authUrl = this.get('ghostPaths.url').api('authentication', 'passwordreset'); this.toggleProperty('submitting'); - ajax({ - url: this.get('ghostPaths.url').api('authentication', 'passwordreset'), - type: 'PUT', + this.get('ajax').put(authUrl, { data: { passwordreset: [credentials] } @@ -50,8 +49,8 @@ export default Controller.extend(ValidationEngine, { this.toggleProperty('submitting'); this.get('notifications').showAlert(resp.passwordreset[0].message, {type: 'warn', delayed: true, key: 'password.reset'}); this.get('session').authenticate('authenticator:oauth2', this.get('email'), credentials.newPassword); - }).catch((response) => { - this.get('notifications').showAPIError(response, {key: 'password.reset'}); + }).catch((error) => { + this.get('notifications').showAPIError(error, {key: 'password.reset'}); this.toggleProperty('submitting'); }); }).catch((error) => { diff --git a/app/controllers/settings/labs.js b/app/controllers/settings/labs.js index b1602bc38..a3688ff44 100644 --- a/app/controllers/settings/labs.js +++ b/app/controllers/settings/labs.js @@ -1,7 +1,6 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; -const {$, Controller, computed, inject} = Ember; +const {$, Controller, computed, inject, isArray} = Ember; export default Controller.extend({ uploadButtonText: 'Import', @@ -13,6 +12,7 @@ export default Controller.extend({ notifications: inject.service(), session: inject.service(), feature: inject.controller(), + ajax: inject.service(), labsJSON: computed('model.labs', function () { return JSON.parse(this.get('model.labs') || {}); @@ -47,14 +47,14 @@ export default Controller.extend({ let formData = new FormData(); let notifications = this.get('notifications'); let currentUserId = this.get('session.user.id'); + let dbUrl = this.get('ghostPaths.url').api('db'); this.set('uploadButtonText', 'Importing'); this.set('importErrors', ''); formData.append('importfile', file); - ajax(this.get('ghostPaths.url').api('db'), { - type: 'POST', + this.get('ajax').post(dbUrl, { data: formData, dataType: 'json', cache: false, @@ -68,8 +68,8 @@ export default Controller.extend({ // TODO: keep as notification, add link to view content notifications.showNotification('Import successful.', {key: 'import.upload.success'}); }).catch((response) => { - if (response && response.jqXHR && response.jqXHR.responseJSON && response.jqXHR.responseJSON.errors) { - this.set('importErrors', response.jqXHR.responseJSON.errors); + if (response && response.errors && isArray(response.errors)) { + this.set('importErrors', response.errors); } notifications.showAlert('Import Failed', {type: 'error', key: 'import.upload.failed'}); @@ -93,20 +93,15 @@ export default Controller.extend({ sendTestEmail() { let notifications = this.get('notifications'); + let emailUrl = this.get('ghostPaths.url').api('mail', 'test'); this.toggleProperty('submitting'); - ajax(this.get('ghostPaths.url').api('mail', 'test'), { - type: 'POST' - }).then(() => { + this.get('ajax').post(emailUrl).then(() => { notifications.showAlert('Check your email for the test message.', {type: 'info', key: 'test-email.send.success'}); this.toggleProperty('submitting'); }).catch((error) => { - if (typeof error.jqXHR !== 'undefined') { - notifications.showAPIError(error, {key: 'test-email.send'}); - } else { - notifications.showErrors(error, {key: 'test-email.send'}); - } + notifications.showAPIError(error, {key: 'test-email:send'}); this.toggleProperty('submitting'); }); }, diff --git a/app/controllers/setup/two.js b/app/controllers/setup/two.js index a9cea253c..8abb28528 100644 --- a/app/controllers/setup/two.js +++ b/app/controllers/setup/two.js @@ -1,8 +1,7 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; import ValidationEngine from 'ghost/mixins/validation-engine'; -const {Controller, RSVP, inject} = Ember; +const {Controller, RSVP, inject, isArray} = Ember; export default Controller.extend(ValidationEngine, { size: 90, @@ -20,6 +19,7 @@ export default Controller.extend(ValidationEngine, { application: inject.controller(), config: inject.service(), session: inject.service(), + ajax: inject.service(), // ValidationEngine settings validationType: 'setup', @@ -36,10 +36,9 @@ export default Controller.extend(ValidationEngine, { image.formData = {}; image.submit() .success((response) => { + let usersUrl = this.get('ghostPaths.url').api('users', user.id.toString()); user.image = response; - ajax({ - url: this.get('ghostPaths.url').api('users', user.id.toString()), - type: 'PUT', + this.get('ajax').put(usersUrl, { data: { users: [user] } @@ -51,8 +50,9 @@ export default Controller.extend(ValidationEngine, { _handleSaveError(resp) { this.toggleProperty('submitting'); - if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) { - this.set('flowErrors', resp.jqXHR.responseJSON.errors[0].message); + + if (resp && resp.errors && isArray(resp.errors)) { + this.set('flowErrors', resp.errors[0].message); } else { this.get('notifications').showAPIError(resp, {key: 'setup.blog-details'}); } @@ -96,16 +96,15 @@ export default Controller.extend(ValidationEngine, { let setupProperties = ['blogTitle', 'name', 'email', 'password']; let data = this.getProperties(setupProperties); let config = this.get('config'); - let method = this.get('blogCreated') ? 'PUT' : 'POST'; + let method = this.get('blogCreated') ? 'put' : 'post'; this.toggleProperty('submitting'); this.set('flowErrors', ''); this.get('hasValidated').addObjects(setupProperties); this.validate().then(() => { - ajax({ - url: this.get('ghostPaths.url').api('authentication', 'setup'), - type: method, + let authUrl = this.get('ghostPaths.url').api('authentication', 'setup'); + this.get('ajax')[method](authUrl, { data: { setup: [{ name: data.name, diff --git a/app/controllers/signin.js b/app/controllers/signin.js index 8e648584c..d8307c67a 100644 --- a/app/controllers/signin.js +++ b/app/controllers/signin.js @@ -1,8 +1,7 @@ import Ember from 'ember'; import ValidationEngine from 'ghost/mixins/validation-engine'; -import {request as ajax} from 'ic-ajax'; -const {$, Controller, inject} = Ember; +const {$, Controller, inject, isArray} = Ember; export default Controller.extend(ValidationEngine, { submitting: false, @@ -13,6 +12,7 @@ export default Controller.extend(ValidationEngine, { notifications: inject.service(), session: inject.service(), application: inject.controller(), + ajax: inject.service(), flowErrors: '', // ValidationEngine settings @@ -76,11 +76,10 @@ export default Controller.extend(ValidationEngine, { // This is a bit dirty, but there's no other way to ensure the properties are set as well as 'forgotPassword' this.get('hasValidated').addObject('identification'); this.validate({property: 'forgotPassword'}).then(() => { + let forgottenUrl = this.get('ghostPaths.url').api('authentication', 'passwordreset'); this.toggleProperty('submitting'); - ajax({ - url: this.get('ghostPaths.url').api('authentication', 'passwordreset'), - type: 'POST', + this.get('ajax').put(forgottenUrl, { data: { passwordreset: [{email}] } @@ -89,9 +88,9 @@ export default Controller.extend(ValidationEngine, { notifications.showAlert('Please check your email for instructions.', {type: 'info', key: 'forgot-password.send.success'}); }).catch((resp) => { this.toggleProperty('submitting'); - if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) { - let [error] = resp.jqXHR.responseJSON.errors; - let {message} = error; + + if (resp && resp.errors && isArray(resp.errors)) { + let [{message}] = resp.errors; this.set('flowErrors', message); diff --git a/app/controllers/signup.js b/app/controllers/signup.js index 660f5a3e4..8246c3f49 100644 --- a/app/controllers/signup.js +++ b/app/controllers/signup.js @@ -1,8 +1,7 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; import ValidationEngine from 'ghost/mixins/validation-engine'; -const {Controller, RSVP, inject} = Ember; +const {Controller, RSVP, inject, isArray} = Ember; export default Controller.extend(ValidationEngine, { // ValidationEngine settings @@ -16,6 +15,7 @@ export default Controller.extend(ValidationEngine, { config: inject.service(), notifications: inject.service(), session: inject.service(), + ajax: inject.service(), sendImage() { let image = this.get('image'); @@ -25,10 +25,9 @@ export default Controller.extend(ValidationEngine, { image.formData = {}; image.submit() .success((response) => { + let usersUrl = this.get('ghostPaths.url').api('users', user.id.toString()); user.image = response; - ajax({ - url: this.get('ghostPaths.url').api('users', user.id.toString()), - type: 'PUT', + this.get('ajax').put(usersUrl, { data: { users: [user] } @@ -51,10 +50,9 @@ export default Controller.extend(ValidationEngine, { this.get('hasValidated').addObjects(setupProperties); this.validate().then(() => { + let authUrl = this.get('ghostPaths.url').api('authentication', 'invitation'); this.toggleProperty('submitting'); - ajax({ - url: this.get('ghostPaths.url').api('authentication', 'invitation'), - type: 'POST', + this.get('ajax').post(authUrl, { dataType: 'json', data: { invitation: [{ @@ -74,8 +72,9 @@ export default Controller.extend(ValidationEngine, { }); }).catch((resp) => { this.toggleProperty('submitting'); - if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) { - this.set('flowErrors', resp.jqXHR.responseJSON.errors[0].message); + + if (resp && resp.errors && isArray(resp.errors)) { + this.set('flowErrors', resp.errors[0].message); } else { notifications.showAPIError(resp, {key: 'signup.complete'}); } diff --git a/app/controllers/team/user.js b/app/controllers/team/user.js index a22b62ffc..0acbba8bd 100644 --- a/app/controllers/team/user.js +++ b/app/controllers/team/user.js @@ -1,5 +1,4 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; import isNumber from 'ghost/utils/isNumber'; import boundOneWay from 'ghost/utils/bound-one-way'; import ValidationEngine from 'ghost/mixins/validation-engine'; @@ -17,6 +16,7 @@ export default Controller.extend(ValidationEngine, { showUploadCoverModal: false, showUplaodImageModal: false, + ajax: inject.service(), dropdown: inject.service(), ghostPaths: inject.service('ghost-paths'), notifications: inject.service(), @@ -242,8 +242,7 @@ export default Controller.extend(ValidationEngine, { this.get('dropdown').closeDropdowns(); - return ajax(url, { - type: 'PUT', + return this.get('ajax').put(url, { data: { owner: [{ id: user.get('id') diff --git a/app/instance-initializers/oauth-prefilter.js b/app/instance-initializers/oauth-prefilter.js deleted file mode 100644 index 686ede4e6..000000000 --- a/app/instance-initializers/oauth-prefilter.js +++ /dev/null @@ -1,21 +0,0 @@ -import Ember from 'ember'; - -const {merge} = Ember; - -export default { - name: 'oauth-prefilter', - after: 'ember-simple-auth', - - initialize(application) { - let session = application.lookup('service:session'); - - Ember.$.ajaxPrefilter(function (options) { - session.authorize('authorizer:oauth2', function (headerName, headerValue) { - let headerObject = {}; - - headerObject[headerName] = headerValue; - options.headers = merge(options.headers || {}, headerObject); - }); - }); - } -}; diff --git a/app/models/user.js b/app/models/user.js index 963f04779..610387f44 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -1,7 +1,6 @@ /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ import Ember from 'ember'; import DS from 'ember-data'; -import {request as ajax} from 'ic-ajax'; import ValidationEngine from 'ghost/mixins/validation-engine'; const {computed, inject} = Ember; @@ -37,6 +36,7 @@ export default Model.extend(ValidationEngine, { count: DS.attr('raw'), ghostPaths: inject.service('ghost-paths'), + ajax: inject.service(), // TODO: Once client-side permissions are in place, // remove the hard role check. @@ -87,8 +87,7 @@ export default Model.extend(ValidationEngine, { saveNewPassword() { let url = this.get('ghostPaths.url').api('users', 'password'); - return ajax(url, { - type: 'PUT', + return this.get('ajax').put(url, { data: { password: [{ user_id: this.get('id'), @@ -106,9 +105,9 @@ export default Model.extend(ValidationEngine, { email: fullUserData.email, roles: fullUserData.roles }; + let inviteUrl = this.get('ghostPaths.url').api('users'); - return ajax(this.get('ghostPaths.url').api('users'), { - type: 'POST', + return this.get('ajax').post(inviteUrl, { data: JSON.stringify({users: [userData]}), contentType: 'application/json' }); diff --git a/app/routes/about.js b/app/routes/about.js index f140cf663..6238c78ed 100644 --- a/app/routes/about.js +++ b/app/routes/about.js @@ -1,5 +1,4 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; import AuthenticatedRoute from 'ghost/routes/authenticated'; import styleBody from 'ghost/mixins/style-body'; @@ -11,17 +10,19 @@ export default AuthenticatedRoute.extend(styleBody, { classNames: ['view-about'], ghostPaths: inject.service('ghost-paths'), + ajax: inject.service(), cachedConfig: false, model() { let cachedConfig = this.get('cachedConfig'); + let configUrl = this.get('ghostPaths.url').api('configuration'); if (cachedConfig) { return cachedConfig; } - return ajax(this.get('ghostPaths.url').api('configuration')) + return this.get('ajax').request(configUrl) .then((configurationResponse) => { let configKeyValues = configurationResponse.configuration; diff --git a/app/routes/setup.js b/app/routes/setup.js index 8d9f48d6f..09d9854e7 100644 --- a/app/routes/setup.js +++ b/app/routes/setup.js @@ -1,5 +1,4 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; import Configuration from 'ember-simple-auth/configuration'; import styleBody from 'ghost/mixins/style-body'; @@ -12,6 +11,7 @@ export default Route.extend(styleBody, { ghostPaths: inject.service('ghost-paths'), session: inject.service(), + ajax: inject.service(), // use the beforeModel hook to check to see whether or not setup has been // previously completed. If it has, stop the transition into the setup page. @@ -23,16 +23,17 @@ export default Route.extend(styleBody, { return; } - // If user is not logged in, check the state of the setup process via the API - return ajax(this.get('ghostPaths.url').api('authentication/setup'), { - type: 'GET' - }).then((result) => { - let setup = result.setup[0].status; + let authUrl = this.get('ghostPaths.url').api('authentication', 'setup'); - if (setup) { - return this.transitionTo('signin'); - } - }); + // If user is not logged in, check the state of the setup process via the API + return this.get('ajax').request(authUrl) + .then((result) => { + let setup = result.setup[0].status; + + if (setup) { + return this.transitionTo('signin'); + } + }); }, deactivate() { diff --git a/app/routes/setup/one.js b/app/routes/setup/one.js index d45b5e631..1af6c35cf 100644 --- a/app/routes/setup/one.js +++ b/app/routes/setup/one.js @@ -1,5 +1,5 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; +import AjaxService from 'ember-ajax/services/ajax'; const {Route, inject, run} = Ember; @@ -8,6 +8,8 @@ let DownloadCountPoller = Ember.Object.extend({ count: '', runId: null, + ajax: AjaxService.create(), + init() { this._super(...arguments); this.downloadCounter(); @@ -27,7 +29,7 @@ let DownloadCountPoller = Ember.Object.extend({ }, downloadCounter() { - ajax(this.get('url')).then((data) => { + this.get('ajax').request(this.get('url')).then((data) => { let pattern = /(-?\d+)(\d{3})/; let count = data.count.toString(); diff --git a/app/routes/signup.js b/app/routes/signup.js index b8b7fd193..d516f99d9 100644 --- a/app/routes/signup.js +++ b/app/routes/signup.js @@ -1,6 +1,5 @@ import Ember from 'ember'; import DS from 'ember-data'; -import {request as ajax} from 'ic-ajax'; import Configuration from 'ember-simple-auth/configuration'; import styleBody from 'ghost/mixins/style-body'; @@ -13,6 +12,7 @@ export default Route.extend(styleBody, { ghostPaths: inject.service('ghost-paths'), notifications: inject.service(), session: inject.service(), + ajax: inject.service(), beforeModel() { this._super(...arguments); @@ -43,9 +43,9 @@ export default Route.extend(styleBody, { model.set('token', params.token); model.set('errors', Errors.create()); - return ajax({ - url: this.get('ghostPaths.url').api('authentication', 'invitation'), - type: 'GET', + let authUrl = this.get('ghostPaths.url').api('authentication', 'invitation'); + + return this.get('ajax').request(authUrl, { dataType: 'json', data: { email diff --git a/app/services/ajax.js b/app/services/ajax.js new file mode 100644 index 000000000..bec8ea502 --- /dev/null +++ b/app/services/ajax.js @@ -0,0 +1,32 @@ +import Ember from 'ember'; +import AjaxService from 'ember-ajax/services/ajax'; + +const {inject, computed} = Ember; + +export default AjaxService.extend({ + session: inject.service(), + + headers: computed('session.isAuthenticated', function () { + let session = this.get('session'); + + if (session.get('isAuthenticated')) { + let headers = {}; + + session.authorize('authorizer:oauth2', (headerName, headerValue) => { + headers[headerName] = headerValue; + }); + + return headers; + } else { + return []; + } + }), + + normalizeErrorResponse(status, headers, payload) { + if (payload && typeof payload === 'object') { + return payload.error || payload.errors || payload.message || false; + } else { + return false; + } + } +}); diff --git a/app/services/notifications.js b/app/services/notifications.js index d17226077..09666650b 100644 --- a/app/services/notifications.js +++ b/app/services/notifications.js @@ -1,7 +1,13 @@ import Ember from 'ember'; +import {AjaxError} from 'ember-ajax/errors'; -const {Service, computed, get, set} = Ember; -const {filter} = computed; +const { + Service, + computed: {filter}, + get, + set, + isArray +} = Ember; const emberA = Ember.A; // Notification keys take the form of "noun.verb.message", eg: @@ -109,14 +115,16 @@ export default Service.extend({ options.defaultErrorText = options.defaultErrorText || 'There was a problem on the server, please try again.'; - if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.error) { - this.showAlert(resp.jqXHR.responseJSON.error, options); - } else if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) { - this.showErrors(resp.jqXHR.responseJSON.errors, options); - } else if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.message) { - this.showAlert(resp.jqXHR.responseJSON.message, options); - } else { - this.showAlert(options.defaultErrorText, options); + if (resp instanceof AjaxError) { + resp = resp.errors; + } + + if (resp && isArray(resp) && resp.length) { // Array of errors + this.showErrors(resp, options); + } else if (resp && resp.detail) { // ember-ajax provided error message + this.showAlert(resp.detail, options); + } else { // text error or no error + this.showAlert(resp || options.defaultErrorText, options); } }, diff --git a/app/services/slug-generator.js b/app/services/slug-generator.js index c88232a04..d88113674 100644 --- a/app/services/slug-generator.js +++ b/app/services/slug-generator.js @@ -1,10 +1,10 @@ import Ember from 'ember'; -import {request as ajax} from 'ic-ajax'; const {RSVP, inject, Service} = Ember; export default Service.extend({ ghostPaths: inject.service('ghost-paths'), + ajax: inject.service(), generateSlug(slugType, textToSlugify) { let url; @@ -15,7 +15,7 @@ export default Service.extend({ url = this.get('ghostPaths.url').api('slugs', slugType, encodeURIComponent(textToSlugify)); - return ajax(url).then((response) => { + return this.get('ajax').request(url).then((response) => { let [firstSlug] = response.slugs; let {slug} = firstSlug; diff --git a/package.json b/package.json index 9a4590de0..a18d0e4c2 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "license": "MIT", "devDependencies": { "broccoli-asset-rev": "2.2.0", + "ember-ajax": "0.7.1", "ember-cli": "1.13.13", "ember-cli-app-version": "1.0.0", "ember-cli-babel": "5.1.5", @@ -29,7 +30,6 @@ "ember-cli-fastclick": "1.0.3", "ember-cli-htmlbars": "1.0.1", "ember-cli-htmlbars-inline-precompile": "0.3.1", - "ember-cli-ic-ajax": "0.2.4", "ember-cli-mirage": "0.1.9", "ember-cli-mocha": "0.9.8", "ember-cli-pretender": "0.5.0", diff --git a/tests/integration/services/ajax-test.js b/tests/integration/services/ajax-test.js new file mode 100644 index 000000000..ebef98039 --- /dev/null +++ b/tests/integration/services/ajax-test.js @@ -0,0 +1,92 @@ +import { expect } from 'chai'; +import { + describeModule, + it +} from 'ember-mocha'; +import Pretender from 'pretender'; +import wait from 'ember-test-helpers/wait'; + +function stubAjaxEndpoint(server, response) { + server.get('/test/', function () { + return [ + 500, + {'Content-Type': 'application/json'}, + JSON.stringify(response) + ]; + }); +} + +describeModule( + 'service:ajax', + 'Integration: Service: ajax', + { + integration: true + }, + function () { + let server; + + beforeEach(function () { + server = new Pretender(); + }); + + afterEach(function () { + server.shutdown(); + }); + + it('correctly parses single message response text', function (done) { + let error = {message: 'Test Error'}; + stubAjaxEndpoint(server, error); + + let ajax = this.subject(); + + ajax.request('/test/').then(() => { + expect(false).to.be.true(); + }).catch((error) => { + expect(error.errors).to.equal('Test Error'); + done(); + }); + }); + + it('correctly parses single error response text', function (done) { + let error = {error: 'Test Error'}; + stubAjaxEndpoint(server, error); + + let ajax = this.subject(); + + ajax.request('/test/').then(() => { + expect(false).to.be.true(); + }).catch((error) => { + expect(error.errors).to.equal('Test Error'); + done(); + }); + }); + + it('correctly parses multiple error messages', function (done) { + let error = {errors: ['First Error', 'Second Error']}; + stubAjaxEndpoint(server, error); + + let ajax = this.subject(); + + ajax.request('/test/').then(() => { + expect(false).to.be.true(); + }).catch((error) => { + expect(error.errors).to.deep.equal(['First Error', 'Second Error']); + done(); + }); + }); + + it('correctly returns default error message if no error text provided', function (done) { + stubAjaxEndpoint(server, {}); + + let ajax = this.subject(); + + ajax.request('/test/').then(() => { + expect(false).to.be.true; + }).catch((error) => { + let [defaultError] = error.errors; + expect(defaultError.detail).to.equal('Ajax operation failed'); + done(); + }); + }); + } +); diff --git a/tests/unit/services/notifications-test.js b/tests/unit/services/notifications-test.js index 7c3bdd924..4440430d7 100644 --- a/tests/unit/services/notifications-test.js +++ b/tests/unit/services/notifications-test.js @@ -6,6 +6,7 @@ import { describeModule, it } from 'ember-mocha'; +import {AjaxError, InvalidError} from 'ember-ajax/errors'; const {run, get} = Ember; const emberA = Ember.A; @@ -170,10 +171,10 @@ describeModule( it('#showAPIError adds single json response error', function () { const notifications = this.subject(); - const resp = {jqXHR: {responseJSON: {error: 'Single error'}}}; + const error = new AjaxError('Single error'); run(() => { - notifications.showAPIError(resp); + notifications.showAPIError(error); }); let notification = notifications.get('alerts.firstObject'); @@ -186,10 +187,10 @@ describeModule( // used to display validation errors returned from the server it('#showAPIError adds multiple json response errors', function () { const notifications = this.subject(); - const resp = {jqXHR: {responseJSON: {errors: ['First error', 'Second error']}}}; + const error = new AjaxError(['First error', 'Second error']); run(() => { - notifications.showAPIError(resp); + notifications.showAPIError(error); }); expect(notifications.get('notifications')).to.deep.equal([ @@ -198,26 +199,12 @@ describeModule( ]); }); - it('#showAPIError adds single json response message', function () { - const notifications = this.subject(); - const resp = {jqXHR: {responseJSON: {message: 'Single message'}}}; - - run(() => { - notifications.showAPIError(resp); - }); - - let notification = notifications.get('alerts.firstObject'); - expect(get(notification, 'message')).to.equal('Single message'); - expect(get(notification, 'status')).to.equal('alert'); - expect(get(notification, 'type')).to.equal('error'); - expect(get(notification, 'key')).to.equal('api-error'); - }); - it('#showAPIError displays default error text if response has no error/message', function () { const notifications = this.subject(); - const resp = {}; + const resp = false; run(() => { notifications.showAPIError(resp); }); + expect(notifications.get('content')).to.deep.equal([ {message: 'There was a problem on the server, please try again.', status: 'alert', type: 'error', key: 'api-error'} ]); @@ -252,6 +239,21 @@ describeModule( expect(notifications.get('alerts.firstObject.key')).to.equal('api-error'); }); + it('#showAPIError parses errors from ember-ajax correctly', function () { + const notifications = this.subject(); + const error = new InvalidError('Test Error'); + + run(() => { + notifications.showAPIError(error); + }); + + let notification = notifications.get('alerts.firstObject'); + expect(get(notification, 'message')).to.equal('Test Error'); + expect(get(notification, 'status')).to.equal('alert'); + expect(get(notification, 'type')).to.equal('error'); + expect(get(notification, 'key')).to.equal('api-error'); + }); + it('#displayDelayed moves delayed notifications into content', function () { const notifications = this.subject();