Refactor code and add comments

Fix eslint errors
Move partNumbers object to a json file
This commit is contained in:
Carlos E Silva
2017-11-09 23:35:28 -05:00
parent 058457bb8b
commit 46ab621a10
2 changed files with 125 additions and 51 deletions

154
index.js
View File

@ -1,6 +1,11 @@
// Require npm packages.
const fetch = require('node-fetch');
const commandLineArgs = require('command-line-args')
const commandLineArgs = require('command-line-args');
// Get partNumbers from json file.
const partNumbers = require('./partNumbers.json');
// Define command line args accepted.
const optionDefinitions = [
{ name: 'carrier', type: String, defaultValue: 'TMOBILE' },
{ name: 'model', type: String, defaultValue: 'x' },
@ -10,72 +15,119 @@ const optionDefinitions = [
{ name: 'delay', type: Number, defaultValue: 30 },
];
// Parse command line args.
const options = commandLineArgs(optionDefinitions);
console.log(options);
const partNumbers = {
'x': {
'gray': {
'64': "",
'256': "MQAU2LL/A",
},
'silver': {
'64': "",
'256': "",
},
},
'8': {
'gray': {
'64': "",
'256': "MQ932LL/A",
},
'silver': {
'64': "",
'256': "",
},
},
};
// Get part number for the specified device.
const partNumber = partNumbers[options.model][options.color][options.storage];
// Construct the endpoint url with the options selected.
const endpoint = `https://www.apple.com/shop/retail/pickup-message?pl=true&cppart=${options.carrier}/US&parts.0=${partNumber}&location=${options.zip}`;
// const endpoint = 'https://www.apple.com/shop/retail/pickup-message?pl=true&cppart=TMOBILE/US&parts.0=MQ932LL/A&location=Salem,%20NH';
let requestsMade = 0;
// Keep track of the last request time.
let lastRequestTimestamp = null;
makeRequest();
/**
* Update program status display
*
* @param {String} str The string that will be outputed.
*/
function updateStatus() {
if (lastRequestTimestamp === null) {
return;
}
function checkAvailability(data) {
const timeDelta = Date.now() - lastRequestTimestamp;
const timeInSeconds = Math.floor(timeDelta / 1000);
process.stdout.write(`Status: Device not available. Last request made ${timeInSeconds} seconds ago\r`);
}
/**
* Parse the returned data and find stores where the device is available
*
* @param {Object} data The api response.
* @return {Array} The array of stores where the devices is available.
*/
function processResponse(data) {
// Destructure the stores object out of the body.
const { stores } = data.body;
const storesAvailable = stores.filter(store => {
const parts = Object.values(store.partsAvailability);
const part = parts[0];
const pickupDisplay = part.pickupDisplay;
const availability = pickupDisplay === 'available';
// Filter out stores that do not have the device available.
const storesAvailable = stores.filter((store) => {
// Select the specified device partNumber.
const part = store.partsAvailability[partNumber];
// Check that the pickupDisplay property says 'available'.
const availability = part.pickupDisplay === 'available';
// Return true if the device is available or else false.
return availability;
});
if (storesAvailable.length > 0) {
console.log(`Available at ${storesAvailable.length} stores near you:`);
console.log(storesAvailable.map(store => `${store.address.address} which is ${store.storeDistanceWithUnit} away`).reduce((msg,store) => `${msg}\n${store}`));
process.exit();
} else {
displayResultInPlace("unavailable");
// Return an array of stores where the device is available.
return storesAvailable;
}
/**
* Make a request to the endpoint and get list of stores available
*
* @return {Promise} A promise that should resolve to an array of stores available.
*/
function getStoresAvailable() {
// Update lastRequestTimestamp.
lastRequestTimestamp = Date.now();
return fetch(endpoint)
.then(stream => stream.json())
.catch(error => process.stderr.write('Fetch Error :-S', error))
.then(data => processResponse(data));
}
/**
* Output list of stores where the device is avaliable.
*
* @param {Array} storesAvailable The array of stores where the device is avaliable.
*/
function displayStoresAvailable(storesAvailable) {
// Construct the output string by reducing the storesAvailable array into a string.
const storesAvailableStr = storesAvailable.reduce(
(result, store) =>
`${result}\n${store.address.address} which is ${store.storeDistanceWithUnit} away`,
'',
);
// Output the message.
process.stdout.write(`The device is currently available at ${storesAvailable.length} stores near you:`);
process.stdout.write(storesAvailableStr);
process.stdout.write('\n');
}
/**
* The main program loop
*
* Continuously check for the device availability until it is available somewhere.
*/
async function requestLoop() {
// Fetch the storesAvailable array.
const storesAvailable = await getStoresAvailable();
if (storesAvailable.length === 0) {
// If the array is empty, update the status and after the
// specified options.delay amount of seconds, try again.
setTimeout(() => {
makeRequest();
requestsMade++;
requestLoop();
}, options.delay * 1000);
} else {
// The device is available. Show that information to the user and exit the program.
displayStoresAvailable(storesAvailable);
process.exit();
}
}
function makeRequest() {
fetch(endpoint)
.then(stream => stream.json())
.then(data => checkAvailability(data))
.catch(error => console.log('Fetch Error :-S', error));
}
// Display program started message.
process.stdout.write('Starting program with the following settings:\n');
process.stdout.write(`${JSON.stringify(options, null, 2)}\n`);
function displayResultInPlace(data) {
process.stdout.write(`${data} --- req: ${requestsMade}\r`);
}
// Kick off program.
setInterval(() => {
updateStatus();
}, 1000);
requestLoop();

22
partNumbers.json Normal file
View File

@ -0,0 +1,22 @@
{
"x": {
"gray": {
"64": "",
"256": "MQAU2LL/A"
},
"silver": {
"64": "",
"256": ""
}
},
"8": {
"gray": {
"64": "",
"256": "MQ932LL/A"
},
"silver": {
"64": "",
"256": ""
}
}
}