Skip to content

Commit

Permalink
Add a checkout adapter that supports persistence
Browse files Browse the repository at this point in the history
  • Loading branch information
Mina Smart committed Feb 19, 2016
1 parent 9cc9a16 commit 43b1aa9
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 0 deletions.
86 changes: 86 additions & 0 deletions lib/adapters/checkout-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import ajax from 'buy-button-sdk/ajax';
import CoreObject from 'buy-button-sdk/metal/core-object';

const CheckoutAdapter = CoreObject.extend({
ajax,

constructor(config) {
this.config = config;
},

get base64ApiKey() {
return btoa(this.config.apiKey);
},

get baseUrl() {
const { myShopifyDomain } = this.config;

return `https://${myShopifyDomain}.myshopify.com/anywhere`;
},

get headers() {
return {
Authorization: `Basic ${this.base64ApiKey}`
};
},

idKeyForType() {
return 'token';
},

pathForType(type) {
return `/${type}`;
},

buildUrl(singleOrMultiple, type, idOrQuery) {
switch (singleOrMultiple) {
case 'multiple':
return this.buildMultipleUrl(type, idOrQuery);
case 'single':
return this.buildSingleUrl(type, idOrQuery);
default:
return '';
}
},

buildMultipleUrl(type, query = {}) {
const url = `${this.baseUrl}${this.pathForType(type)}.json`;
const paramNames = Object.keys(query);

if (paramNames.length > 0) {
throw new Error('Querying checkouts is not allowed');
}

return url;
},

buildSingleUrl(type, id) {
return `${this.baseUrl}${this.pathForType(type)}/${id}.json`;
},

fetchSingle(/* type, id */) {
const url = this.buildUrl('single', ...arguments);

return this.ajax('get', url, { headers: this.headers }).then(response => {
return response.json;
});
},

create(type, payload) {
const url = this.buildUrl('multiple', type);

return this.ajax('post', url, { headers: this.headers, body: JSON.stringify(payload) }).then(response => {
return response.json;
});
},

update(type, id, payload) {
const url = this.buildUrl('single', type, id);

return this.ajax('patch', url, { headers: this.headers, body: JSON.stringify(payload) }).then(response => {
return response.json;
});
}
});

export default CheckoutAdapter;
143 changes: 143 additions & 0 deletions tests/unit/adapters/checkout-adapter-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { module, test } from 'qunit';
import CheckoutAdapter from 'buy-button-sdk/adapters/checkout-adapter';
import assign from 'buy-button-sdk/metal/assign';
import Promise from 'promise';

let adapter;

const myShopifyDomain = 'buckets-o-stuff';
const baseUrl = `https://${myShopifyDomain}.myshopify.com/anywhere`;
const apiKey = 'abc123def456ghi';
const base64ApiKey = btoa(apiKey);

function resolvingPromise(data = {}) {
return new Promise(function (resolve) {
resolve(data);
});
}

module('Unit | CheckoutAdapter', {
setup() {
adapter = new CheckoutAdapter();
},
teardown() {
adapter = null;
}
});

test('it builds an appropriate baseUrl based on configured values', function (assert) {
assert.expect(1);

adapter.config = { myShopifyDomain };

assert.equal(adapter.baseUrl, baseUrl);
});

test('it builds auth headers using the base64 encoded api key', function (assert) {
assert.expect(1);

adapter.config = { apiKey };

assert.deepEqual(adapter.headers, {
Authorization: `Basic ${base64ApiKey}`
});
});

test('it builds the url for all checkouts (used on #create)', function (assert) {
assert.expect(1);

adapter.config = { myShopifyDomain };

assert.equal(adapter.buildUrl('multiple', 'checkouts'), `${baseUrl}/checkouts.json`);
});

test('it builds the url for a single checkout', function (assert) {
assert.expect(1);

const token = 'abc123';

adapter.config = { myShopifyDomain };

assert.equal(adapter.buildUrl('single', 'checkouts', token), `${baseUrl}/checkouts/${token}.json`);
});

test('it throws if someone attempts to query the checkout endpoint', function (assert) {
assert.expect(1);

const token = 'abc123';

adapter.config = { myShopifyDomain };

assert.throws(function () {
adapter.buildUrl('multiple', 'checkouts', { token });
}, 'querying checkouts should produce an error');
});

test('it sends a GET, the correct url, and auth headers for fetchSingle to #ajax', function (assert) {
assert.expect(3);

const token = 'abc123';

adapter.config = { myShopifyDomain, apiKey };

adapter.ajax = function (method, url, opts) {
assert.equal(method, 'get');
assert.equal(url, `${baseUrl}/checkouts/${token}.json`);
assert.deepEqual(opts.headers, { Authorization: `Basic ${base64ApiKey}` });
return resolvingPromise();
};

adapter.fetchSingle('checkouts', token);
});

test('it sends a POST, the correct url, and auth headers to #ajax on #create', function (assert) {
assert.expect(4);

const done = assert.async();

const checkoutJson = { token: 'abc123' };

adapter.config = { myShopifyDomain, apiKey };

adapter.ajax = function (method, url, opts) {
assert.equal(method, 'post');
assert.equal(url, `${baseUrl}/checkouts.json`);
assert.deepEqual(opts.headers, { Authorization: `Basic ${base64ApiKey}` });
return resolvingPromise({ json: checkoutJson });
};

adapter.create('checkouts').then(result => {
assert.equal(result, checkoutJson);
done();
}).catch(() => {
assert.ok(false);
done();
});
});

test('it sends a PATCH, the correct url, and auth headers to #ajax on #update', function (assert) {
assert.expect(4);

const done = assert.async();

const id = 'abc123';
const payload = { checkout: { id, line_items: [] } };
const serverResponse = assign({}, payload);

adapter.config = { myShopifyDomain, apiKey };

adapter.ajax = function (method, url, opts) {
assert.equal(method, 'patch');
assert.equal(url, `${baseUrl}/checkouts/${id}.json`);
assert.deepEqual(opts.headers, { Authorization: `Basic ${base64ApiKey}` });
return resolvingPromise({ json: serverResponse });
};

adapter.update('checkouts', id, payload).then(result => {
assert.equal(result, serverResponse);
done();
}).catch(() => {
assert.ok(false);
done();
});
});

0 comments on commit 43b1aa9

Please sign in to comment.