Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

compago-ajax

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

compago-ajax - npm Package Compare versions

Comparing version 1.0.0 to 1.0.1

LICENSE

24

package.json
{
"name": "compago-ajax",
"version": "1.0.0",
"description": "",
"version": "1.0.1",
"description": "An AJAX storage engine for the Compago framework.",
"main": "dist/node/index.js",
"keywords": [
"ajax"
"ajax",
"storage",
"compago"
],

@@ -18,3 +20,3 @@ "repository": {

"scripts": {
"build": "npm run clean && npm run build-node",
"lint": "eslint src/index.js tests/unit.test.js",
"report-coverage": "cat ./coverage/lcov.info | codecov",

@@ -33,3 +35,3 @@ "test": "babel-node ./node_modules/istanbul/lib/cli cover node_modules/mocha/bin/_mocha -- -R spec tests/unit.test.js"

"dependencies": {
"compago-listener": "npm:compago-listener@^1.0.2",
"compago-listener": "npm:compago-listener@^1.0.4",
"compago-model": "npm:compago-model@^1.0.1"

@@ -43,2 +45,9 @@ }

},
"eslintConfig": {
"extends": "airbnb",
"rules": {
"no-nested-ternary": 1,
"eqeqeq": [2, "smart"]
}
},
"devDependencies": {

@@ -49,2 +58,5 @@ "babel-cli": "^6.3.17",

"compago-view": "^1.0.0",
"eslint": "^1.10.3",
"eslint-config-airbnb": "^2.1.1",
"eslint-plugin-react": "^3.13.1",
"expect": "^1.13.4",

@@ -56,5 +68,5 @@ "istanbul": "^1.0.0-alpha.2",

"dependencies": {
"compago-listener": "^1.0.2",
"compago-listener": "^1.0.4",
"compago-model": "^1.0.1"
}
}
import { Listener } from 'compago-listener';
import Model from "compago-model";
import Model from 'compago-model';
export default class Ajax {
/**
* Facilitates interaction with a REST server through the XMLHttpRequest API.
*
* @mixes Listener
*/
class Ajax {
/**
* @param {Object} [options]
* @param {string} [options.url] The URL for requests, by default uses the window's origin.
* @example
* model.storage = new Ajax('http://example.com/post/');
* @param {string} [options.url] the URL for requests, by default uses the window's origin
*/

@@ -20,4 +23,4 @@ constructor(options = {}) {

*
* @param {Model} model The model to be checked.
* @return {boolean}
* @param {Model} model the model to be checked
* @return {boolean} True if the model is already stored on the server
*/

@@ -31,12 +34,12 @@ static isStored(model) {

*
* @param {string} method A method name to execute.
* Internal method names are mapped to ajax methods in `this.methods`.
* @param {(Model|Collection)} model A model or a collection to by synchronized.
* @param {string} method a method name to execute.
* Internal method names are mapped to ajax methods in `Ajax.methods`.
* @param {(Model|Collection)} model a model or a collection to by synchronized
* @param {Object} options
* @param {Function} options.success The function called if the request succeeds.
* @param {Function} options.error The function called if the server returns an error.
* @param {string} [options.url] A specific url for the request, in case it's different from the default url of the storage
* @param {boolean} [options.silent] to avoid firing any events.
* @param {Boolean} [options.patch] to send only changed attributes (if present) using `PATCH` method
* @return {Object} Returns an XMLHttpRequest object.
* @param {Function} options.success the function called if the request succeeds
* @param {Function} options.error the function called if the server returns an error
* @param {string} [options.url] a specific url for the request, in case it's different from the default url of the storage
* @param {boolean} [options.silent] whether to avoid firing any events
* @param {Boolean} [options.patch] whether to send only changed attributes (if present) using `PATCH` method
* @return {XMLHttpRequest} an request object
*/

@@ -48,6 +51,6 @@ sync(method, model, options) {

let {url=this.url, patch, silent, success, error} = options;
const { patch, silent, success, error } = options;
let { url = this.url } = options;
const xhr = new XMLHttpRequest();
const isStored = this.constructor.isStored(model);
let status, data;
const self = this;

@@ -76,2 +79,3 @@ const changes = (patch && model.changes) ? model.changes : false;

let data;
if (method === 'write') {

@@ -82,7 +86,8 @@ xhr.setRequestHeader('Content-Type', 'application/json');

xhr.onreadystatechange = function() {
xhr.onreadystatechange = () => {
const status = xhr.status;
if (xhr.readyState === 4) {
if (!silent) self.emit('response', options);
const response = Ajax.isJSONString(xhr.responseText) ? JSON.parse(xhr.responseText) : xhr.responseText;
if (((status = xhr.status) >= 200) && (status < 300 || status === 304)) {
const response = Ajax._isJSONString(xhr.responseText) ? JSON.parse(xhr.responseText) : xhr.responseText;
if ((status >= 200) && (status < 300 || status === 304)) {
success(response);

@@ -105,4 +110,4 @@ } else {

* @param {Object} [options]
* @param {boolean} [options.silent] Avoids firing `dispose` event.
* @return {Ajax}
* @param {boolean} [options.silent] whether to avoid emitting the `dispose` event.
* @return {this}
*/

@@ -118,6 +123,6 @@ dispose(options = {}) {

*
* @param {*} val The value to be checked.
* @return {Boolean}
* @param {*} val the value to be checked
* @return {Boolean} True if the value is a valid JSON string
*/
static isJSONString(val) {
static _isJSONString(val) {
try {

@@ -133,3 +138,3 @@ JSON.parse(val);

/**
* The map translating internal method names to the HTTP methods.
* The map translating internal method names to their respective HTTP methods.
*/

@@ -141,3 +146,5 @@ Ajax.methods = {

'update': 'PUT',
'patch': 'PATCH'
};
'patch': 'PATCH',
};
export default Ajax;

@@ -0,1 +1,3 @@

/* eslint-env node, mocha */
import Ajax from '../src/index';

@@ -7,41 +9,40 @@ import Model from 'compago-model';

describe('Ajax', ()=> {
describe('Ajax', () => {
let storage;
beforeEach(()=> {
storage = new Ajax({url: 'http://example.com/posts'});
beforeEach(() => {
storage = new Ajax({ url: 'http://example.com/posts' });
});
describe('constructor', ()=> {
it('creates an ajax storage controller', ()=> {
describe('constructor', () => {
it('creates an ajax storage controller', () => {
expect(storage.url).toBe('http://example.com/posts');
});
});
describe('isStored', ()=> {
it('checks whether a model has been already persisted on the server', ()=> {
const model = {id: 100};
describe('isStored', () => {
it('checks whether a model has been already persisted on the server', () => {
const model = { id: 100 };
expect(Ajax.isStored(model)).toBe(true);
expect(Ajax.isStored({})).toBe(false);
});
});
describe('sync', ()=> {
let xhr, model, options, requests;
describe('sync', () => {
let xhr;
let model;
let options;
let requests;
beforeEach(()=> {
beforeEach(() => {
xhr = sinon.useFakeXMLHttpRequest();
global.XMLHttpRequest = xhr;
requests = [];
/* eslint no-shadow: 1*/
xhr.onCreate = (xhr) => {
requests.push(xhr)
requests.push(xhr);
};
model = {id: 42};
model.toJSON = ()=> {
return model
model = { id: 42 };
model.toJSON = () => {
return model;
};

@@ -53,7 +54,7 @@ options = {};

afterEach(()=> {
afterEach(() => {
xhr.restore();
});
it('returns `false` if no valid operation type is provided', ()=> {
it('returns `false` if no valid operation type is provided', () => {
expect(storage.sync()).toBe(false);

@@ -63,10 +64,10 @@ expect(requests.length).toBe(0);

it('parses JSON responses', ()=> {
it('parses JSON responses', () => {
storage.sync('read', model, options);
requests[0].respond(200, {'Content-Type': 'application/json'}, '{"name":"Arthur"}');
expect(options.success.calls[0].arguments).toEqual([{name: 'Arthur'}]);
requests[0].respond(200, { 'Content-Type': 'application/json' }, '{"name":"Arthur"}');
expect(options.success.calls[0].arguments).toEqual([{ name: 'Arthur' }]);
expect(options.error).toNotHaveBeenCalled();
});
it('retrieves a model', ()=> {
it('retrieves a model', () => {
storage.sync('read', model, options);

@@ -76,3 +77,3 @@ expect(requests.length).toBe(1);

expect(options.success).toNotHaveBeenCalled();
requests[0].respond(200, {'Content-Type': 'text/plain'}, "Ok");
requests[0].respond(200, { 'Content-Type': 'text/plain' }, 'Ok');
expect(options.success).toHaveBeenCalledWith('Ok');

@@ -82,4 +83,4 @@ expect(options.error).toNotHaveBeenCalled();

it('invokes `option.error` when attempts to retrieve an unsaved model', ()=> {
let m = new Model();
it('invokes `option.error` when attempts to retrieve an unsaved model', () => {
const m = new Model();
storage.sync('read', m, options);

@@ -90,4 +91,4 @@ expect(options.error).toHaveBeenCalled();

it('retrieves all models in a collection', ()=> {
let collection = {};
it('retrieves all models in a collection', () => {
const collection = {};
storage.sync('read', collection, options);

@@ -97,3 +98,3 @@ expect(requests[0].url).toBe('http://example.com/posts');

expect(options.success).toNotHaveBeenCalled();
requests[0].respond(200, {'Content-Type': 'text/plain'}, "Ok");
requests[0].respond(200, { 'Content-Type': 'text/plain' }, 'Ok');
expect(options.success).toHaveBeenCalledWith('Ok');

@@ -103,3 +104,3 @@ expect(options.error).toNotHaveBeenCalled();

it('saves a model', ()=> {
it('saves a model', () => {
delete model.id;

@@ -113,3 +114,3 @@ model.name = 'Arthur';

it('updates a model', ()=> {
it('updates a model', () => {
storage.sync('write', model, options);

@@ -121,4 +122,4 @@ expect(requests[0].url).toBe('http://example.com/posts/42');

it('sends only changes if `patch:true`', ()=> {
model.changes = {name: 'Arthur'};
it('sends only changes if `patch:true`', () => {
model.changes = { name: 'Arthur' };
options.patch = true;

@@ -131,4 +132,3 @@ storage.sync('write', model, options);

it('sends all attributes if no changes found despite `patch:true`', ()=> {
it('sends all attributes if no changes found despite `patch:true`', () => {
options.patch = true;

@@ -142,3 +142,3 @@ storage.sync('write', model, options);

it('deletes a single model', ()=> {
it('deletes a single model', () => {
storage.sync('erase', model, options);

@@ -149,3 +149,3 @@ expect(requests[0].url).toBe('http://example.com/posts/42');

it('invokes `option.success` when attempts to delete an unsaved model', ()=> {
it('invokes `option.success` when attempts to delete an unsaved model', () => {
delete model.id;

@@ -157,3 +157,3 @@ storage.sync('erase', model, options);

it('invokes `options.error` if a error is returned by the server', ()=> {
it('invokes `options.error` if a error is returned by the server', () => {
storage.sync('read', model, options);

@@ -163,3 +163,3 @@ expect(requests[0].url).toBe('http://example.com/posts/42');

expect(options.success).toNotHaveBeenCalled();
requests[0].respond(404, {'Content-Type': 'text/plain'}, 'Not Ok');
requests[0].respond(404, { 'Content-Type': 'text/plain' }, 'Not Ok');
expect(options.success).toNotHaveBeenCalled();

@@ -169,3 +169,3 @@ expect(options.error).toHaveBeenCalledWith('Not Ok');

it('fires `before:request`, `request`, and `response` events unless `silent:true`', ()=> {
it('fires `before:request`, `request`, and `response` events unless `silent:true`', () => {
storage.someMethod = expect.createSpy();

@@ -178,3 +178,3 @@ storage.otherMethod = expect.createSpy();

storage.sync('read', model, options);
requests[0].respond(200, {'Content-Type': 'text/plain'}, 'Not Ok');
requests[0].respond(200, { 'Content-Type': 'text/plain' }, 'Not Ok');
expect(storage.someMethod).toHaveBeenCalled();

@@ -185,3 +185,3 @@ expect(storage.otherMethod).toHaveBeenCalled();

it('does not fire events if `silent:true`', ()=> {
it('does not fire events if `silent:true`', () => {
storage.someMethod = expect.createSpy();

@@ -195,22 +195,19 @@ storage.otherMethod = expect.createSpy();

storage.sync('read', model, options);
requests[0].respond(200, {'Content-Type': 'text/plain'}, 'Not Ok');
requests[0].respond(200, { 'Content-Type': 'text/plain' }, 'Not Ok');
expect(storage.someMethod).toNotHaveBeenCalled();
expect(storage.otherMethod).toNotHaveBeenCalled();
expect(storage.anotherMethod).toNotHaveBeenCalled();
})
});
});
describe('isJSONString', ()=> {
it('checks whether the passed argument is a JSON string or not', ()=> {
expect(Ajax.isJSONString('{"name":"Arthur"}')).toBe(true);
expect(Ajax.isJSONString('Arthur')).toBe(false);
describe('_isJSONString', () => {
it('checks whether the passed argument is a JSON string or not', () => {
expect(Ajax._isJSONString('{"name":"Arthur"}')).toBe(true);
expect(Ajax._isJSONString('Arthur')).toBe(false);
});
});
describe('dispose', ()=> {
it("prepares the storage controller to be disposed", ()=> {
storage.on(storage, 'dispose', ()=> {
describe('dispose', () => {
it('prepares the storage controller to be disposed', () => {
storage.on(storage, 'dispose', () => {
});

@@ -223,4 +220,3 @@ expect(storage._events).toExist();

});
it('fires `dispose` event unless `silent:true`', ()=> {
it('fires `dispose` event unless `silent:true`', () => {
storage.someMethod = expect.createSpy();

@@ -230,9 +226,8 @@ storage.on(storage, 'dispose', storage.someMethod);

expect(storage.someMethod).toHaveBeenCalled();
let otherMethod = expect.createSpy();
const otherMethod = expect.createSpy();
storage.on(storage, 'dispose', storage.otherMethod);
storage.dispose({silent: true});
storage.dispose({ silent: true });
expect(otherMethod).toNotHaveBeenCalled();
});
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc