@mediocre/bloodhound
Advanced tools
Comparing version 1.9.0 to 1.10.0
@@ -5,3 +5,3 @@ const async = require('async'); | ||
const checkDigit = require('../util/checkDigit'); | ||
const DhlEcommerceSolutions = require('./dhlEcommerceSolutions'); | ||
const USPS = require('./usps'); | ||
@@ -15,9 +15,6 @@ | ||
// In an IMpb number, an initial '420' followed by ZIP or ZIP+4 is part of the barcode but is not supposed to be printed. If the tracking number comes from a barcode scanner, it will have that info. | ||
// 109124 is a Mailer ID provided by DHL. See https://postalpro.usps.com/shipping/impb/BarcodePackageIMSpec for full IMpb specs. | ||
const DHL_IMPB_REGEX = new RegExp(/^(?:420(?:\d{9}|\d{5}))?(93\d{3}109124(?:\d{14}|\d{10})\d)$/); | ||
const geography = require('../util/geography'); | ||
function DHL(options) { | ||
const dhlEcommerceSolutions = new DhlEcommerceSolutions(options && options.dhlEcommerceSolutions); | ||
const usps = new USPS(options && options.usps); | ||
@@ -29,10 +26,7 @@ | ||
if (DHL_IMPB_REGEX.test(trackingNumber)) { | ||
// Strip off the IMpb routing code and ZIP | ||
trackingNumber = trackingNumber.replace(DHL_IMPB_REGEX, '$1'); | ||
return checkDigit(trackingNumber, [3, 1], 10); | ||
if ([/94748\d{17}$/, /93612\d{17}$/].some(regex => regex.test(trackingNumber))) { | ||
return true; | ||
} | ||
return false; | ||
return dhlEcommerceSolutions.isTrackingNumberValid(trackingNumber); | ||
}; | ||
@@ -77,11 +71,3 @@ | ||
}, function(err, body) { | ||
if (err) { | ||
// If DHL fails, try USPS | ||
if (options.usps && usps.isTrackingNumberValid(trackingNumber)) { | ||
return usps.track(trackingNumber, callback); | ||
} | ||
return callback(err); | ||
} | ||
const results = { | ||
@@ -93,95 +79,114 @@ carrier: 'DHL', | ||
if (!body || !body.shipments || !body.shipments.length) { | ||
return callback(null, results); | ||
} | ||
if (err || !body?.shipments?.length) { | ||
// If DHL fails, try DHL eCommerce Solutions | ||
if (options.dhlEcommerceSolutions && dhlEcommerceSolutions.isTrackingNumberValid(trackingNumber)) { | ||
async.retry(function(callback) { | ||
dhlEcommerceSolutions.track(trackingNumber, callback); | ||
}, function(err, results) { | ||
// If DHL eCommerce Solutions fails, try USPS | ||
if (err || !results.raw?.packages?.length) { | ||
if (options.usps && usps.isTrackingNumberValid(trackingNumber)) { | ||
return usps.track(trackingNumber, callback); | ||
} | ||
} | ||
// We only support the first shipment | ||
const shipment = body.shipments[0]; | ||
return callback(err, results); | ||
}); | ||
} else if (options.usps && usps.isTrackingNumberValid(trackingNumber)) { | ||
return usps.track(trackingNumber, callback); | ||
} else { | ||
return callback(err, results); | ||
} | ||
} else { | ||
// We only support the first shipment | ||
const shipment = body.shipments[0]; | ||
// Reverse the array to get events in order Least Recent - Most Recent | ||
const events = shipment.events.reverse(); | ||
// Reverse the array to get events in order Least Recent - Most Recent | ||
const events = shipment.events.reverse(); | ||
// Used when there is no address data present | ||
var previousAddress = shipment.origin?.address; | ||
// Used when there is no address data present | ||
var previousAddress = shipment.origin?.address; | ||
// Set address and locationString of each event | ||
events.forEach(event => { | ||
if (!event.location?.address) { | ||
event.location = event.location || {}; | ||
event.location.address = previousAddress; | ||
} | ||
event.locationString = `${event.location.address?.addressLocality} ${event.location.address?.postalCode} ${event.location.address?.countryCode}`.trim(); | ||
}); | ||
// Set address and locationString of each event | ||
events.forEach(event => { | ||
if (!event.location?.address) { | ||
event.location = event.location || {}; | ||
event.location.address = previousAddress; | ||
} | ||
event.locationString = `${event.location.address?.addressLocality} ${event.location.address?.postalCode} ${event.location.address?.countryCode}`.trim(); | ||
}); | ||
// Get unique array of locations (remove falsy values) | ||
const locations = Array.from(new Set(events.map(event => event.locationString))).filter(l => l); | ||
// Get unique array of locations (remove falsy values) | ||
const locations = Array.from(new Set(events.map(event => event.locationString))).filter(l => l); | ||
// Lookup each location | ||
async.mapLimit(locations, 10, function(location, callback) { | ||
geography.parseLocation(location, options, function(err, address) { | ||
if (err || !address) { | ||
return callback(err, address); | ||
// Lookup each location | ||
async.mapLimit(locations, 10, function(location, callback) { | ||
geography.parseLocation(location, options, function(err, address) { | ||
if (err || !address) { | ||
return callback(err, address); | ||
} | ||
address.location = location; | ||
callback(null, address); | ||
}); | ||
}, function(err, addresses) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
address.location = location; | ||
events.forEach(event => { | ||
const address = addresses.find(a => a && a.location === event.locationString); | ||
callback(null, address); | ||
}); | ||
}, function(err, addresses) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
// Use the geolocated timezone, or ET as a default | ||
let timezone = 'America/New_York'; | ||
if (address && address.timezone) { | ||
timezone = address.timezone; | ||
} | ||
events.forEach(event => { | ||
const address = addresses.find(a => a && a.location === event.locationString); | ||
const _event = { | ||
address: { | ||
city: address.city, | ||
country: event.location.address.countryCode, | ||
state: address.state, | ||
zip: event.location.address.postalCode | ||
}, | ||
date: moment.tz(event.timestamp, 'YYYY-MM-DDTHH:mm:ss', timezone).toDate(), | ||
description: event.status | ||
}; | ||
// Use the geolocated timezone, or ET as a default | ||
let timezone = 'America/New_York'; | ||
if (address && address.timezone) { | ||
timezone = address.timezone; | ||
} | ||
// Ensure event is after minDate (used to prevent data from reused tracking numbers) | ||
if (_event.date < _options.minDate) { | ||
return; | ||
} | ||
const _event = { | ||
address: { | ||
city: address.city, | ||
country: event.location.address.countryCode, | ||
state: address.state, | ||
zip: event.location.address.postalCode | ||
}, | ||
date: moment.tz(event.timestamp, 'YYYY-MM-DDTHH:mm:ss', timezone).toDate(), | ||
description: event.status | ||
}; | ||
if (event.description) { | ||
_event.details = event.description; | ||
} | ||
// Ensure event is after minDate (used to prevent data from reused tracking numbers) | ||
if (_event.date < _options.minDate) { | ||
return; | ||
} | ||
if (!results.deliveredAt && _event.description && DELIVERED_TRACKING_DESCRIPTIONS.includes(_event.description.toUpperCase())) { | ||
results.deliveredAt = _event.date; | ||
} | ||
if (event.description) { | ||
_event.details = event.description; | ||
} | ||
if (!results.shippedAt && _event.description && SHIPPED_TRACKING_DESCRIPTIONS.includes(_event.description.toUpperCase())) { | ||
results.shippedAt = _event.date; | ||
} | ||
if (!results.deliveredAt && _event.description && DELIVERED_TRACKING_DESCRIPTIONS.includes(_event.description.toUpperCase())) { | ||
results.deliveredAt = _event.date; | ||
} | ||
results.events.push(_event); | ||
}); | ||
if (!results.shippedAt && _event.description && SHIPPED_TRACKING_DESCRIPTIONS.includes(_event.description.toUpperCase())) { | ||
results.shippedAt = _event.date; | ||
// Add URL to carrier tracking page | ||
results.url = `https://www.dhl.com/us-en/home/tracking.html?tracking-id=${encodeURIComponent(trackingNumber)}&submit=1`; | ||
// Reverse results again to get events in order Most Recent - Least Recent | ||
results.events.reverse(); | ||
if (!results.shippedAt && results.deliveredAt) { | ||
results.shippedAt = results.deliveredAt; | ||
} | ||
results.events.push(_event); | ||
callback(null, results); | ||
}); | ||
} | ||
// Add URL to carrier tracking page | ||
results.url = `https://www.dhl.com/us-en/home/tracking.html?tracking-id=${encodeURIComponent(trackingNumber)}&submit=1`; | ||
// Reverse results again to get events in order Most Recent - Least Recent | ||
results.events.reverse(); | ||
if (!results.shippedAt && results.deliveredAt) { | ||
results.shippedAt = results.deliveredAt; | ||
} | ||
callback(null, results); | ||
}); | ||
}); | ||
@@ -188,0 +193,0 @@ } |
@@ -9,2 +9,6 @@ # Changelog | ||
## [1.10.0] - 2021-06-13 | ||
### Changed | ||
- Updated DHL to use DHL eCommerce Solutions if configured. | ||
## [1.9.0] - 2021-06-08 | ||
@@ -11,0 +15,0 @@ ### Changed |
@@ -48,2 +48,7 @@ const NodeGeocoder = require('node-geocoder'); | ||
// Allow DHL to use DHL eCommerce Solutions | ||
if (options.dhlEcommerceSolutions) { | ||
options.dhl.dhlEcommerceSolutions = options.dhlEcommerceSolutions; | ||
} | ||
// Allow DHL to use USPS | ||
@@ -50,0 +55,0 @@ if (options.usps) { |
@@ -5,2 +5,3 @@ { | ||
"async": "~3.2.0", | ||
"dhl-ecommerce-solutions": "~0.3.0", | ||
"fast-xml-parser": "~3.21.0", | ||
@@ -43,3 +44,3 @@ "moment-timezone": "~0.5.28", | ||
}, | ||
"version": "1.9.0" | ||
"version": "1.10.0" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
76751
13
1174
10
+ Addeddepd@2.0.0(transitive)
+ Addeddhl-ecommerce-solutions@0.3.0(transitive)
+ Addedhttp-errors@2.0.0(transitive)
+ Addedstatuses@2.0.1(transitive)