5.0.0 • Published 6 years ago

earbuds-http v5.0.0

Weekly downloads
5
License
UNLICENSED
Repository
bitbucket
Last release
6 years ago

Earbuds Http

An HTTP library to standardize ajax requests to a Transplace API.

Installation

npm install earbuds-http

earbuds-http isn't currently pre-compiled, so you may need to modify your webpack config to deal with it.

// webpack.base.conf.js

module.exports = {
  entry: [ /* ... */ ],
  resolve: [ /* ... */ ],
  module: {
    rules: {
      { /* eslint rules */ },
      { /* vue-loader rules */ },
      {
        /* babel rules */
        test: /\.js$/,
        loader: 'babel-loader',
        include: [
          resolve('src'),
          resolve('test'),
          // transpile all packages named earbuds{whatever}
          new RegExp(resolve(['node_modules', 'earbuds.*']), 'i'),
        ]
      }
    }
  }
};

Axios

earbuds-http is built on top of axios. Anything that axios does Earbuds-http should do, too.

earbuds-http adds caching and custom server response handling to base axios.

https://github.com/mzabriskie/axios

Usage

Basics

  • ebHttp.get(url, [options])
  • ebHttp.post(url, data, [options])

Common Options

  • params - the URL parameters to be sent with the request. Must be a plain object or a URLSearchParams object params: { ID: 12345 },

  • data - the data to be sent as the request body Only applicable for request methods 'PUT', 'POST', and 'PATCH' When no transformRequest is set, must be of one of the following types:

    • string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
    • Browser only: FormData, File, Blob
    • Node only: Stream, Buffer data: { firstName: 'Fred' },
  • cache - whether or not to cache the response. boolean. {cache: true}

  • cacheTimeToLive - how long the data should be cached. the value is milliseconds until the cache should expire {cacheTimeToLive: 5000}

  • updateCache - if true, the current cached value is ignored and the new value is saved in it's place {cacheTimeToLive: true}

More Options

see https://github.com/mzabriskie/axios#request-config

Extension Points

Extend earbuds-http by using axios's extension points (which were inspired by angular's $http module) https://github.com/mzabriskie/axios#interceptors

Handling the server response

earbuds-http correctly handles api entity responses from the server.

An ApiEntity response looks like this:

{
  success: true,
  errors: [ ],
  tid: null,
  data: {
    domainId: null,
    accountId: -1,
    accountName: null,
    // ...
  }
}

earbuds-http handles automatically handles the success flag. If true, the promise is resolved with the contents of the data field. Otherwise, the promise is rejected with a list of errors.

Currently the tid (transaction-id) field is not managed.

Handling an AuthenticationError response

Add a response interceptor to catch the AuthenticationError and handle it

// signInRequiredResponseInterceptor.js

import AuthenticationError from 'earbuds-http/src/errors/AuthenticationError';

// todo - confirm that this is the best generic login URL to try
const LOGON_URL_BEST_GUESS = process.env.NODE_ENV !== 'production'
	? '/security/baseLogon.do'
	: '/00-tms-logon-redirect.php';

function handleSignInRequired(error) {
	// Returning a non-resolving promise prevents the api request from resolving
	// at all. E.g. if this happens in a beforeEnter navigation guard in the
	// router, it will stop the page from loading while waiting for the redirect
	// to resolve.
	return new Promise(() => {
		if (error.response && error.response.data && error.response.data.redirect) {
			window.location.href = error.response.data.redirect;
		} else {
			console.warn('ERROR - SIGN IN REQUIRED', 'No redirect url provided. Trying best guess.', LOGON_URL_BEST_GUESS);
			window.location.href = LOGON_URL_BEST_GUESS;
		}
	});
}

export default function rejectedHandler(err) {
	if (err && (err instanceof AuthenticationError || err.name === 'AuthenticationError')) {
		return handleSignInRequired(err);
	}

	return err;
}
// BaseApi.js
import ebHttp from 'earbuds-http';
import combineUrls from 'axios/lib/helpers/combineURLs';
import signInRequiredResponseInterceptor from './signInRequiredResponseInterceptor';

const TMS_API_URL = '/tms/api/v1/';

class BaseTmsApi {
	constructor(url) {
		const baseUrl = combineUrls(TMS_API_URL, url);
		this.ebHttp = ebHttp.create({
			baseURL: baseUrl,
			headers: { 'Content-Type': 'application/json', Accept: 'application/json' }
		});

		// Handle sign-in required errors
		this.ebHttp.interceptors.response.use(null, signInRequiredResponseInterceptor);
	}
}

export { BaseTmsApi };
export default BaseTmsApi;

Caching

Cached calls

Both get and post calls can be cached.

var url = '/user/123/details';
var a = instance.get(url, {cache:true});
var b = instance.get(url, {cache:true});
var c = instance.get(url, {params: {includeAddress: true}, cache: true});
var d = instance.get(url, {params: {includeAddress: true}, cache: true});

a === b; // true
c === d; // true
a === c; // false. Although the url is the same, the options are not.

Subsequent calls to that url with the same options will used cached data instead of hitting the server.

Time To Live

You can automatically expire cached data after a specified number of milliseconds by including the cacheTimeToLive parameter.

var url = '/user/123/details';
var a = instance.get(url, {cache:true, cacheTimeToLive: 10000});

setTimeout(function () {
  var c = instance.get(url, {cache:true, cacheTimeToLive: 10000});
  a === c; // true
}, 5000);

setTimeout(function () {
  var d = instance.get(url, {cache:true, cacheTimeToLive: 10000});
  a === d; // false
}, 20000);

Example

// userApi.js

import ebHttp from 'earbuds-http';

const BASE_URL = './api/user';

export default class Api {
  _httpInstance;

  constructor(url) {
    if (!url) {
      throw new Error('url must be provided');
    }

    this._httpInstance = ebHttp.create({
      baseURL: path.join(BASE_URL, url)
    });
  }

  get http() {
    return this._httpInstance;
  }

  getSecurityContext() {
    // url -> ./api/user/securityContext/
    // cached for the entirety of the javascript session
    return this.http.get('/securityContext', { cache: true });
  }

  getUserDetail(userId) {
    // url -> ./api/user/userDetail/userId
    // not cached at all
    return this.http.get(`/userDetail/${userId}`, { cache: false });
  }

  getMapData(showMapPaths, mapStyle) {
    // url -> ./api/user/getMapData/userId?showMapPaths=true&mapStyle=SQUARE
    // cached for 10 seconds
    return this.http.get('/userDetail/${userId}', {
      params: {showMapPaths, mapStyle},
      cache: true,
      cacheTimeToLive: 10000
    });
  }
}
// consumer.js

import userApi from './userApi';

userApi.getSecurityContext()
  .then(securityContext => console.log(securityContext))
  .catch(errors => toastr.danger(errors.join(', ')));

// ...

Development

You can develop npm packages locally without having to push to test your changes

https://docs.npmjs.com/cli/link

  1. Git clone this repository
  2. In earbuds-http, $ npm link
  3. In a project using earbuds-http, $ npm link earbuds-http

Once you're done developing, use npm version to push your changes

https://docs.npmjs.com/cli/version

  1. In earbuds-http, make sure everything is committed
  2. In earbuds-http, $ npm version [major|minor|patch]
  3. In a project using earbuds-http,
    • first time install: $ npm install earbuds-http
    • upgrade: $ npm upgrade earbuds-http