Skip to content

Commit 2c315f1

Browse files
cuevaskochJeff Cuevas-Koch
andauthored
feat(assets): EN-7240: Add Customer Asset support. (#96)
Signed-off-by: Jeff Cuevas-Koch <jcuevas-koch@gmvsync.com> Co-authored-by: Jeff Cuevas-Koch <jcuevas-koch@gmvsync.com>
1 parent 1d1fa18 commit 2c315f1

9 files changed

Lines changed: 379 additions & 0 deletions

File tree

src/examples/get_asset.test.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import chai from 'chai';
2+
import chaiAsPromised from 'chai-as-promised';
3+
import fetchMock from 'fetch-mock';
4+
import Track from '../index';
5+
import { charlie, assets as mockAssets } from '../mocks';
6+
7+
chai.should();
8+
chai.use(chaiAsPromised);
9+
10+
describe('When searching for assets by name', () => {
11+
const api = new Track({ autoRenew: false });
12+
13+
beforeEach(() => charlie.setUpSuccessfulMock(api.client));
14+
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
15+
beforeEach(() => fetchMock.catch(503));
16+
afterEach(fetchMock.restore);
17+
18+
it('should get a list of assets', () => {
19+
api.logIn({ username: 'charlie@example.com', password: 'securepassword' });
20+
21+
const assetsPromise = api.customer('SYNC').assets()
22+
.getPage()
23+
.then(page => page.list)
24+
.then(assets => assets); // Do things with list of assets
25+
26+
return assetsPromise;
27+
});
28+
});
29+
30+
describe('When retrieving an asset by ID', () => {
31+
const api = new Track({ autoRenew: false });
32+
33+
beforeEach(() => charlie.setUpSuccessfulMock(api.client));
34+
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
35+
beforeEach(() => fetchMock.catch(503));
36+
afterEach(fetchMock.restore);
37+
38+
it('should get an asset', () => {
39+
api.logIn({ username: 'charlie@example.com', password: 'securepassword' });
40+
41+
const assetPromise = api.customer('SYNC').asset(1)
42+
.fetch()
43+
.then(asset => asset); // Do things with asset
44+
45+
return assetPromise;
46+
});
47+
});
48+
49+
describe('When creating an asset', () => {
50+
const api = new Track({ autoRenew: false });
51+
52+
beforeEach(() => charlie.setUpSuccessfulMock(api.client));
53+
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
54+
beforeEach(() => fetchMock.catch(503));
55+
afterEach(fetchMock.restore);
56+
57+
it('should create an asset', () => {
58+
api.logIn({ username: 'charlie@example.com', password: 'securepassword' });
59+
60+
const assetPromise = api.customer('SYNC').asset({
61+
base64_data: 'some_data',
62+
asset_type: 'Logo',
63+
filename: 'logo.png',
64+
})
65+
.create()
66+
.then(asset => asset); // Do things with asset
67+
68+
return assetPromise;
69+
});
70+
});
71+
72+
describe('When marking an asset saved', () => {
73+
const api = new Track({ autoRenew: false });
74+
75+
beforeEach(() => charlie.setUpSuccessfulMock(api.client));
76+
beforeEach(() => mockAssets.setUpSuccessfulMock(api.client));
77+
beforeEach(() => fetchMock.catch(503));
78+
afterEach(fetchMock.restore);
79+
80+
it('should mark the asset saved', () => {
81+
api.logIn({ username: 'charlie@example.com', password: 'securepassword' });
82+
83+
const assetPromise = api.customer('SYNC').asset(1)
84+
.fetch()
85+
.then(asset => asset.markSaved());
86+
87+
return assetPromise;
88+
});
89+
});

src/mocks/assets.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// eslint-disable-next-line import/no-extraneous-dependencies
2+
import fetchMock from 'fetch-mock';
3+
import Client from '../Client';
4+
5+
const assets = {
6+
setUpSuccessfulMock: (client) => {
7+
const listResponse = () => new Response(
8+
Client.toBlob(assets.list), {
9+
headers: {
10+
Link: '</1/SYNC/assets?page=1&per_page=10&sort=>; rel="next", </1/SYNC/assets?page=1&per_page=10&sort=>; rel="last"',
11+
},
12+
});
13+
const singleResponse = () => new Response(Client.toBlob(assets.getById(1)));
14+
const postResponse = () => new Response(undefined, {
15+
headers: {
16+
Location: '/1/SYNC/assets/1',
17+
},
18+
});
19+
const patchResponse = () => new Response(undefined, {
20+
headers: {},
21+
});
22+
23+
fetchMock
24+
.get(client.resolve('/1/SYNC/assets?page=1&per_page=10&sort='), listResponse)
25+
.get(client.resolve('/1/SYNC/assets/1'), singleResponse)
26+
.post(client.resolve('/1/SYNC/assets'), postResponse)
27+
.patch(client.resolve('/1/SYNC/assets/1'), patchResponse);
28+
},
29+
getById: id => assets.list.find(v => v.id === id),
30+
list: [{
31+
href: '/1/SYNC/assets/1',
32+
id: 1,
33+
is_saved: true,
34+
url: "http://example.com/file.png",
35+
asset_type: "Logo",
36+
}],
37+
};
38+
39+
export default assets;

src/mocks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Client from '../Client';
44

55
export { default as agencies } from './agencies';
66
export { default as areas } from './areas';
7+
export { default as assets } from './assets';
78
export { default as blocks } from './blocks';
89
export { default as calls } from './calls';
910
export { default as callParticipants } from './callParticipants';

src/resources/Asset.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import Resource from './Resource';
2+
3+
/**
4+
* Asset resource
5+
*/
6+
class Asset extends Resource {
7+
/**
8+
* Creates a new Asset.
9+
*
10+
* @param {Client} client Instance of pre-configured client
11+
* @param {Array} rest Remaining arguments to use in assigning values to this instance
12+
*/
13+
constructor(client, ...rest) {
14+
super(client);
15+
16+
const newProperties = Object.assign({}, ...rest);
17+
const hydrated = !Object.keys(newProperties).every(k => k === 'href' || k === 'code');
18+
19+
Object.assign(this, newProperties, {
20+
hydrated,
21+
});
22+
}
23+
24+
/**
25+
* Makes a href for a given customer code and ID
26+
* @param {string} customerCode Customer code
27+
* @param {number} id Asset ID
28+
* @returns {{href: string}} URI to instance of asset
29+
*/
30+
static makeHref(customerCode, id) {
31+
return {
32+
href: `/1/${customerCode}/assets/${id}`,
33+
code: customerCode,
34+
};
35+
}
36+
37+
/**
38+
* Fetches the data for this asset via the client
39+
* @returns {Promise} If successful, a hydrated instance of this asset
40+
*/
41+
fetch() {
42+
return this.client.get(this.href)
43+
.then(response => response.json())
44+
.then(asset => new Asset(this.client, this, asset));
45+
}
46+
47+
/**
48+
* Saves data for a new asset via the client
49+
* @returns {Promise} if successful, returns an asset with the id property set
50+
*/
51+
create() {
52+
// eslint-disable-next-line no-unused-vars
53+
const { client, hydrated, code, ...body } = this;
54+
return this.client.post(`/1/${this.code}/assets`, { body })
55+
.then(response => response.headers.get('location'))
56+
.then((href) => {
57+
const match = /\/\d+\/\S+\/assets\/(\d+)/.exec(href);
58+
return new Asset(this.client, { ...this, href, id: parseFloat(match[1]) });
59+
});
60+
}
61+
62+
/**
63+
* Updates this asset to mark it as permanently saved via the client
64+
* @returns {Promise} if successful, returns an instance of this asset
65+
*/
66+
markSaved() {
67+
const { href } = Asset.makeHref(this.code, this.id);
68+
this.is_saved = true;
69+
return this.client.patch(href, {
70+
body: [
71+
{
72+
op: 'replace',
73+
path: '/is_saved',
74+
value: this.is_saved,
75+
},
76+
],
77+
});
78+
}
79+
}
80+
81+
export default Asset;

src/resources/Asset.test.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import chai from 'chai';
2+
import chaiAsPromised from 'chai-as-promised';
3+
import fetchMock from 'fetch-mock';
4+
import Client from '../Client';
5+
import Asset from './Asset';
6+
import { assets as mockAssets } from '../mocks';
7+
8+
chai.should();
9+
chai.use(chaiAsPromised);
10+
11+
describe('When instantiating an asset based on customer and ID', () => {
12+
const client = new Client();
13+
const asset = new Asset(client, Asset.makeHref('SYNC', 1));
14+
15+
it('should set the href', () => asset.href.should.equal('/1/SYNC/assets/1'));
16+
it('should not be hydrated', () => asset.hydrated.should.equal(false));
17+
});
18+
19+
describe('When instantiating an asset based on an object', () => {
20+
const client = new Client();
21+
const asset = new Asset(client, mockAssets.getById(1));
22+
23+
it('should set the ID', () => asset.id.should.equal(1));
24+
it('should set the href', () => asset.href.should.equal('/1/SYNC/assets/1'));
25+
it('should be hydrated', () => asset.hydrated.should.equal(true));
26+
});
27+
28+
describe('When fetching an asset based on customer and ID', () => {
29+
const client = new Client();
30+
31+
beforeEach(() => mockAssets.setUpSuccessfulMock(client));
32+
beforeEach(() => fetchMock.catch(503));
33+
afterEach(fetchMock.restore);
34+
35+
let promise;
36+
beforeEach(() => {
37+
promise = new Asset(client, Asset.makeHref('SYNC', 1)).fetch();
38+
});
39+
40+
it('should resolve the promise', () => promise.should.be.fulfilled);
41+
it('should set the ID', () => promise.then(v => v.id).should.eventually.equal(1));
42+
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/assets/1'));
43+
it('should be hydrated', () => promise.then(v => v.hydrated).should.eventually.equal(true));
44+
});
45+
46+
describe('When creating an asset', () => {
47+
const client = new Client();
48+
49+
beforeEach(() => mockAssets.setUpSuccessfulMock(client));
50+
beforeEach(() => fetchMock.catch(503));
51+
afterEach(fetchMock.restore);
52+
53+
let promise;
54+
beforeEach(() => {
55+
promise = new Asset(client, { code: 'SYNC', base64_data: 'some_data' }).create();
56+
});
57+
58+
it('should resolve the promise', () => promise.should.be.fulfilled);
59+
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/assets/1'));
60+
it('should set the ID', () => promise.then(v => v.id).should.eventually.equal(1));
61+
});
62+
63+
describe('When updating an asset', () => {
64+
const client = new Client();
65+
66+
beforeEach(() => mockAssets.setUpSuccessfulMock(client));
67+
beforeEach(() => fetchMock.catch(503));
68+
afterEach(fetchMock.restore);
69+
70+
let promise;
71+
beforeEach(() => {
72+
promise = new Asset(client, { code: 'SYNC' })
73+
.create()
74+
.then(asset => asset.markSaved()
75+
.then(() => asset));
76+
});
77+
78+
it('should resolve the promise', () => promise.should.be.fulfilled);
79+
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/assets/1'));
80+
it('should set is_saved to true', () => promise.then(v => v.is_saved).should.eventually.equal(true));
81+
});

src/resources/AssetsContext.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'isomorphic-fetch';
2+
import PagedContext from './PagedContext';
3+
import Asset from './Asset';
4+
5+
/**
6+
* Asset querying context
7+
*
8+
* This is used to query the list of assets for a customer
9+
*/
10+
class AssetsContext extends PagedContext {
11+
/**
12+
* Creates a new asset context
13+
* @param {Client} client Instance of pre-configured client
14+
* @param {string} customerCode Customer code
15+
* @param {object} params Object of querystring parameters to append to the URL
16+
*/
17+
constructor(client, customerCode, params) {
18+
super(client, { ...params });
19+
this.code = customerCode;
20+
}
21+
22+
/**
23+
* Gets the first page of results for this context
24+
* @returns {Promise} If successful, a page of Asset objects
25+
* @see Asset
26+
*/
27+
getPage() {
28+
return this.page(Asset, `/1/${this.code}/assets`);
29+
}
30+
}
31+
32+
export default AssetsContext;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import chai from 'chai';
2+
import chaiAsPromised from 'chai-as-promised';
3+
import fetchMock from 'fetch-mock';
4+
import Client from '../Client';
5+
import AssetsContext from './AssetsContext';
6+
import { assets as mockAssets } from '../mocks';
7+
8+
chai.should();
9+
chai.use(chaiAsPromised);
10+
11+
describe('When building a query for assets', () => {
12+
const client = new Client();
13+
client.setAuthenticated();
14+
15+
beforeEach(() => fetchMock
16+
.get(client.resolve('/1/SYNC/assets?page=1&per_page=10&sort='), mockAssets.list)
17+
.catch(503));
18+
afterEach(fetchMock.restore);
19+
20+
let promise;
21+
beforeEach(() => {
22+
const assets = new AssetsContext(client, 'SYNC');
23+
promise = assets
24+
.withPage(1)
25+
.withPerPage(10)
26+
.getPage();
27+
});
28+
29+
it('should make the expected request', () => promise.should.be.fulfilled);
30+
});

0 commit comments

Comments
 (0)