gophr-bridge-sdk
Version:
A simple SDK for interacting with the Gophr Bridge API
386 lines (347 loc) ⢠14.2 kB
JavaScript
const axios = require('axios');
/**
* Gophr Bridge SDK
* A simple SDK for interacting with the Gophr Bridge API
*/
class GophrBridge {
/**
* Initialize the Gophr Bridge SDK
* @param {Object} config - Configuration object
* @param {string} config.clientId - Gophr client ID
* @param {string} config.clientSecret - Gophr client secret
* @param {string} config.host - Gophr Bridge API host URL
*/
constructor(config) {
if (!config.clientId || !config.clientSecret || !config.host) {
throw new Error('Missing required configuration: clientId, clientSecret, and host are required');
}
this.clientId = config.clientId;
this.clientSecret = config.clientSecret;
this.host = config.host;
// Create axios instance with default headers
this.client = axios.create({
baseURL: this.host,
headers: {
'client-id': this.clientId,
'client-secret': this.clientSecret,
'Content-Type': 'application/json'
}
});
// Add response interceptor for consistent error handling
this.client.interceptors.response.use(
response => response,
error => this._handleError(error)
);
}
/**
* Get a quote for delivery
* @param {Object} quoteData - Quote request data
* @param {string} quoteData.first_name - Customer first name
* @param {string} quoteData.last_name - Customer last name
* @param {string} quoteData.phone - Customer phone number
* @param {string} quoteData.email - Customer email (optional)
* @param {string} quoteData.address_1 - Delivery address line 1
* @param {string} quoteData.address_2 - Delivery address line 2 (optional)
* @param {string} quoteData.city - Delivery city
* @param {string} quoteData.state - Delivery state
* @param {string} quoteData.zip - Delivery ZIP code
* @param {string} quoteData.country - Delivery country (default: 'US')
* @param {string} quoteData.pick_up_instructions - Pickup instructions (optional)
* @param {string} quoteData.drop_off_instructions - Drop-off instructions (optional)
* @param {string|null} quoteData.scheduled_for - Scheduled delivery date (YYYY-MM-DD format, null for ASAP)
* @param {Array} quoteData.items - Array of items to be delivered
* @returns {Promise<Object>} Quote response with standard and expedited options
*/
async getQuote(quoteData) {
try {
const response = await this.client.post('/quote', quoteData);
return response.data;
} catch (error) {
throw error;
}
}
/**
* Create a shipment using a quote ID
* @param {Object} shipmentData - Shipment creation data
* @param {string} shipmentData.quote_id - Quote ID from previous quote request
* @param {string} shipmentData.drop_off_instructions - Drop-off instructions (optional)
* @returns {Promise<Object>} Shipment creation response
*/
async createShipment(shipmentData) {
try {
const response = await this.client.post('/create', shipmentData);
return response.data;
} catch (error) {
throw error;
}
}
/**
* Helper method to build quote data with defaults
* @param {Object} customerInfo - Customer information
* @param {Object} addressInfo - Address information
* @param {Array} items - Items array
* @param {Object} options - Additional options
* @returns {Object} Formatted quote data
*/
buildQuoteData(customerInfo, addressInfo, items, options = {}) {
return {
first_name: customerInfo.firstName,
last_name: customerInfo.lastName,
phone: customerInfo.phone,
email: customerInfo.email || '',
address_1: addressInfo.address1,
address_2: addressInfo.address2 || '',
city: addressInfo.city,
state: addressInfo.state,
zip: addressInfo.zip,
country: addressInfo.country || 'US',
pick_up_instructions: options.pickupInstructions || '',
drop_off_instructions: options.dropoffInstructions || '',
scheduled_for: options.scheduledFor || null,
items: items
};
}
/**
* Extract standard quote ID from quote response
* @param {Object} quoteResponse - Response from getQuote()
* @returns {string|null} Standard quote ID or null if not found
*/
getStandardQuoteId(quoteResponse) {
return quoteResponse?.payload?.standard_quote?.quote_id || null;
}
/**
* Extract expedited quote ID from quote response
* @param {Object} quoteResponse - Response from getQuote()
* @returns {string|null} Expedited quote ID or null if not found
*/
getExpeditedQuoteId(quoteResponse) {
return quoteResponse?.payload?.expedited_quote?.quote_id || null;
}
/**
* Extract standard quote fee from quote response
* @param {Object} quoteResponse - Response from getQuote()
* @returns {number|null} Standard quote fee or null if not found
*/
getStandardQuoteFee(quoteResponse) {
return quoteResponse?.payload?.standard_quote?.fee || null;
}
/**
* Extract expedited quote fee from quote response
* @param {Object} quoteResponse - Response from getQuote()
* @returns {number|null} Expedited quote fee or null if not found
*/
getExpeditedQuoteFee(quoteResponse) {
return quoteResponse?.payload?.expedited_quote?.fee || null;
}
/**
* Extract delivery ID from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {string|number|null} Delivery ID or null if not found
*/
getDeliveryId(shipmentResponse) {
return shipmentResponse?.payload?.delivery_id || null;
}
/**
* Extract delivery status from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {string|null} Delivery status or null if not found
*/
getDeliveryStatus(shipmentResponse) {
return shipmentResponse?.payload?.delivery_status || null;
}
/**
* Extract shipping fee from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {number|null} Shipping fee or null if not found
*/
getShippingFee(shipmentResponse) {
return shipmentResponse?.payload?.shipping_fee || null;
}
/**
* Extract vehicle type from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {string|null} Vehicle type or null if not found
*/
getVehicleType(shipmentResponse) {
return shipmentResponse?.payload?.vehicle_type || null;
}
/**
* Extract distance from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {number|null} Distance in miles or null if not found
*/
getDistance(shipmentResponse) {
return shipmentResponse?.payload?.distance || null;
}
/**
* Extract full payload from quote response
* @param {Object} quoteResponse - Response from getQuote()
* @returns {Object|null} Quote payload or null if not found
*/
getQuotePayload(quoteResponse) {
return quoteResponse?.payload || null;
}
/**
* Extract full payload from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {Object|null} Shipment payload or null if not found
*/
getShipmentPayload(shipmentResponse) {
return shipmentResponse?.payload || null;
}
/**
* Extract pickup address from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {Object|null} Pickup address object or null if not found
*/
getPickupAddress(shipmentResponse) {
return shipmentResponse?.payload?.pick_up || null;
}
/**
* Extract drop-off address from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {Object|null} Drop-off address object or null if not found
*/
getDropoffAddress(shipmentResponse) {
return shipmentResponse?.payload?.drop_off || null;
}
/**
* Extract items array from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {Array|null} Items array or null if not found
*/
getShipmentItems(shipmentResponse) {
return shipmentResponse?.payload?.items || null;
}
/**
* Extract weight from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {number|null} Weight or null if not found
*/
getShipmentWeight(shipmentResponse) {
return shipmentResponse?.payload?.weight || null;
}
/**
* Check if shipment is expedited
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {boolean|null} True if expedited, false if standard, null if not found
*/
isExpedited(shipmentResponse) {
return shipmentResponse?.payload?.is_expedited ?? null;
}
/**
* Extract scheduled_for date from shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {string|null} Scheduled date or null if ASAP/not found
*/
getScheduledFor(shipmentResponse) {
return shipmentResponse?.payload?.scheduled_for || null;
}
/**
* Check if response is successful
* @param {Object} response - API response object
* @returns {boolean} True if response status is 'successful'
*/
isSuccessful(response) {
return response?.status === 'successful' && response?.status_code === 200;
}
/**
* Get a formatted summary of quote response
* @param {Object} quoteResponse - Response from getQuote()
* @returns {Object} Formatted quote summary
*/
getQuoteSummary(quoteResponse) {
if (!this.isSuccessful(quoteResponse)) {
return { error: 'Quote request was not successful' };
}
return {
success: true,
standard: {
quoteId: this.getStandardQuoteId(quoteResponse),
fee: this.getStandardQuoteFee(quoteResponse)
},
expedited: {
quoteId: this.getExpeditedQuoteId(quoteResponse),
fee: this.getExpeditedQuoteFee(quoteResponse)
}
};
}
/**
* Get a formatted summary of shipment response
* @param {Object} shipmentResponse - Response from createShipment()
* @returns {Object} Formatted shipment summary
*/
getShipmentSummary(shipmentResponse) {
if (!this.isSuccessful(shipmentResponse)) {
return { error: 'Shipment creation was not successful' };
}
return {
success: true,
deliveryId: this.getDeliveryId(shipmentResponse),
status: this.getDeliveryStatus(shipmentResponse),
fee: this.getShippingFee(shipmentResponse),
vehicleType: this.getVehicleType(shipmentResponse),
distance: this.getDistance(shipmentResponse)
};
}
/**
* Enhanced error handling for API responses
* @private
*/
_handleError(error) {
const enhancedError = new Error();
if (error.response) {
// Server responded with error status
enhancedError.message = `API Error (${error.response.status}): ${error.response.statusText}`;
enhancedError.status = error.response.status;
enhancedError.statusText = error.response.statusText;
enhancedError.data = error.response.data;
enhancedError.url = error.config?.url;
// Add more specific error details
if (error.response.data) {
if (typeof error.response.data === 'object') {
enhancedError.details = error.response.data;
if (error.response.data.message) {
enhancedError.message += ` - ${error.response.data.message}`;
}
}
}
} else if (error.request) {
// Network error
enhancedError.message = `Network Error: Unable to connect to ${this.host}`;
enhancedError.code = error.code;
enhancedError.isNetworkError = true;
} else {
// Request setup error
enhancedError.message = `Request Error: ${error.message}`;
enhancedError.isRequestError = true;
}
enhancedError.originalError = error;
throw enhancedError;
}
/**
* Utility method to log errors in a formatted way
* @param {Error} error - Error object to log
*/
static logError(error) {
console.error('\nš“ Gophr Bridge API Error:');
console.error('================================');
if (error.status) {
console.error(`š” Status: ${error.status} (${error.statusText})`);
console.error(`š URL: ${error.url || 'Unknown'}`);
if (error.details) {
console.error(`š Details:`);
console.error(JSON.stringify(error.details, null, 2));
}
} else if (error.isNetworkError) {
console.error(`š Network Error: ${error.message}`);
if (error.code) {
console.error(`š Code: ${error.code}`);
}
} else {
console.error(`āļø Error: ${error.message}`);
}
console.error('================================\n');
}
}
module.exports = GophrBridge;