Skip to content

Commit f4336d1

Browse files
cuevaskochJeff Cuevas-Koch
andauthored
feat(rider_apps): Manage rider app config. (#97)
Allows users to get and set the configuration for a customer's Rider Apps. Resolves EN-7246. Signed-off-by: Jeff Cuevas-Koch <jcuevas-koch@gmvsync.com> Co-authored-by: Jeff Cuevas-Koch <jcuevas-koch@gmvsync.com>
1 parent 2c315f1 commit f4336d1

7 files changed

Lines changed: 223 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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, riderAppConfiguration as mocks } from '../mocks';
6+
7+
chai.should();
8+
chai.use(chaiAsPromised);
9+
10+
describe('When retrieving a rider app configuration', () => {
11+
const api = new Track({ autoRenew: false });
12+
13+
beforeEach(() => charlie.setUpSuccessfulMock(api.client));
14+
beforeEach(() => mocks.setUpSuccessfulMock(api.client));
15+
beforeEach(() => fetchMock.catch(503));
16+
afterEach(fetchMock.restore);
17+
18+
it('should get a the configuration', () => {
19+
api.logIn({ username: 'charlie@example.com', password: 'securepassword' });
20+
21+
const configPromise = api.customer('SYNC').riderAppConfiguration()
22+
.fetch()
23+
.then(config => config); // Do things with config
24+
25+
return configPromise;
26+
});
27+
});
28+
29+
describe('When updating a rider app configuration', () => {
30+
const api = new Track({ autoRenew: false });
31+
32+
beforeEach(() => charlie.setUpSuccessfulMock(api.client));
33+
beforeEach(() => mocks.setUpSuccessfulMock(api.client));
34+
beforeEach(() => fetchMock.catch(503));
35+
afterEach(fetchMock.restore);
36+
37+
it('should update the configuration', () => {
38+
api.logIn({ username: 'charlie@example.com', password: 'securepassword' });
39+
40+
const configPromise = api.customer('SYNC').riderAppConfiguration()
41+
.fetch()
42+
.then((config) => {
43+
// eslint-disable-next-line no-param-reassign
44+
config.spash_image_url = 'https://example.com/updated.png';
45+
return config.update();
46+
});
47+
48+
return configPromise;
49+
});
50+
});

src/mocks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export { default as messageChannels } from './messageChannels';
1919
export { default as patterns } from './patterns';
2020
export { default as reportingTickets } from './reportingTickets';
2121
export { default as realTime } from './realTime';
22+
export { default as riderAppConfiguration } from './riderAppConfiguration';
2223
export { default as roles } from './roles';
2324
export { default as routes } from './routes';
2425
export { default as runs } from './runs';

src/mocks/riderAppConfiguration.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// eslint-disable-next-line import/no-extraneous-dependencies
2+
import fetchMock from 'fetch-mock';
3+
import Client from '../Client';
4+
5+
const riderAppConfiguration = {
6+
rawObject: {
7+
href: '/1/SYNC/rider_app_configuration',
8+
splash_image_url: 'https://example.com/logo.png',
9+
accent_color: '#ABCDEF',
10+
information: [{
11+
title: 'Agency Information',
12+
items: [
13+
{
14+
title: 'About Us',
15+
link: 'https://example.com/about',
16+
},
17+
{
18+
title: 'Routes',
19+
link: 'https://example.com/routes',
20+
},
21+
],
22+
}],
23+
},
24+
setUpSuccessfulMock: (client) => {
25+
const url = client.resolve('/1/SYNC/rider_app_configuration');
26+
27+
const putResponse = () => new Response(undefined);
28+
const getResponse = () => new Response(Client.toBlob(riderAppConfiguration.rawObject));
29+
30+
fetchMock
31+
.put(url, putResponse)
32+
.get(url, getResponse);
33+
}
34+
};
35+
36+
export default riderAppConfiguration;

src/resources/Customer.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import DispatchMessageStatus from './DispatchMessageStatus';
2727
import Pattern from './Pattern';
2828
import PatternsContext from './PatternsContext';
2929
import ReportingTicket from './ReportingTicket';
30+
import RiderAppConfiguration from './RiderAppConfiguration';
3031
import Route from './Route';
3132
import RoutesContext from './RoutesContext';
3233
import Run from './Run';
@@ -296,6 +297,16 @@ class Customer extends Resource {
296297
return this.resource(Message, { code: this.code, ...payload });
297298
}
298299

300+
/**
301+
* Gets a Rider App Configuration resource
302+
* @param {Object} payload New properties with which to initialize the Rider App Configuration
303+
* @returns {RiderAppConfiguration} RiderAppConfiguration resource
304+
*/
305+
riderAppConfiguration(payload = {}) {
306+
const href = RiderAppConfiguration.makeHref(this.code);
307+
return this.resource(RiderAppConfiguration, { code: this.code, ...href, ...payload });
308+
}
309+
299310
/**
300311
* Gets a reporting ticket resource
301312
* @returns {ReportingTicket} ReportingTicket resource

src/resources/Customer.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Message from './Message';
2424
import MessagesContext from './MessagesContext';
2525
import Pattern from './Pattern';
2626
import PatternsContext from './PatternsContext';
27+
import RiderAppConfiguration from './RiderAppConfiguration';
2728
import ReportingTicket from './ReportingTicket';
2829
import Route from './Route';
2930
import RoutesContext from './RoutesContext';
@@ -75,6 +76,7 @@ describe('When getting resources related to a customer', () => {
7576
it('should allow a message to be retrieved', () => customer.message().should.be.instanceof(Message));
7677
it('should allow patterns to be searched', () => customer.patterns().should.be.instanceof(PatternsContext));
7778
it('should allow a pattern to be retrieved', () => customer.pattern().should.be.instanceof(Pattern));
79+
it('should allow a rider app configuration to be retrieved', () => customer.riderAppConfiguration().should.be.instanceOf(RiderAppConfiguration));
7880
it('should allow a reporting ticket to be retrieved', () => customer.reportingTicket().should.be.instanceof(ReportingTicket));
7981
it('should allow routes to be searched', () => customer.routes().should.be.instanceof(RoutesContext));
8082
it('should allow a route to be retrieved', () => customer.route().should.be.instanceof(Route));
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import Resource from './Resource';
2+
3+
/**
4+
* Rider App Configuration resource
5+
*/
6+
class RiderAppConfiguration extends Resource {
7+
/**
8+
* Creates a new RiderAppConfiguration.
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+
* Creates an href for a given customer code
26+
*
27+
* @param {string} customerCode Customer code
28+
* @returns {{href: string}} URI to instance of Rider App Configuration
29+
*/
30+
static makeHref(customerCode) {
31+
return {
32+
href: `/1/${customerCode}/rider_app_configuration`,
33+
code: customerCode,
34+
};
35+
}
36+
37+
/**
38+
* Fetches the Rider App Configuration for this customer data via the client
39+
* @returns {Promise} If successful,a hydrated instance of Rider App Configuration
40+
*/
41+
fetch() {
42+
return this.client.get(this.href)
43+
.then(response => response.json())
44+
.then(config => new RiderAppConfiguration(this.client, this, config));
45+
}
46+
47+
/**
48+
* Updates the Rider App Configuration for this customer via the client
49+
* @returns {Promise} If successful, returns the updated Rider App Configuration
50+
*/
51+
update() {
52+
// eslint-disable-next-line no-unused-vars
53+
const { client, hydrated, code, ...body } = this;
54+
const { href } = RiderAppConfiguration.makeHref(code);
55+
return this.client.put(href, { body })
56+
.then(() => new RiderAppConfiguration(this.client, { ...this }));
57+
}
58+
}
59+
60+
export default RiderAppConfiguration;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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 RiderAppConfiguration from './RiderAppConfiguration';
6+
import { riderAppConfiguration as mocks } from '../mocks';
7+
8+
chai.should();
9+
chai.use(chaiAsPromised);
10+
11+
describe('When instantiating a Rider App Configuration based on customer', () => {
12+
const client = new Client();
13+
const config = new RiderAppConfiguration(client, RiderAppConfiguration.makeHref('SYNC'));
14+
15+
it('should set the href', () => config.href.should.equal('/1/SYNC/rider_app_configuration'));
16+
it('should not be hydrated', () => config.hydrated.should.equal(false));
17+
});
18+
19+
describe('When instantiating a Rider App Configuration based on an object', () => {
20+
const client = new Client();
21+
const config = new RiderAppConfiguration(client, mocks.rawObject);
22+
23+
it('should set the href', () => config.href.should.equal('/1/SYNC/rider_app_configuration'));
24+
it('should set the splash image url', () => config.splash_image_url.should.equal('https://example.com/logo.png'));
25+
it('should set the accent color', () => config.accent_color.should.equal('#ABCDEF'));
26+
it('should be hydrated', () => config.hydrated.should.equal(true));
27+
});
28+
29+
describe('When fetching a Rider App Configuration based on customer', () => {
30+
const client = new Client();
31+
32+
beforeEach(() => mocks.setUpSuccessfulMock(client));
33+
beforeEach(() => fetchMock.catch(503));
34+
afterEach(fetchMock.restore);
35+
36+
let promise;
37+
beforeEach(() => {
38+
promise = new RiderAppConfiguration(client, RiderAppConfiguration.makeHref('SYNC')).fetch();
39+
});
40+
41+
it('should resolve the promise', () => promise.should.be.fulfilled);
42+
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/rider_app_configuration'));
43+
it('should be hydrated', () => promise.then(v => v.hydrated).should.eventually.equal(true));
44+
});
45+
46+
describe('When updating a Rider App Configuration for a customer', () => {
47+
const client = new Client();
48+
49+
beforeEach(() => mocks.setUpSuccessfulMock(client));
50+
beforeEach(() => fetchMock.catch(503));
51+
afterEach(fetchMock.restore);
52+
53+
let promise;
54+
beforeEach(() => {
55+
const config = new RiderAppConfiguration(client, RiderAppConfiguration.makeHref('SYNC'));
56+
config.splash_image_url = 'https://example.com/logo.png';
57+
config.accent_color = '#ABCDEF';
58+
promise = config.update();
59+
});
60+
61+
it('should resolve the promise', () => promise.should.be.fulfilled);
62+
it('should set the href', () => promise.then(v => v.href).should.eventually.equal('/1/SYNC/rider_app_configuration'));
63+
});

0 commit comments

Comments
 (0)