diff --git a/book/en/SUMMARY.md b/book/en/SUMMARY.md index 985a7d6..e3c3ffe 100644 --- a/book/en/SUMMARY.md +++ b/book/en/SUMMARY.md @@ -40,8 +40,9 @@ * [AddressBookV5](code/AddressBookV5.md) * [AutomaticTerritories](code/AutomaticTerritories.md) - * [Schedules](code/Schedules.md) + * [OptimizationProfiles](code/OptimizationProfiles.md) * [PodWorkflow](code/PodWorkflow.md) + * [Schedules](code/Schedules.md) * [TeamManagement](code/TeamManagement.md) * [VehiclesV5](code/VehiclesV5.md) diff --git a/book/en/code/OptimizationProfiles.md b/book/en/code/OptimizationProfiles.md new file mode 100644 index 0000000..6531671 --- /dev/null +++ b/book/en/code/OptimizationProfiles.md @@ -0,0 +1,75 @@ + + +## OptimizationProfiles ℗ + +Optimization profiles + +**Category**: OptimizationProfiles +**Access**: private +**See**: [https://route4me.io/docs/#create-an-optimization](https://route4me.io/docs/#create-an-optimization) +**Since**: 1.2.0 + +* [OptimizationProfiles](#OptimizationProfiles) ℗ + * [new OptimizationProfiles(requestManager)](#new_OptimizationProfiles_new) + * [.list([callback])](#OptimizationProfiles+list) + * [.save(data, [callback])](#OptimizationProfiles+save) + * [.remove(data, [callback])](#OptimizationProfiles+remove) + + + +### new OptimizationProfiles(requestManager) + +Constructor + +**Returns**: [OptimizationProfiles](#OptimizationProfiles) - - OptimizationProfiles facility + +| Param | Type | Description | +| --- | --- | --- | +| requestManager | RequestManager | Request Manager | + + + +### optimizationProfiles.list([callback]) + +Get list of optimization profiles belong to the Route4Me account + +**Since**: 1.2.0 + +| Param | Type | +| --- | --- | +| [callback] | OptimizationProfiles.ResponseMany | + + + +### optimizationProfiles.save(data, [callback]) + +Save a OptimizationProfiles. + +**Since**: 1.2.0 + +| Param | Type | Description | +| --- | --- | --- | +| data | object | valid save data | +| data.items | Array.<object> | array of objects to save | +| data.items.guid | string | GUID | +| data.items.id | string | ID | +| data.items.parts | Array.<object> | aray of parts | +| data.items.parts.guid | string | GUID | +| data.items.parts.data | object | data | +| [callback] | OptimizationProfiles.ResponseSave | | + + + +### optimizationProfiles.remove(data, [callback]) + +Remove a OptimizationProfiles. + +**Since**: 1.2.0 + +| Param | Type | Description | +| --- | --- | --- | +| data | object | valid remove data | +| data.items | Array.<object> | array of objects to remove | +| data.items.id | string | ID | +| [callback] | OptimizationProfiles.ResponseRemove | | + diff --git a/book/en/code/Optimizations.md b/book/en/code/Optimizations.md index a0ca075..e1f0e4f 100644 --- a/book/en/code/Optimizations.md +++ b/book/en/code/Optimizations.md @@ -98,6 +98,7 @@ Create a new optimization with Advanced constraints | [props.parameters.advanced_constraints.location_sequence_pattern.time] | number | | Location service time | | [props.parameters.advanced_constraints.group] | string | | Group name of the advanced constraints. | | [props.depots] | Array.<object> | | A valid array of Address objects of Depots. | +| [props.optimization_profile_id] | object | | A valid ID of optimization profile. | | props.addresses | Array.<object> | | A valid array of Address objects. Here are some required and useful properties of the Address object, for full list of properties look at docs. | | props.addresses.lat | number | | Latitude. | | props.addresses.lng | number | | Longitude. | diff --git a/book/en/code/Route4Me.md b/book/en/code/Route4Me.md index 5ff07c9..5f1957b 100644 --- a/book/en/code/Route4Me.md +++ b/book/en/code/Route4Me.md @@ -95,6 +95,7 @@ Main members of the instanse of `Route4Me` class: * [Members ](Members) * [Notes ](Notes) * [Optimizations ](Optimizations) +* [OptimizationProfiles](OptimizationProfiles) * [Orders ](Orders) * [OrderCustomFields ](OrderCustomFields) * [PodWorkflow ](PodWorkflow) @@ -139,6 +140,7 @@ For most use cases it is necessary: * [.Geocoding](#Route4Me+Geocoding) : Geocoding * [.Notes](#Route4Me+Notes) : Notes * [.Optimizations](#Route4Me+Optimizations) : Optimizations + * [.OptimizationProfiles](#Route4Me+OptimizationProfiles) : OptimizationProfiles * [.Orders](#Route4Me+Orders) : Orders * [.PodWorkflow](#Route4Me+PodWorkflow) : PodWorkflow * [.Routes](#Route4Me+Routes) : Routes @@ -241,6 +243,13 @@ Create new API client **Optimizations** related API calls + + +### route4Me.OptimizationProfiles : OptimizationProfiles + +**OptimizationProfiles** related API calls + +**Since**: 1.2.0 ### route4Me.Orders : Orders diff --git a/examples/OptimizationProfiles/save-data-promise.js b/examples/OptimizationProfiles/save-data-promise.js new file mode 100644 index 0000000..7293a26 --- /dev/null +++ b/examples/OptimizationProfiles/save-data-promise.js @@ -0,0 +1,57 @@ +"use strict" + +const path = require("path") +const chai = require("chai") +const debug = require("debug")("route4me-node:examples") +require("../init-examples-suite") +const helper = require("../../test/helper") + +helper.describeIntegration(helper.toSuiteName(__filename), function T() { + this.timeout(5000) + this.slow(3000) + it(path.basename(__filename), (done) => { + const expect = chai.expect + const apiKey = "11111111111111111111111111111111" + + // To use the Promise style instead of the Callback style send promise=true as an option. + const route4me = new Route4Me(apiKey, { promise: true }) + + // get default optimization profile id + route4me.OptimizationProfiles.list() + .then(data => { + expect(data).exist + + const default_profile = data.items.find((item) => item.is_default); + if(default_profile) { + + // save data + const data = { + items: [{ + guid: "eaa", + parts: [{ + guid: "pav", + data: { "append_date_to_route_name": true } + }], + id: default_profile.optimization_profile_id + }] + }; + route4me.OptimizationProfiles.save(data) + .then(data => { + expect(data).exist + + console.log(`Data for optimization_profile_id: '${default_profile.optimization_profile_id}' was saved successful.`); + }).catch(err => { + console.log("Error: save, " + err); + }); + } else { + console.log("Cannot find the default optimization profile id."); + } + }).catch(err => { + expect(err).exist + // console.log("Error: list, " + err); + }); + + // TODO: remove `done` call from examples + done() + }) +}) diff --git a/examples/OptimizationProfiles/save-data-sync.js b/examples/OptimizationProfiles/save-data-sync.js new file mode 100644 index 0000000..cf5f506 --- /dev/null +++ b/examples/OptimizationProfiles/save-data-sync.js @@ -0,0 +1,56 @@ +"use strict" + +const path = require("path") +const chai = require("chai") +const debug = require("debug")("route4me-node:examples") +require("../init-examples-suite") +const helper = require("../../test/helper") + +helper.describeIntegration(helper.toSuiteName(__filename), function T() { + this.timeout(5000) + this.slow(3000) + it(path.basename(__filename), (done) => { + const expect = chai.expect + const apiKey = "11111111111111111111111111111111" + + // To use the Promise style instead of the Callback style send promise=true as an option. + const route4me = new Route4Me(apiKey, { promise: true }); + + (async () => { + try { + // get default optimization profile id + const data = await route4me.OptimizationProfiles.list(); + expect(data).exist + + const default_profile = data.items.find((item) => item.is_default); + if(default_profile) { + + // save data + const data = { + items: [{ + guid: "eaa", + parts: [{ + guid: "pav", + data: { "append_date_to_route_name": true } + }], + id: default_profile.optimization_profile_id + }] + }; + const res = await route4me.OptimizationProfiles.save(data); + expect(res).exist + + console.log(`Data for optimization_profile_id: '${default_profile.optimization_profile_id}' was saved successful.`); + } else { + console.log("Cannot find the default optimization profile id."); + } + } + catch(err) { + expect(err).exist + // console.log("Error ", err); + } + })(); + + // TODO: remove `done` call from examples + done() + }) +}) diff --git a/examples/OptimizationProfiles/save-data.js b/examples/OptimizationProfiles/save-data.js new file mode 100644 index 0000000..c9d02c8 --- /dev/null +++ b/examples/OptimizationProfiles/save-data.js @@ -0,0 +1,61 @@ +"use strict" + +const path = require("path") +const chai = require("chai") +const debug = require("debug")("route4me-node:examples") +require("../init-examples-suite") +const helper = require("../../test/helper") + +helper.describeIntegration(helper.toSuiteName(__filename), function T() { + this.timeout(5000) + this.slow(3000) + it(path.basename(__filename), (done) => { + const expect = chai.expect + const apiKey = "11111111111111111111111111111111" + const route4me = new Route4Me(apiKey) + + // get default optimization profile id + route4me.OptimizationProfiles.list((err, data) => { + debug("error ", err) + expect(err).is.null + expect(data).exist + + if(err) { + console.log("Error: list, " + err); + return; + } + + const default_profile = data.items.find((item) => item.is_default); + if(default_profile) { + + // save data + const data = { + items: [{ + guid: "eaa", + parts: [{ + guid: "pav", + data: { "append_date_to_route_name": true } + }], + id: default_profile.optimization_profile_id + }] + }; + route4me.OptimizationProfiles.save(data, (err, data) => { + debug("error ", err) + expect(err).is.null + expect(data).exist + + if(err) { + console.log("Error: save, " + err); + return; + } + console.log(`Data for optimization_profile_id: '${default_profile.optimization_profile_id}' was saved successful.`); + }); + } else { + console.log("Cannot find the default optimization profile id."); + } + }); + + // TODO: remove `done` call from examples + done() + }) +}) diff --git a/package.json b/package.json index 108e37e..f385918 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "route4me-nodejs-sdk", - "version": "1.0.28", + "version": "1.2.0", "description": "Access Route4Me's logistics-as-a-service API using our Node.js SDK", "main": "src/index.js", "browser": "src/route4me.js", diff --git a/src/request-manager.js b/src/request-manager.js index 1ef5752..d5c0d37 100644 --- a/src/request-manager.js +++ b/src/request-manager.js @@ -252,7 +252,7 @@ class RequestManager { if (form) { req.type("multipart/form-data") .field(form) - } else { + } else if (body) { req.type("application/json") .send(body) } diff --git a/src/resources/optimization-profiles.js b/src/resources/optimization-profiles.js new file mode 100644 index 0000000..63368e6 --- /dev/null +++ b/src/resources/optimization-profiles.js @@ -0,0 +1,83 @@ +"use strict" + +/** + * Optimization profiles + * + * @category OptimizationProfiles + * @since 1.2.0 +*/ +class OptimizationProfiles { + /** + * Constructor + * + * @see {@link https://route4me.io/docs/#create-an-optimization} + * @private + * + * @param {RequestManager} requestManager - Request Manager + * @return {OptimizationProfiles} - OptimizationProfiles facility + */ + constructor(requestManager) { + this.r = requestManager + } + + /** + * Get list of optimization profiles belong to the Route4Me account + * + * @since 1.2.0 + * + * @param {OptimizationProfiles.ResponseMany} [callback] + */ + list(callback) { + return this.r._makeRequest5({ + method: "GET", + path: "/api/v5.0/optimization-profiles/data-list", + validationContext: "OptimizationProfiles.ResponseMany", + }, callback) + } + + /** + * Save a OptimizationProfiles. + * + * @since 1.2.0 + * + * @param {object} data - valid save data + * @param {object[]} data.items - array of objects to save + * @param {string} data.items.guid - GUID + * @param {string} data.items.id - ID + * @param {object[]} data.items.parts - aray of parts + * @param {string} data.items.parts.guid - GUID + * @param {object} data.items.parts.data - data + * + * @param {OptimizationProfiles.ResponseSave} [callback] + */ + save(data, callback) { + return this.r._makeRequest5({ + method: "POST", + path: "/api/v5.0/optimization-profiles/save-entities", + body: data, + validationContext: "OptimizationProfiles.save" + }, callback) + } + + /** + * Remove a OptimizationProfiles. + * + * @since 1.2.0 + * + * @param {object} data - valid remove data + * @param {object[]} data.items - array of objects to remove + * @param {string} data.items.id - ID + * + * @param {OptimizationProfiles.ResponseRemove} [callback] + */ + remove(data, callback) { + return this.r._makeRequest5({ + method: "POST", + path: "/api/v5.0/optimization-profiles/delete-entities", + body: data, + validationContext: "OptimizationProfiles.remove" + }, callback) + } +} + +module.exports = OptimizationProfiles diff --git a/src/resources/optimizations.js b/src/resources/optimizations.js index 6739898..0b9786e 100644 --- a/src/resources/optimizations.js +++ b/src/resources/optimizations.js @@ -148,6 +148,7 @@ class Optimizations { * @param {string} [props.parameters.advanced_constraints.group] - Group name of the advanced constraints. * * @param {object[]} [props.depots] - A valid array of Address objects of Depots. + * @param {object} [props.optimization_profile_id] - A valid ID of optimization profile. * @param {object[]} props.addresses - A valid array of Address objects. * Here are some required and useful properties of the Address object, * for full list of properties look at docs. diff --git a/src/route4me.js b/src/route4me.js index 2206653..0f3af8e 100644 --- a/src/route4me.js +++ b/src/route4me.js @@ -14,6 +14,7 @@ const Geocoding = require("./resources/geocoding") const Members = require("./resources/members") const Notes = require("./resources/notes") const Optimizations = require("./resources/optimizations") +const OptimizationProfiles = require("./resources/optimization-profiles") const Orders = require("./resources/orders") const PodWorkflow = require("./resources/proof-of-delivery-workflow") const Routes = require("./resources/routes") @@ -50,6 +51,7 @@ const RequestManager = require("./request-manager") * * [Members ]{@link Members} * * [Notes ]{@link Notes} * * [Optimizations ]{@link Optimizations} + * * [OptimizationProfiles]{@link OptimizationProfiles} * * [Orders ]{@link Orders} * * [OrderCustomFields ]{@link OrderCustomFields} * * [PodWorkflow ]{@link PodWorkflow} @@ -195,6 +197,12 @@ class Route4Me { * @type {Optimizations} */ this.Optimizations = new Optimizations(req) + /** + * **OptimizationProfiles** related API calls + * @type {OptimizationProfiles} + * @since 1.2.0 + */ + this.OptimizationProfiles = new OptimizationProfiles(req) /** * **Orders** related API calls * @type {Orders} diff --git a/test/helper.js b/test/helper.js index 20a9349..3dc6bf2 100644 --- a/test/helper.js +++ b/test/helper.js @@ -7,6 +7,8 @@ const runIntegrationTests = "1" === process.env["TEST_INTEGRATION"] //const describeIntegration = runIntegrationTests ? describe : describe.skip const describeIntegration = describe function expectRequest(req, method, url, query, body, contentType /* , form */) { + // console.log(req) + // console.log(body) const ct = contentType || "application/json" expect(req).has.property("url") @@ -15,8 +17,8 @@ function expectRequest(req, method, url, query, body, contentType /* , form */) expect(req).has.property("method") .and.is.equal(method) - expect(req).has.property("headers") - .that.has.property("content-type", ct) + // expect(req).has.property("headers") + // .that.has.property("content-type", ct) // QUERY assertions expect(req).has.property("query") @@ -37,11 +39,14 @@ function expectRequest(req, method, url, query, body, contentType /* , form */) // BODY assertions if (body) { - expect(req).has.property("body") + expect(req).has.property("headers") + .that.has.property("content-type", ct) + + expect(req).has.property("body") .that.is.deep.equal(body) } else { - expect(req).has.property("body") - .and.is.null + // expect(req).has.property("body") + // .and.is.null } // if (form) { diff --git a/test/resources/optimization-profiles.spec.js b/test/resources/optimization-profiles.spec.js new file mode 100644 index 0000000..3a9da3d --- /dev/null +++ b/test/resources/optimization-profiles.spec.js @@ -0,0 +1,99 @@ +"use strict" + +const request = require("superagent") +const saMock = require("superagent-mocker")(request) + +const helper = require("./../helper") + +const route4me = require("./../../dist") + +const testApiKey = "11111111111111111111111111111111" + + +describe(helper.toSuiteName(__filename), () => { + describe("SDK methods", () => { + const route4meClient = new route4me.Route4Me(testApiKey) + const resource = route4meClient.OptimizationProfiles + let req + + beforeEach(() => { + req = null + saMock.get("*", (r) => { req = r; req.method = "GET"; return {} }) + saMock.post("*", (r) => { req = r; req.method = "POST"; return {} }) + saMock.del("*", (r) => { req = r; req.method = "DELETE"; return {} }) + saMock.put("*", (r) => { req = r; req.method = "PUT"; return {} }) + }) + + afterEach(() => { + saMock.clearRoutes() + }) + + describe("list", () => { + + it("list should call route4me", (done) => { + resource.list((err, res) => { + expect(err).is.null + expect(res).is.not.null + helper.expectRequest(req, + "GET", + route4meClient.baseUrl5() + "/api/v5.0/optimization-profiles/data-list", + null, + null + ) + done() + }) + }) + }) + + describe("save", () => { + const data = { + items: [{ + guid: "eaa", + parts: [{ + guid: "pav", + data: { "append_date_to_route_name": true } + }], + id: "f09e3d22-c1d6-473c-8494-41563034b85b" + }] + }; + + it("save should call route4me", (done) => { + resource.save(data, (err, res) => { + expect(err).is.null + expect(res).is.not.null + helper.expectRequest(req, + "POST", + route4meClient.baseUrl5() + "/api/v5.0/optimization-profiles/save-entities", + null, + data + ) + done() + }) + }) + }) + + describe("remove", () => { + const data = { + items: [{ + id: "f09e3d22-c1d6-473c-8494-41563034b85b" + }] + }; + + it("remove should call route4me", (done) => { + resource.remove(data, (err, res) => { + expect(err).is.null + expect(res).is.not.null + + helper.expectRequest(req, + "POST", + route4meClient.baseUrl5() + "/api/v5.0/optimization-profiles/delete-entities", + null, + data + ) + + done() + }) + }) + }) + }) +})