Skip to content

Commit

Permalink
feat(assets): EN-7240: Add Customer Asset support. (#96)
Browse files Browse the repository at this point in the history
Signed-off-by: Jeff Cuevas-Koch <[email protected]>

Co-authored-by: Jeff Cuevas-Koch <[email protected]>
  • Loading branch information
cuevaskoch and Jeff Cuevas-Koch authored Mar 19, 2021
1 parent 1d1fa18 commit 2c315f1
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 0 deletions.
89 changes: 89 additions & 0 deletions src/examples/get_asset.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import fetchMock from 'fetch-mock';
import Track from '../index';
import { charlie, assets as mockAssets } from '../mocks';

chai.should();
chai.use(chaiAsPromised);

describe('When searching for assets by name', () => {
const api = new Track({ autoRenew: false });

beforeEach(() => charlie.setUpSuccessfulMock(api.client));
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
beforeEach(() => fetchMock.catch(503));
afterEach(fetchMock.restore);

it('should get a list of assets', () => {
api.logIn({ username: '[email protected]', password: 'securepassword' });

const assetsPromise = api.customer('SYNC').assets()
.getPage()
.then(page => page.list)
.then(assets => assets); // Do things with list of assets

return assetsPromise;
});
});

describe('When retrieving an asset by ID', () => {
const api = new Track({ autoRenew: false });

beforeEach(() => charlie.setUpSuccessfulMock(api.client));
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
beforeEach(() => fetchMock.catch(503));
afterEach(fetchMock.restore);

it('should get an asset', () => {
api.logIn({ username: '[email protected]', password: 'securepassword' });

const assetPromise = api.customer('SYNC').asset(1)
.fetch()
.then(asset => asset); // Do things with asset

return assetPromise;
});
});

describe('When creating an asset', () => {
const api = new Track({ autoRenew: false });

beforeEach(() => charlie.setUpSuccessfulMock(api.client));
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
beforeEach(() => fetchMock.catch(503));
afterEach(fetchMock.restore);

it('should create an asset', () => {
api.logIn({ username: '[email protected]', password: 'securepassword' });

const assetPromise = api.customer('SYNC').asset({
base64_data: 'some_data',
asset_type: 'Logo',
filename: 'logo.png',
})
.create()
.then(asset => asset); // Do things with asset

return assetPromise;
});
});

describe('When marking an asset saved', () => {
const api = new Track({ autoRenew: false });

beforeEach(() => charlie.setUpSuccessfulMock(api.client));
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
beforeEach(() => fetchMock.catch(503));
afterEach(fetchMock.restore);

it('should mark the asset saved', () => {
api.logIn({ username: '[email protected]', password: 'securepassword' });

const assetPromise = api.customer('SYNC').asset(1)
.fetch()
.then(asset => asset.markSaved());

return assetPromise;
});
});
39 changes: 39 additions & 0 deletions src/mocks/assets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import fetchMock from 'fetch-mock';
import Client from '../Client';

const assets = {
setUpSuccessfulMock: (client) => {
const listResponse = () => new Response(
Client.toBlob(assets.list), {
headers: {
Link: '</1/SYNC/assets?page=1&per_page=10&sort=>; rel="next", </1/SYNC/assets?page=1&per_page=10&sort=>; rel="last"',
},
});
const singleResponse = () => new Response(Client.toBlob(assets.getById(1)));
const postResponse = () => new Response(undefined, {
headers: {
Location: '/1/SYNC/assets/1',
},
});
const patchResponse = () => new Response(undefined, {
headers: {},
});

fetchMock
.get(client.resolve('/1/SYNC/assets?page=1&per_page=10&sort='), listResponse)
.get(client.resolve('/1/SYNC/assets/1'), singleResponse)
.post(client.resolve('/1/SYNC/assets'), postResponse)
.patch(client.resolve('/1/SYNC/assets/1'), patchResponse);
},
getById: id => assets.list.find(v => v.id === id),
list: [{
href: '/1/SYNC/assets/1',
id: 1,
is_saved: true,
url: "http://example.com/file.png",
asset_type: "Logo",
}],
};

export default assets;
1 change: 1 addition & 0 deletions src/mocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Client from '../Client';

export { default as agencies } from './agencies';
export { default as areas } from './areas';
export { default as assets } from './assets';
export { default as blocks } from './blocks';
export { default as calls } from './calls';
export { default as callParticipants } from './callParticipants';
Expand Down
81 changes: 81 additions & 0 deletions src/resources/Asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import Resource from './Resource';

/**
* Asset resource
*/
class Asset extends Resource {
/**
* Creates a new Asset.
*
* @param {Client} client Instance of pre-configured client
* @param {Array} rest Remaining arguments to use in assigning values to this instance
*/
constructor(client, ...rest) {
super(client);

const newProperties = Object.assign({}, ...rest);
const hydrated = !Object.keys(newProperties).every(k => k === 'href' || k === 'code');

Object.assign(this, newProperties, {
hydrated,
});
}

/**
* Makes a href for a given customer code and ID
* @param {string} customerCode Customer code
* @param {number} id Asset ID
* @returns {{href: string}} URI to instance of asset
*/
static makeHref(customerCode, id) {
return {
href: `/1/${customerCode}/assets/${id}`,
code: customerCode,
};
}

/**
* Fetches the data for this asset via the client
* @returns {Promise} If successful, a hydrated instance of this asset
*/
fetch() {
return this.client.get(this.href)
.then(response => response.json())
.then(asset => new Asset(this.client, this, asset));
}

/**
* Saves data for a new asset via the client
* @returns {Promise} if successful, returns an asset with the id property set
*/
create() {
// eslint-disable-next-line no-unused-vars
const { client, hydrated, code, ...body } = this;
return this.client.post(`/1/${this.code}/assets`, { body })
.then(response => response.headers.get('location'))
.then((href) => {
const match = /\/\d+\/\S+\/assets\/(\d+)/.exec(href);
return new Asset(this.client, { ...this, href, id: parseFloat(match[1]) });
});
}

/**
* Updates this asset to mark it as permanently saved via the client
* @returns {Promise} if successful, returns an instance of this asset
*/
markSaved() {
const { href } = Asset.makeHref(this.code, this.id);
this.is_saved = true;
return this.client.patch(href, {
body: [
{
op: 'replace',
path: '/is_saved',
value: this.is_saved,
},
],
});
}
}

export default Asset;
81 changes: 81 additions & 0 deletions src/resources/Asset.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import fetchMock from 'fetch-mock';
import Client from '../Client';
import Asset from './Asset';
import { assets as mockAssets } from '../mocks';

chai.should();
chai.use(chaiAsPromised);

describe('When instantiating an asset based on customer and ID', () => {
const client = new Client();
const asset = new Asset(client, Asset.makeHref('SYNC', 1));

it('should set the href', () => asset.href.should.equal('/1/SYNC/assets/1'));
it('should not be hydrated', () => asset.hydrated.should.equal(false));
});

describe('When instantiating an asset based on an object', () => {
const client = new Client();
const asset = new Asset(client, mockAssets.getById(1));

it('should set the ID', () => asset.id.should.equal(1));
it('should set the href', () => asset.href.should.equal('/1/SYNC/assets/1'));
it('should be hydrated', () => asset.hydrated.should.equal(true));
});

describe('When fetching an asset based on customer and ID', () => {
const client = new Client();

beforeEach(() => mockAssets.setUpSuccessfulMock(client));
beforeEach(() => fetchMock.catch(503));
afterEach(fetchMock.restore);

let promise;
beforeEach(() => {
promise = new Asset(client, Asset.makeHref('SYNC', 1)).fetch();
});

it('should resolve the promise', () => promise.should.be.fulfilled);
it('should set the ID', () => promise.then(v => v.id).should.eventually.equal(1));
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/assets/1'));
it('should be hydrated', () => promise.then(v => v.hydrated).should.eventually.equal(true));
});

describe('When creating an asset', () => {
const client = new Client();

beforeEach(() => mockAssets.setUpSuccessfulMock(client));
beforeEach(() => fetchMock.catch(503));
afterEach(fetchMock.restore);

let promise;
beforeEach(() => {
promise = new Asset(client, { code: 'SYNC', base64_data: 'some_data' }).create();
});

it('should resolve the promise', () => promise.should.be.fulfilled);
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/assets/1'));
it('should set the ID', () => promise.then(v => v.id).should.eventually.equal(1));
});

describe('When updating an asset', () => {
const client = new Client();

beforeEach(() => mockAssets.setUpSuccessfulMock(client));
beforeEach(() => fetchMock.catch(503));
afterEach(fetchMock.restore);

let promise;
beforeEach(() => {
promise = new Asset(client, { code: 'SYNC' })
.create()
.then(asset => asset.markSaved()
.then(() => asset));
});

it('should resolve the promise', () => promise.should.be.fulfilled);
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/assets/1'));
it('should set is_saved to true', () => promise.then(v => v.is_saved).should.eventually.equal(true));
});
32 changes: 32 additions & 0 deletions src/resources/AssetsContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'isomorphic-fetch';
import PagedContext from './PagedContext';
import Asset from './Asset';

/**
* Asset querying context
*
* This is used to query the list of assets for a customer
*/
class AssetsContext extends PagedContext {
/**
* Creates a new asset context
* @param {Client} client Instance of pre-configured client
* @param {string} customerCode Customer code
* @param {object} params Object of querystring parameters to append to the URL
*/
constructor(client, customerCode, params) {
super(client, { ...params });
this.code = customerCode;
}

/**
* Gets the first page of results for this context
* @returns {Promise} If successful, a page of Asset objects
* @see Asset
*/
getPage() {
return this.page(Asset, `/1/${this.code}/assets`);
}
}

export default AssetsContext;
30 changes: 30 additions & 0 deletions src/resources/AssetsContext.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import fetchMock from 'fetch-mock';
import Client from '../Client';
import AssetsContext from './AssetsContext';
import { assets as mockAssets } from '../mocks';

chai.should();
chai.use(chaiAsPromised);

describe('When building a query for assets', () => {
const client = new Client();
client.setAuthenticated();

beforeEach(() => fetchMock
.get(client.resolve('/1/SYNC/assets?page=1&per_page=10&sort='), mockAssets.list)
.catch(503));
afterEach(fetchMock.restore);

let promise;
beforeEach(() => {
const assets = new AssetsContext(client, 'SYNC');
promise = assets
.withPage(1)
.withPerPage(10)
.getPage();
});

it('should make the expected request', () => promise.should.be.fulfilled);
});
Loading

0 comments on commit 2c315f1

Please sign in to comment.