From e7b19a58c1d8fee346b6aad5f21fae74a91d9cca Mon Sep 17 00:00:00 2001 From: Christopher Gammie Date: Mon, 31 Oct 2016 11:47:52 +0000 Subject: [PATCH] [DOC ds-extended-errors] Add docs for extended errors --- addon/adapters/errors.js | 173 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/addon/adapters/errors.js b/addon/adapters/errors.js index 23cbed79dcb..5594ccd8920 100644 --- a/addon/adapters/errors.js +++ b/addon/adapters/errors.js @@ -9,6 +9,69 @@ const SOURCE_POINTER_PRIMARY_REGEXP = /^\/?data/; const PRIMARY_ATTRIBUTE_KEY = 'base'; /** + A `DS.AdapterError` is used by an adapter to signal that an error occurred + during a request to an external API. It indicates a generic error, and + subclasses are used to indicate specific error states. The following + subclasses are provided: + + - `DS.InvalidError` + - `DS.TimeoutError` + - `DS.AbortError` + - `DS.UnauthorizedError` + - `DS.ForbiddenError` + - `DS.NotFoundError` + - `DS.ConflictError` + - `DS.ServerError` + + To create a custom error to signal a specific error state in communicating + with an external API, extend the `DS.AdapterError`. For example if the + external API exclusively used HTTP `503 Service Unavailable` to indicate + it was closed for maintenance: + + ```app/adapters/maintenance-error.js + import DS from 'ember-data'; + + export default DS.AdapterError.extend({ message: "Down for maintenance." }); + ``` + + This error would then be returned by an adapter's `handleResponse` method: + + ```app/adapters/application.js + import DS from 'ember-data'; + import MaintenanceError from './maintenance-error'; + + export default DS.JSONAPIAdapter.extend({ + handleResponse(status) { + if (503 === status) { + return new MaintenanceError(); + } + + return this._super(...arguments); + } + }); + ``` + + And can then be detected in an application and used to send the user to an + `under-maintenance` route: + + ```app/routes/application.js + import Ember from 'ember'; + import MaintenanceError from '../adapters/maintenance-error'; + + export default Ember.Route.extend({ + actions: { + error(error, transition) { + if (error instanceof MaintenanceError) { + this.transitionTo('under-maintenance'); + return; + } + + // ...other error handling logic + } + } + }); + ``` + @class AdapterError @namespace DS */ @@ -117,6 +180,34 @@ export const InvalidError = extend(AdapterError, 'The adapter rejected the commit because it was invalid'); /** + A `DS.TimeoutError` is used by an adapter to signal that a request + to the external API has timed out. I.e. no response was received from + the external API within an allowed time period. + + An example use case would be to warn the user to check their internet + connection if an adapter operation has timed out: + + ```app/routes/application.js + import Ember from 'ember'; + import DS from 'ember-data'; + + const { TimeoutError } = DS; + + export default Ember.Route.extend({ + actions: { + error(error, transition) { + if (error instanceof TimeoutError) { + // alert the user + alert('Are you still connected to the internet?'); + return; + } + + // ...other error handling logic + } + } + }); + ``` + @class TimeoutError @namespace DS */ @@ -124,6 +215,11 @@ export const TimeoutError = extend(AdapterError, 'The adapter operation timed out'); /** + A `DS.AbortError` is used by an adapter to signal that a request to + the external API was aborted. For example, this can occur if the user + navigates away from the current page after a request to the external API + has been initiated but before a response has been received. + @class AbortError @namespace DS */ @@ -131,6 +227,35 @@ export const AbortError = extend(AdapterError, 'The adapter operation was aborted'); /** + A `DS.UnauthorizedError` equates to a HTTP `401 Unauthorized` response + status. It is used by an adapter to signal that a request to the external + API was rejected because authorization is required and has failed or has not + yet been provided. + + An example use case would be to redirect the user to a log in route if a + request is unauthorized: + + ```app/routes/application.js + import Ember from 'ember'; + import DS from 'ember-data'; + + const { UnauthorizedError } = DS; + + export default Ember.Route.extend({ + actions: { + error(error, transition) { + if (error instanceof UnauthorizedError) { + // go to the sign in route + this.transitionTo('login'); + return; + } + + // ...other error handling logic + } + } + }); + ``` + @class UnauthorizedError @namespace DS */ @@ -138,6 +263,12 @@ export const UnauthorizedError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter operation is unauthorized') : null; /** + A `DS.ForbiddenError` equates to a HTTP `403 Forbidden` response status. + It is used by an adapter to signal that a request to the external API was + valid but the server is refusing to respond to it. If authorization was + provided and is valid, then the authenticated user does not have the + necessary permissions for the request. + @class ForbiddenError @namespace DS */ @@ -145,6 +276,38 @@ export const ForbiddenError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter operation is forbidden') : null; /** + A `DS.NotFoundError` equates to a HTTP `404 Not Found` response status. + It is used by an adapter to signal that a request to the external API + was rejected because the resource could not be found on the API. + + An example use case would be to detect if the user has entered a route + for a specific model that does not exist. For example: + + ```app/routes/post.js + import Ember from 'ember'; + import DS from 'ember-data'; + + const { NotFoundError } = DS; + + export default Ember.Route.extend({ + model(params) { + return this.get('store').findRecord('post', params.post_id); + }, + + actions: { + error(error, transition) { + if (error instanceof NotFoundError) { + // redirect to a list of all posts instead + this.transitionTo('posts'); + } else { + // otherwise let the error bubble + return true; + } + } + } + }); + ``` + @class NotFoundError @namespace DS */ @@ -152,6 +315,12 @@ export const NotFoundError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter could not find the resource') : null; /** + A `DS.ConflictError` equates to a HTTP `409 Conflict` response status. + It is used by an adapter to indicate that the request could not be processed + because of a conflict in the request. An example scenario would be when + creating a record with a client generated id but that id is already known + to the external API. + @class ConflictError @namespace DS */ @@ -159,6 +328,10 @@ export const ConflictError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter operation failed due to a conflict') : null; /** + A `DS.ServerError` equates to a HTTP `500 Internal Server Error` response + status. It is used by the adapter to indicate that a request has failed + because of an error in the external API. + @class ServerError @namespace DS */