diff --git a/spec/adapters/ajax/create-spec.js b/spec/adapters/ajax/create-spec.js index 18bc4eb..f949d24 100644 --- a/spec/adapters/ajax/create-spec.js +++ b/spec/adapters/ajax/create-spec.js @@ -1,2 +1,34 @@ -// import test from "tape"; -// import Store from "../../../src/store"; +import test from "tape"; +import sinon from "sinon"; +import Store from "../../../src/store"; + +test.skip("create must post a resource to the server and add it to the store", function (t) { + var server = sinon.fakeServer.create({ autoRespond: true }); + var adapter = new Store.AjaxAdapter(); + var store = new Store(adapter); + t.plan(2); + t.timeoutAfter(1000); + store.define("products", { + title: Store.attr() + }); + server.respondWith("POST", "/products", [ + 201, + { + "Content-Type": "application/vnd.api+json" + }, + JSON.stringify({ + data: { + type: "products", + id: "9", + attributes: { + title: "My Book" + } + } + }) + ]); + store.create("products", { title: "My Book" }, function (product) { + t.equal(product.title, "My Book"); + t.equal(store.find("products", "9").title, "My Book"); + }); + server.restore(); +}); diff --git a/spec/adapters/ajax/load-spec.js b/spec/adapters/ajax/load-spec.js index 18bc4eb..0f52319 100644 --- a/spec/adapters/ajax/load-spec.js +++ b/spec/adapters/ajax/load-spec.js @@ -1,2 +1,82 @@ -// import test from "tape"; -// import Store from "../../../src/store"; +import test from "tape"; +import sinon from "sinon"; +import Store from "../../../src/store"; + +test("load must fetch a single resource from the server and add it to the store", function (t) { + var server = sinon.fakeServer.create({ autoRespond: true }); + var adapter = new Store.AjaxAdapter(); + var store = new Store(adapter); + t.plan(2); + t.timeoutAfter(1000); + store.define("products", { + title: Store.attr() + }); + server.respondWith("GET", "/products/12", [ + 200, + { + "Content-Type": "application/vnd.api+json" + }, + JSON.stringify({ + data: { + type: "products", + id: "12", + attributes: { + title: "An Awesome Book" + } + } + }) + ]); + store.load("products", "12", function (product) { + t.equal(product.title, "An Awesome Book"); + t.equal(store.find("products", "12").title, "An Awesome Book"); + }); + server.restore(); +}); + +test("load must fetch a collection of resources from the server and add them to the store", function (t) { + var server = sinon.fakeServer.create({ autoRespond: true }); + var adapter = new Store.AjaxAdapter(); + var store = new Store(adapter); + t.plan(3); + t.timeoutAfter(1000); + store.define("products", { + title: Store.attr() + }); + server.respondWith("GET", "/products", [ + 200, + { + "Content-Type": "application/vnd.api+json" + }, + JSON.stringify({ + data: [ + { + type: "products", + id: "2", + attributes: { + title: "A Book" + } + }, + { + type: "products", + id: "4", + attributes: { + title: "B Book" + } + }, + { + type: "products", + id: "7", + attributes: { + title: "C Book" + } + } + ] + }) + ]); + store.load("products", function (products) { + t.equal(products.length, 3); + t.equal(store.find("products").length, 3); + t.deepEqual(store.find("products").map(a => a.title).sort(), [ "A Book", "B Book", "C Book" ]); + }); + server.restore(); +}); diff --git a/spec/store/clud/create-spec.js b/spec/store/clud/create-spec.js index 05c614b..cbd6b2c 100644 --- a/spec/store/clud/create-spec.js +++ b/spec/store/clud/create-spec.js @@ -1,4 +1,5 @@ import test from "tape"; +import sinon from "sinon"; import Store from "../../../src/store"; test("create must throw an error if it is called when there isn't an adapter", function (t) { @@ -8,3 +9,15 @@ test("create must throw an error if it is called when there isn't an adapter", f store.create(); }, /Adapter missing\. Specify an adapter when creating the store: `var store = new Store\(adapter\);`/); }); + +test("create must call the create method prodvided by the adapter", function (t) { + var adatper = { create: sinon.spy() }; + var store = new Store(adatper); + var a = {}; + var cb = function () {}; + t.plan(2); + t.doesNotThrow(function () { + store.create("foo", a, cb); + }, "should not throw an error"); + t.ok(adatper.create.calledWith(store, "foo", a, cb), "should call adapter with the same params"); +}); diff --git a/spec/store/clud/destroy-spec.js b/spec/store/clud/destroy-spec.js index 3c7eb16..02fea76 100644 --- a/spec/store/clud/destroy-spec.js +++ b/spec/store/clud/destroy-spec.js @@ -1,4 +1,5 @@ import test from "tape"; +import sinon from "sinon"; import Store from "../../../src/store"; test("destroy must throw an error if it is called when there isn't an adapter", function (t) { @@ -8,3 +9,14 @@ test("destroy must throw an error if it is called when there isn't an adapter", store.destroy(); }, /Adapter missing\. Specify an adapter when creating the store: `var store = new Store\(adapter\);`/); }); + +test("destroy must call the destroy method prodvided by the adapter", function (t) { + var adatper = { destroy: sinon.spy() }; + var store = new Store(adatper); + var cb = function () {}; + t.plan(2); + t.doesNotThrow(function () { + store.destroy("foo", "1", cb); + }, "should not throw an error"); + t.ok(adatper.destroy.calledWith(store, "foo", "1", cb), "should call adapter with the same params"); +}); diff --git a/spec/store/clud/load-spec.js b/spec/store/clud/load-spec.js index 0aac5a7..92153ce 100644 --- a/spec/store/clud/load-spec.js +++ b/spec/store/clud/load-spec.js @@ -1,4 +1,5 @@ import test from "tape"; +import sinon from "sinon"; import Store from "../../../src/store"; test("load must throw an error if it is called when there isn't an adapter", function (t) { @@ -8,3 +9,14 @@ test("load must throw an error if it is called when there isn't an adapter", fun store.load(); }, /Adapter missing\. Specify an adapter when creating the store: `var store = new Store\(adapter\);`/); }); + +test("load must call the load method prodvided by the adapter", function (t) { + var adatper = { load: sinon.spy() }; + var store = new Store(adatper); + var cb = function () {}; + t.plan(2); + t.doesNotThrow(function () { + store.load("foo", "1", cb); + }, "should not throw an error"); + t.ok(adatper.load.calledWith(store, "foo", "1", cb), "should call adapter with the same params"); +}); diff --git a/spec/store/clud/update-spec.js b/spec/store/clud/update-spec.js index d2d2e18..2ffaf97 100644 --- a/spec/store/clud/update-spec.js +++ b/spec/store/clud/update-spec.js @@ -1,4 +1,5 @@ import test from "tape"; +import sinon from "sinon"; import Store from "../../../src/store"; test("update must throw an error if update is called when there isn't an adapter", function (t) { @@ -8,3 +9,15 @@ test("update must throw an error if update is called when there isn't an adapter store.update(); }, /Adapter missing\. Specify an adapter when creating the store: `var store = new Store\(adapter\);`/); }); + +test("update must call the update method prodvided by the adapter", function (t) { + var adatper = { update: sinon.spy() }; + var store = new Store(adatper); + var a = {}; + var cb = function () {}; + t.plan(2); + t.doesNotThrow(function () { + store.update("foo", "1", a, cb); + }, "should not throw an error"); + t.ok(adatper.update.calledWith(store, "foo", "1", a, cb), "should call adapter with the same params"); +}); diff --git a/src/ajax-adapter.js b/src/ajax-adapter.js new file mode 100644 index 0000000..bfe48ac --- /dev/null +++ b/src/ajax-adapter.js @@ -0,0 +1,52 @@ +export default class AjaxAdapter { + + // create(store, type, data, callback) { + // + // let request = new XMLHttpRequest(); + // + // request.open('POST', `/${type}`, true); + // + // request.onload = function () { + // var data = JSON.parse(request.responseText); + // store.push(data); + // callback(store.find(type, data.id)); + // }; + // + // // we need to convert to data here. + // + // request.send({ + // data: JSON.stringify(data) + // }); + // + // } + + load(store, type, id, options, callback) { + + if (id && {}.toString.call(id) === '[object Function]') { + this.load(store, type, null, null, id); + } else if (options && {}.toString.call(options) === '[object Function]') { + this.load(store, type, id, null, options); + } else if (id && typeof id === "object") { + this.load(store, type, null, id, callback); + } else { + + let request = new XMLHttpRequest(); + let url; + + options = options || {}; + url = id ? `/${type}/${id}` : `/${type}`; + + request.open('GET', url, true); + + request.onload = function () { + store.push(JSON.parse(request.responseText)); + callback(store.find(type, id)); + }; + + request.send(); + + } + + } + +} diff --git a/src/store.js b/src/store.js index a441edc..d744fc1 100644 --- a/src/store.js +++ b/src/store.js @@ -1,4 +1,5 @@ import "array.prototype.find"; +import AjaxAdapter from "./ajax-adapter"; export default class Store { @@ -88,7 +89,8 @@ export default class Store { } } - constructor() { + constructor(adapter) { + this._adapter = adapter; this._collectionListeners = { "added": {}, "updated": {}, "removed": {} }; this._data = {}; this._resourceListeners = { "added": {}, "updated": {}, "removed": {} }; @@ -129,8 +131,12 @@ export default class Store { } } - create() { - throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + create(type, data, callback) { + if (this._adapter) { + this._adapter.create(this, type, data, callback); + } else { + throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + } } /** @@ -153,8 +159,12 @@ export default class Store { }); } - destroy() { - throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + destroy(type, id, callback) { + if (this._adapter) { + this._adapter.destroy(this, type, id, callback); + } else { + throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + } } /** @@ -202,8 +212,12 @@ export default class Store { } } - load() { - throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + load(type, id, callback) { + if (this._adapter) { + this._adapter.load(this, type, id, callback); + } else { + throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + } } /** @@ -340,8 +354,12 @@ export default class Store { } } - update() { - throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + update(type, id, data, callback) { + if (this._adapter) { + this._adapter.update(this, type, id, data, callback); + } else { + throw new Error("Adapter missing. Specify an adapter when creating the store: `var store = new Store(adapter);`"); + } } _addField(object, resource, definition, fieldName) { @@ -450,3 +468,5 @@ export default class Store { } } + +Store.AjaxAdapter = AjaxAdapter;