Our front-end applications often exchange JSON data with various RESTful APIs. We use modern JavaScript to interact with these APIs and to transform their responses into a format needed by the client application. In this kata, we provide a sample API with a single endpoint and ask you to write some JavaScript to request data from the API and transform the response.
You are provided with a /records API endpoint that returns a JSON array of items in the following format:
[ { "id": 1, "color": "brown", "disposition": "closed" }, { "id": 2, "color": "green", "disposition": "open" } ]
Each item returned from the /records endpoint will have the following:
- id: A unique integer
- color: One of “red”, “brown”, “blue”, “yellow”, or “green”
- disposition: Either “open” or “closed”
The /records endpoint accepts the following options, sent as query string parameters on the request URL:
- limit: The number of items to be returned
- offset: The index of the first item to be returned
- color[]: Which color to be included in the result set. May be included multiple times, once for each color. If omitted, all colors will be returned.
An example request URL might look like:
/records?limit=2&offset=0&color[]=brown&color[]=green
In api/managed-records.js, write a function named retrieve that requests data from the /records endpoint, transforms the result into the format outlined below, and returns a promise that resolves with the transformed object. In addition to retrieve, you may add as many helper functions as you wish.
Get data from the /records endpoint using the Fetch API. Fetch pages of 10 items at a time. Note that the /records endpoint may return more than 10 items per request.
The retrieve function accepts an options object and should support the following keys:
- page: Specifies which page to retrieve from the /records endpoint; If omitted, fetch page 1
- colors: An array of colors to retrieve from the /records endpoint; If omitted, fetch all colors
As an example, to fetch the 2nd page of red and brown items from the API, retrieve might be called like this:
retrieve({page: 2, colors: ["red", "brown"]});
You can assume standard HTTP status codes on the response. If a request is unsuccessful, output a simple error message via console.log() and recover.
Upon a successful API response, transform the fetched payload into an object containing the following keys:
- ids: An array containing the ids of all items returned from the request
- open: An array containing all of the items returned from the request that have a disposition value of “open” – add a fourth key to each item called isPrimary indicating whether or not the item contains a primary color (red, blue, or yellow)
- closedPrimaryCount: The total number of items returned from the request that have a disposition value of “closed” and contain a primary color
- previousPage: The page number for the previous page of results, or null if this is the first page
- nextPage: The page number for the next page of results, or null if this is the last page
Return a promise from retrieve that resolves with the transformed data. Additional requirements include:
- Use the provided URI library to construct the request URL – refer to https://medialize.github.io/URI.js/ for documentation
- Write your solution using ES2015 – any methods and syntax supported by the Babel polyfill are considered fair game – https://babeljs.io/docs/usage/polyfill/
- You must use the provided Fetch API to interact with the records endpoint – refer to https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API for documentation
- Do not use any for or while loops in your solution
- Do not add any additional libraries or edit any files other than api/managed-records.js
- A suite of unit tests is provided – your solution should pass all tests
Well that’s a mouthful.
Essentially the kata is asking us to fit a puzzle piece into an existing environment. Our piece sits between a REST API and some consumer set to call our retrieve function. The consumer will pass arguments. It will expect the data to be formatted a certain way and be wrapped in a promise. And we will be swimming in Node.js and ES6 Javascript!
Furthermore, we’re restricted in how we can request data. We must show that we can work with standard libraries. In this case we will use URI.js to form and Mozilla Fetch API to make the API request.
The task can be broken down into the following subtasks:
- transform arguments: determine pagination properties (item limit, page offset) and color filtering from passed-in arguments, provide default fallbacks in the case these are missing
- form the request: convert arguments into a valid API endpoint using URI.js
- make the request: request data from the API using Mozilla Fetch API, handle errors and wait for all responses to resolve – we need to make three separate requests to support the pagination requirements of the consumer:
- the current page the consumer is after based on passed in options (filters and pagination)
- the previous page link if it exists – we will determine if previous link we generate is valid based on the API response
- the next page link if it exists – again it is up to us to generate the link and test it’s validity against the API
- transform API response: derive properties based on the data returned: ids, open, closedPrimaryCount, previousPage, and nextPage – this returns a promise
My solution is here. It lives in the prescribed Node.js environment. It’s pure, and abides by the functional programming requirement. It passes all pre-written unit tests.