Skip to content
This repository has been archived by the owner on Apr 4, 2022. It is now read-only.

Commit

Permalink
Imported Kinto.js api module to its own package.
Browse files Browse the repository at this point in the history
  • Loading branch information
n1k0 committed Feb 5, 2016
0 parents commit 87c681c
Show file tree
Hide file tree
Showing 16 changed files with 1,677 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .babelrc
@@ -0,0 +1,7 @@
{
"env": {
"development": {
"presets": ["es2015"]
}
}
}
22 changes: 22 additions & 0 deletions .eslintrc
@@ -0,0 +1,22 @@
{
"parser": "babel-eslint",
"rules": {
"comma-dangle": 0,
"curly": 2,
"indent": [2, 2],
"linebreak-style": [2, "unix"],
"no-console": 0,
"no-unused-vars": [2, {"vars": "all", "args": "none"}],
"no-var": 2,
"prefer-const": 1,
"quotes": [2, "double"],
"semi": [2, "always"]
},
"env": {
"es6": true,
"browser": true,
"node": true
},
"extends": "eslint:recommended",
"root": true
}
7 changes: 7 additions & 0 deletions .gitignore
@@ -0,0 +1,7 @@
/.venv
/dist
/lib
/npm-debug.log
/coverage
/esdoc
/node_modules
2 changes: 2 additions & 0 deletions .npmignore
@@ -0,0 +1,2 @@
/.venv
/src
22 changes: 22 additions & 0 deletions .travis.yml
@@ -0,0 +1,22 @@
language:
- node_js
- python
node_js:
- "4"
python:
- "2.7"
before_install:
- export PATH=$HOME/.local/bin:$PATH
- pip install --user `whoami` virtualenv
- virtualenv .env
- .env/bin/pip install kinto==1.11.0
- export KINTO_PSERVE_EXECUTABLE=.env/bin/pserve
env:
- ACTION=test
- ACTION="run lint"
- ACTION="run dist" # ensures building dist files doesn't break
- ACTION="run build-demo" # ensures building the demo doesn't break
script:
- npm $ACTION
after_script:
- npm run report-coverage
13 changes: 13 additions & 0 deletions LICENSE
@@ -0,0 +1,13 @@
Copyright 2015 - Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
21 changes: 21 additions & 0 deletions esdoc.json
@@ -0,0 +1,21 @@
{
"source": "./src",
"destination": "./esdoc",
"test": {
"type": "mocha",
"source": "./test"
},
"plugins": [
{
"name": "esdoc-importpath-plugin",
"option": {
"replaces": [
{"from": "^src", "to": "lib"}
]
}
},
{
"name": "esdoc-es7-plugin"
}
]
}
62 changes: 62 additions & 0 deletions package.json
@@ -0,0 +1,62 @@
{
"name": "kinto-api.js",
"version": "0.0.1",
"description": "JavaScript HTTP client for the Kinto API.",
"main": "lib/index.js",
"scripts": {
"build": "babel -d lib/ src/",
"dist": "mkdir -p dist && rm -f dist/*.* && npm run dist-dev && npm run dist-prod && npm run dist-noshim",
"dist-dev": "browserify -s KintoApi -d -e src/index.js -o dist/kinto-api.js-$npm_package_version.js -t [ babelify --sourceMapRelative . ]",
"dist-noshim": "browserify -s KintoApi -g uglifyify --ignore isomorphic-fetch --ignore babel-polyfill -e src/index.js -o dist/kinto-api.js-$npm_package_version.noshim.js -t [ babelify --sourceMapRelative . ]",
"dist-prod": "browserify -s KintoApi -g uglifyify -e src/index.js -o dist/kinto-api.js-$npm_package_version.min.js -t [ babelify --sourceMapRelative . ]",
"report-coverage": "npm run test-cover && ./node_modules/coveralls/bin/coveralls.js < ./coverage/lcov.info",
"tdd": "babel-node node_modules/.bin/_mocha --watch 'test/**/*_test.js'",
"test": "npm run lint && npm run test-nocover",
"test-cover": "babel-node node_modules/.bin/babel-istanbul cover --report text $npm_package_config_ISTANBUL_OPTS node_modules/.bin/_mocha -- 'test/**/*_test.js'",
"test-cover-html": "babel-node node_modules/.bin/babel-istanbul cover --report html $npm_package_config_ISTANBUL_OPTS node_modules/.bin/_mocha -- 'test/**/*_test.js' && open coverage/index.html",
"test-nocover": "babel-node node_modules/.bin/_mocha 'test/**/*_test.js'",
"lint": "eslint src test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Kinto/kinto-api.js.git"
},
"keywords": [
"kinto",
"http",
"client",
"api"
],
"author": "Mozilla <storage-team@mozilla.com>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/Kinto/kinto-api.js/issues"
},
"homepage": "https://github.com/Kinto/kinto-api.js#readme",
"dependencies": {
"isomorphic-fetch": "^2.2.1"
},
"devDependencies": {
"babel": "^6.3.26",
"babel-cli": "^6.4.5",
"babel-core": "^6.3.26",
"babel-eslint": "^5.0.0-beta6",
"babel-istanbul": "^0.6.0",
"babel-loader": "^6.2.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.4.0",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.3.13",
"babelify": "^7.2.0",
"browserify": "^13.0.0",
"chai": "^3.4.1",
"chai-as-promised": "^5.0.0",
"coveralls": "^2.11.6",
"esdoc": "^0.4.0",
"esdoc-es7-plugin": "0.0.3",
"esdoc-importpath-plugin": "0.0.1",
"eslint": "^1.2.0",
"mocha": "^2.3.4",
"sinon": "^1.17.2",
"uglifyify": "^3.0.1"
}
}
25 changes: 25 additions & 0 deletions src/errors.js
@@ -0,0 +1,25 @@
/**
* Kinto server error code descriptors.
* @type {Object}
*/
export default {
104: "Missing Authorization Token",
105: "Invalid Authorization Token",
106: "Request body was not valid JSON",
107: "Invalid request parameter",
108: "Missing request parameter",
109: "Invalid posted data",
110: "Invalid Token / id",
111: "Missing Token / id",
112: "Content-Length header was not provided",
113: "Request body too large",
114: "Resource was modified meanwhile",
115: "Method not allowed on this end point",
116: "Requested version not available on this server",
117: "Client has sent too many requests",
121: "Resource access is forbidden for this user",
122: "Another resource violates constraint",
201: "Service Temporary unavailable due to high load",
202: "Service deprecated",
999: "Internal Server Error",
};
173 changes: 173 additions & 0 deletions src/http.js
@@ -0,0 +1,173 @@
"use strict";

import ERROR_CODES from "./errors.js";

/**
* Enhanced HTTP client for the Kinto protocol.
*/
export default class HTTP {
/**
* Default HTTP request headers applied to each outgoing request.
*
* @type {Object}
*/
static get DEFAULT_REQUEST_HEADERS() {
return {
"Accept": "application/json",
"Content-Type": "application/json",
};
}

/**
* Default options.
*
* @type {Object}
*/
static get defaultOptions() {
return {timeout: 5000, requestMode: "cors"};
}

/**
* Constructor.
*
* Options:
* - {Number} timeout The request timeout in ms (default: `5000`).
* - {String} requestMode The HTTP request mode (default: `"cors"`).
*
* @param {EventEmitter} events The event handler.
* @param {Object} options The options object.
*/
constructor(events, options={}) {
// public properties
/**
* The event emitter instance.
* @type {EventEmitter}
*/
if (!events) {
throw new Error("No events handler provided");
}
this.events = events;

options = Object.assign({}, HTTP.defaultOptions, options);

/**
* The request mode.
* @see https://fetch.spec.whatwg.org/#requestmode
* @type {String}
*/
this.requestMode = options.requestMode;

/**
* The request timeout.
* @type {Number}
*/
this.timeout = options.timeout;
}

/**
* Performs an HTTP request to the Kinto server.
*
* Options:
* - `{Object} headers` The request headers object (default: {})
*
* Resolves with an objet containing the following HTTP response properties:
* - `{Number} status` The HTTP status code.
* - `{Object} json` The JSON response body.
* - `{Headers} headers` The response headers object; see the ES6 fetch() spec.
*
* @param {String} url The URL.
* @param {Object} options The fetch() options object.
* @return {Promise}
*/
request(url, options={headers:{}}) {
let response, status, statusText, headers, _timeoutId, hasTimedout;
// Ensure default request headers are always set
options.headers = Object.assign({}, HTTP.DEFAULT_REQUEST_HEADERS, options.headers);
options.mode = this.requestMode;
return new Promise((resolve, reject) => {
_timeoutId = setTimeout(() => {
hasTimedout = true;
reject(new Error("Request timeout."));
}, this.timeout);
fetch(url, options) .then(res => {
if (!hasTimedout) {
clearTimeout(_timeoutId);
resolve(res);
}
}).catch(err => {
if (!hasTimedout) {
clearTimeout(_timeoutId);
reject(err);
}
});
})
.then(res => {
response = res;
headers = res.headers;
status = res.status;
statusText = res.statusText;
this._checkForDeprecationHeader(headers);
this._checkForBackoffHeader(status, headers);
return res.text();
})
// Check if we have a body; if so parse it as JSON.
.then(text => {
if (text.length === 0) {
return null;
}
// Note: we can't consume the response body twice.
return JSON.parse(text);
})
.catch(err => {
const error = new Error(`HTTP ${status || 0}; ${err}`);
error.response = response;
error.stack = err.stack;
throw error;
})
.then(json => {
if (json && status >= 400) {
let message = `HTTP ${status}; `;
if (json.errno && json.errno in ERROR_CODES) {
message += ERROR_CODES[json.errno];
if (json.message) {
message += `: ${json.message}`;
}
} else {
message += statusText || "";
}
const error = new Error(message.trim());
error.response = response;
error.data = json;
throw error;
}
return {status, json, headers};
});
}

_checkForDeprecationHeader(headers) {
const alertHeader = headers.get("Alert");
if (!alertHeader) {
return;
}
let alert;
try {
alert = JSON.parse(alertHeader);
} catch(err) {
console.warn("Unable to parse Alert header message", alertHeader);
return;
}
console.warn(alert.message, alert.url);
this.events.emit("deprecated", alert);
}

_checkForBackoffHeader(status, headers) {
let backoffMs;
const backoffSeconds = parseInt(headers.get("Backoff"), 10);
if (backoffSeconds > 0) {
backoffMs = (new Date().getTime()) + (backoffSeconds * 1000);
} else {
backoffMs = 0;
}
this.events.emit("backoff", backoffMs);
}
}

0 comments on commit 87c681c

Please sign in to comment.