gianluca1606 / ups-easy-shipping

Node JS - UPS Library based on promises
1 stars 1 forks source link

Use xml2js more? #3

Open TheFloydman opened 3 years ago

TheFloydman commented 3 years ago

How would you feel about using xml2js more to create the XML, rather than adding on to a continuous xml string?

buildAccesRequest.js could be this:

const buildAccesRequest = (licenseId, userId, password) => {
    var xml2js = require('xml2js');
    var builder = new xml2js.Builder();
    var request = {
        AccessRequest: {
            $: {
                "xml:lang": "en-US"
            },
            AccessLicenseNumber: licenseId,
            UserId: userId,
            Password: password
        }
    };
    return builder.buildObject(request);
}

module.exports = buildAccesRequest;

Also makes it less likely you'll mess up closing the element.

gianluca1606 commented 3 years ago

I think this is a great idea, in general doing it like this looks more clean. If we choose these approach we should look how to pass optional arguments and how to append pieces of XML correctly. Maybe you already have an solution for this ?

TheFloydman commented 3 years ago

Yes, I've been poking around with rating.js. In this case, I've added mandatory checks before the json is built and optional checks after it's built. Duplicate tags (like <Package>) are supported. You just have to make the element an array. I've tested this and it is working:

const https = require("https");
const buildAccessRequest = require("../utils/accesRequest");

createRating = function() {
    return function RatingRequest(options) {
        settings = this;
        return new Promise(function(resolve, reject) {
            try {
                const req = https.request({
                    host: settings.sandbox ? settings.SANDBOX_API : settings.LIVE_API,
                    path: "/ups.app/xml/Rate",
                    method: "POST",
                });

                /* build the request data for rating and write it to
                       the request body
                */
                const { body } = buildRequestData(options, reject);

                req.write(body);

                req.on("response", function(res) {
                    let responseData = "";

                    res.on("data", function(data) {
                        data = data.toString();
                        responseData += data;
                    });

                    res.on("end", function() {
                        if (settings.useJSON) {
                            let parseString = require("xml2js").parseString;
                            parseString(responseData, function(err, result) {
                                if (err) reject(err.message);
                                if (result.RatingServiceSelectionResponse.Response[0].Error) {
                                    reject(result.RatingServiceSelectionResponse.Response[0].Error[0]);
                                } else {
                                    resolve(result);
                                }
                            });
                        } else {
                            resolve(responseData);
                        }
                    });
                });

                req.on("error", (e) => {
                    reject(e.message);
                });

                req.end();
            } catch (error) {
                reject(error.message);
            }
        });
    };
};

function buildRequestData(data, reject) {
    if (!settings.licenseId || !settings.userId || !settings.password) reject("Credentials missing");

    if (!data.customerContext) reject("Missing Customer Context");

    if (!data.shipment) reject("Missing Shipment");

    if (!data.shipment.shipper.address) reject("Missing shipment address");

    if (!data.shipment.shipTo) reject("Missing ShipTo");

    if (!data.shipment.shipFrom) reject("Missing shipFrom");

    if (!data.shipment.service) reject("Missing shipment service");

    if (!data.shipment.package) reject("Missing Shipment Packages");

    if (!data.shipment.schedule) reject("Missing Shipment Schedule");

    var request = {
        AccessRequest: buildAccessRequest(settings.licenseId, settings.userId, settings.password).AccessRequest,
        RatingServiceSelectionRequest: {
            $: {
                'xml:lang': 'en-US'
            },
            Request: {
                TransactionReference: {
                    CustomerContext: 'Rating and Service',
                    XpciVersion: 1.0
                },
                RequestAction: 'Rate'
            },
            Shipment: {
                Description: data.shipment.description,
                Shipper: {
                    Name: data.shipment.name,
                    PhoneNumber: data.shipment.phoneNumber,
                    ShipperNumber: data.shipment.shipperNumber,
                    Address: {
                        AddressLine1: data.shipment.shipper.address.addressLine,
                        City: data.shipment.shipper.address.city,
                        StateProvinceCode: data.shipment.shipper.address.stateProvinceCode,
                        PostalCode: data.shipment.shipper.address.postalCode,
                        CountryCode: data.shipment.shipper.address.countryCode
                    }
                },
                ShipTo: {
                    CompanyName: data.shipment.shipTo.companyName,
                    PhoneNumber: data.shipment.shipTo.phoneNumber,
                    Address: {
                        AddressLine1: data.shipment.shipTo.address.addressLine,
                        City: data.shipment.shipTo.address.city,
                        StateProvinceCode: data.shipment.shipTo.address.stateProvinceCode,
                        PostalCode: data.shipment.shipTo.address.postalCode,
                        CountryCode: data.shipment.shipTo.address.countryCode
                    }
                },
                ShipFrom: {
                    CompanyName: data.shipment.shipFrom.companyName,
                    PhoneNumber: data.shipment.shipFrom.phoneNumber,
                    FaxNumber: data.shipment.shipFrom.faxNumber,
                    Address: {
                        AddressLine1: data.shipment.shipFrom.address.addressLine,
                        City: data.shipment.shipFrom.address.city,
                        StateProvinceCode: data.shipment.shipFrom.address.stateProvinceCode,
                        PostalCode: data.shipment.shipFrom.address.postalCode,
                        CountryCode: data.shipment.shipFrom.address.countryCode
                    }
                },
                Service: {
                    Code: data.shipment.service.code
                },
                ShipmentServiceOptions: {
                    OnCallAir: {
                        Schedule: {
                            PickupDay: data.shipment.schedule.pickupDay,
                            Method: data.shipment.schedule.method
                        }
                    }
                }
            }
        }
    };

    if (data.pickUpType) {
        request.RatingServiceSelectionRequest.PickupType = {
            Code: data.pickUpType.code,
            Description: data.pickUpType.description
        };
    }

    if (data.shipment.paymentInformation) {
        request.RatingServiceSelectionRequest.PaymentInformation = {
            Prepaid: {
                BillShipper: {
                    AccountNumber: data.shipment.paymentInformation.accountNumber
                }
            }
        };
    }

    var packages = [];
    data.shipment.package.forEach(function(val) {
        insert = buildPackageInternals(val);
        if (insert) {
            packages.push(insert);
        } else {
            reject("Bad Package Internals");
        }
        request.RatingServiceSelectionRequest.Shipment.Package = packages;
    });

    var xml2js = require('xml2js');
    var builder = new xml2js.Builder();
    var unrooted = builder.buildObject(request).replace('<root>', '').replace('</root>', '');
    return { body: unrooted };
}

const buildPackageInternals = function(val) {

    var request = {
        PackagingType: {
            Code: val.code || '02'
        },
        PackageWeight: {
            Weight: val.weight || '1'
        }
    };

    if (val.description) {
        request.Description = val.description;
    }

    //TODO: Insurance
    if (val.insurance) {
        request.PackageServiceOptions = {
            InsuredValue: buildInsurance(val.insurance)
        };
    }
    return request;
};

module.exports = createRating;