Refactor code and add comments
Fix eslint errors Move partNumbers object to a json file
This commit is contained in:
154
index.js
154
index.js
@ -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");
|
||||
setTimeout(() => {
|
||||
makeRequest();
|
||||
requestsMade++;
|
||||
}, options.delay * 1000);
|
||||
}
|
||||
// Return an array of stores where the device is available.
|
||||
return storesAvailable;
|
||||
}
|
||||
|
||||
function makeRequest() {
|
||||
fetch(endpoint)
|
||||
/**
|
||||
* 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())
|
||||
.then(data => checkAvailability(data))
|
||||
.catch(error => console.log('Fetch Error :-S', error));
|
||||
.catch(error => process.stderr.write('Fetch Error :-S', error))
|
||||
.then(data => processResponse(data));
|
||||
}
|
||||
|
||||
function displayResultInPlace(data) {
|
||||
process.stdout.write(`${data} --- req: ${requestsMade}\r`);
|
||||
/**
|
||||
* 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(() => {
|
||||
requestLoop();
|
||||
}, options.delay * 1000);
|
||||
} else {
|
||||
// The device is available. Show that information to the user and exit the program.
|
||||
displayStoresAvailable(storesAvailable);
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Display program started message.
|
||||
process.stdout.write('Starting program with the following settings:\n');
|
||||
process.stdout.write(`${JSON.stringify(options, null, 2)}\n`);
|
||||
|
||||
// Kick off program.
|
||||
setInterval(() => {
|
||||
updateStatus();
|
||||
}, 1000);
|
||||
requestLoop();
|
||||
|
22
partNumbers.json
Normal file
22
partNumbers.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"x": {
|
||||
"gray": {
|
||||
"64": "",
|
||||
"256": "MQAU2LL/A"
|
||||
},
|
||||
"silver": {
|
||||
"64": "",
|
||||
"256": ""
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
"gray": {
|
||||
"64": "",
|
||||
"256": "MQ932LL/A"
|
||||
},
|
||||
"silver": {
|
||||
"64": "",
|
||||
"256": ""
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user