Skip to content

Commit

Permalink
feat(stops): Add support for getting stops for a customer (EN-1527) (#4)
Browse files Browse the repository at this point in the history
feat(stops): Add support for getting stops for a customer (EN-1527)

fix(stops): Added missing integration with Customer and example tests
  • Loading branch information
thzinc authored Mar 28, 2017
1 parent 4b4c6ec commit 279c6d4
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 2 deletions.
48 changes: 48 additions & 0 deletions src/examples/get_stops.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import fetchMock from 'fetch-mock';
import Track from '../index';
import { charlie, stops as mockStops } from '../mocks';

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

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

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

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

const stopsPromise = api.customer('SYNC').stops()
.withQuery('1st') // Stops containing "1st" in their name
.getPage()
.then(page => page.list)
.then(stops => stops); // Do things with list of stops

return stopsPromise;
});
});

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

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

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

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

return stopsPromise;
});
});
27 changes: 27 additions & 0 deletions src/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,33 @@ export const signs = {
],
};

export const stops = {
setUpSuccessfulMock: (client) => {
const listResponse = () => new Response(
toBlob(stops.list),
{
headers: {
Link: '</1/SYNC/stops?page=1&perPage=10&q=1st&sort=>; rel="next", </1/SYNC/stops?page=1&perPage=10&q=1st&sort=>; rel="last"',
},
});
const singleResponse = () => new Response(toBlob(stops.getById(1)));

fetchMock
.get(client.resolve('/1/SYNC/stops?page=1&perPage=10&q=1st&sort='), listResponse)
.get(client.resolve('/1/SYNC/stops/1'), singleResponse);
},
getById: id => stops.list.find(v => v.id === id),
list: [
{
href: '/1/SYNC/stops/1',
id: 1,
name: '1st/Main',
latitude: 34.081728,
longitude: -118.351585,
},
],
};

export const vehicles = {
setUpSuccessfulMock: (client) => {
const listResponse = () => new Response(
Expand Down
19 changes: 19 additions & 0 deletions src/resources/Customer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import Route from './Route';
import RoutesContext from './RoutesContext';
import Sign from './Sign';
import SignsContext from './SignsContext';
import Stop from './Stop';
import StopsContext from './StopsContext';
import Vehicle from './Vehicle';
import VehiclesContext from './VehiclesContext';

Expand Down Expand Up @@ -61,6 +63,23 @@ class Customer extends Resource {
return this.resource(Sign, Sign.makeHref(this.code, id));
}

/**
* Gets a context for querying this customer's stops
* @returns {StopContext} Context for querying this customer's stops
*/
stops() {
return this.resource(StopsContext, this.code);
}

/**
* Gets a stop resource by id
* @param {Number} id Identity of the stop
* @returns {Stop} Stop resource
*/
stop(id) {
return this.resource(Stop, Stop.makeHref(this.code, id));
}

/**
* Gets a context for querying this customer's vehicles
* @returns {VehiclesContext} Context for querying this customer's vehicles
Expand Down
4 changes: 4 additions & 0 deletions src/resources/Customer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import Route from './Route';
import RoutesContext from './RoutesContext';
import Sign from './Sign';
import SignsContext from './SignsContext';
import Stop from './Stop';
import StopsContext from './StopsContext';
import Vehicle from './Vehicle';
import VehiclesContext from './VehiclesContext';

Expand All @@ -20,6 +22,8 @@ describe('When getting resources related to a customer', () => {
it('should allow a route to be retrieved', () => customer.route().should.be.instanceof(Route));
it('should allow signs to be searched', () => customer.signs().should.be.instanceof(SignsContext));
it('should allow a sign to be retrieved', () => customer.sign().should.be.instanceof(Sign));
it('should allow stops to be searched', () => customer.stops().should.be.instanceof(StopsContext));
it('should allow a stop to be retrieved', () => customer.stop().should.be.instanceof(Stop));
it('should allow vehicles to be searched', () => customer.vehicles().should.be.instanceof(VehiclesContext));
it('should allow a vehicle to be retrieved', () => customer.vehicle().should.be.instanceof(Vehicle));
});
2 changes: 1 addition & 1 deletion src/resources/Sign.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Sign extends Resource {
* @example <caption>Assigning partial sign data to a new instance</caption>
* const client = new Client();
* const partialSignData = {
* href: '/1/SYNC/sign/2',
* href: '/1/SYNC/signs/2',
* name: 'The second sign',
* };
* const sign = new Sign(client, partialSignData);
Expand Down
57 changes: 57 additions & 0 deletions src/resources/Stop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Resource from './Resource';

/**
* Stop resource
*/
class Stop extends Resource {
/**
* Creates a new stop
*
* Will populate itself with the values given to it after the client parameter
* @example <caption>Assigning partial stop data to a new instance</caption>
* const client = new Client();
* const partialStopData = {
* href: '/1/SYNC/stops/2',
* name: '9876',
* };
* const stop = new Stop(client, partialStopData);
*
* stop.hydrated == true;
* @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');

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

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

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

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

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

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

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

describe('When instantiating a stop based on an object', () => {
const client = new Client();
const stop = new Stop(client, mockStops.getById(1));

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

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

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

let promise;
beforeEach(() => {
promise = new Stop(client, Stop.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/stops/1'));
it('should be hydrated', () => promise.then(v => v.hydrated).should.eventually.equal(true));
});
48 changes: 48 additions & 0 deletions src/resources/StopsContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'isomorphic-fetch';
import PagedContext from './PagedContext';
import Stop from './Stop';

/**
* Stop querying context
*
* This is used to query the list of stops for a customer
*/
class StopsContext extends PagedContext {
/**
* Creates a new stop 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;
}

/**
* Sets the query term for the context
* @example
* const stops = new StopContext(...);
* stops
* .withQuery('12')
* .getPage()
* .then(page => ...);
* @param {string} term Query term to search for
* @returns {StopsContext} Returns itself
*/
withQuery(term) {
this.params.q = term;
return this;
}

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

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

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

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

beforeEach(() => fetchMock
.get(client.resolve('/1/SYNC/stops?page=9&perPage=27&q=valid&sort='), mockStops.list)
.catch(503));
afterEach(fetchMock.restore);

let promise;
beforeEach(() => {
const stops = new StopsContext(client, 'SYNC');
promise = stops
.withPage(9)
.withPerPage(27)
.withQuery('valid')
.getPage();
});

it('should make the expected request', () => promise.should.be.fulfilled);
});
2 changes: 1 addition & 1 deletion src/resources/Vehicle.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Vehicle extends Resource {
* @example <caption>Assigning partial vehicle data to a new instance</caption>
* const client = new Client();
* const partialVehicleData = {
* href: '/1/SYNC/vehicle/2',
* href: '/1/SYNC/vehicles/2',
* name: '9876',
* assignment: {
* sign_in_type: 'Dispatch',
Expand Down

0 comments on commit 279c6d4

Please sign in to comment.