From 7fb2413759c901ec30176ea01ab475a4f2e1650b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alu=C3=ADsio=20Amaral?= Date: Thu, 8 Mar 2018 10:02:41 -0300 Subject: [PATCH 1/7] fix data recovering for non-list menus when updating --- src/RosApiCrud.ts | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/RosApiCrud.ts b/src/RosApiCrud.ts index 5a23748..1c78073 100644 --- a/src/RosApiCrud.ts +++ b/src/RosApiCrud.ts @@ -51,7 +51,7 @@ export abstract class RouterOSAPICrud { public add(data: object): Types.SocPromise { return this.exec("add", data).then((results: any) => { if (results.length === 0) return Promise.resolve(null); - return this.recoverDataFromIdsOfChangedItems(null, results[0].ret); + return this.recoverDataFromChangedItems(results.shift().ret); }); } @@ -76,7 +76,7 @@ export abstract class RouterOSAPICrud { } const disabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); return this.exec("disable").then((response: any[]) => { - return this.recoverDataFromIdsOfChangedItems(response, disabledIds); + return this.recoverDataFromChangedItems(disabledIds); }); } @@ -101,7 +101,7 @@ export abstract class RouterOSAPICrud { } const enabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); return this.exec("enable").then((response: any[]) => { - return this.recoverDataFromIdsOfChangedItems(response, enabledIds); + return this.recoverDataFromChangedItems(enabledIds); }); } @@ -139,7 +139,7 @@ export abstract class RouterOSAPICrud { } const movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); return this.exec("move").then((response: any[]) => { - return this.recoverDataFromIdsOfChangedItems(response, movedIds); + return this.recoverDataFromChangedItems(movedIds); }); } @@ -157,7 +157,7 @@ export abstract class RouterOSAPICrud { this.makeQuery(data); const updatedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); return this.exec("set").then((response: any[]) => { - return this.recoverDataFromIdsOfChangedItems(response, updatedIds); + return this.recoverDataFromChangedItems(updatedIds); }); } @@ -224,24 +224,9 @@ export abstract class RouterOSAPICrud { const idsForRemoval = utils.lookForIdParameterAndReturnItsValue(this.queryVal); if (!idsForRemoval) return this.exec("remove"); - const promises = []; - const foundIds = idsForRemoval.split(","); - for (const id of foundIds) { - const promise = this.rosApi.write([ - this.pathVal + "/print", - "?.id=" + id - ]); - promises.push(promise); - } let responseData; - return Promise.all(promises).then((data: any) => { - if (!data) return Promise.resolve(data); - data = flatten(data); - data = this.treatMikrotikProperties(data); - if (!idsForRemoval.includes(",")) return Promise.resolve(data[0]); - return Promise.resolve(data); - }).then((data: any) => { - responseData = data; + return this.recoverDataFromChangedItems(idsForRemoval).then((response: any) => { + responseData = response; return this.exec("remove"); }).then(() => { return Promise.resolve(responseData); @@ -501,8 +486,12 @@ export abstract class RouterOSAPICrud { * @param data * @param ids */ - private recoverDataFromIdsOfChangedItems(data: any[], ids?: string): Promise { - if (!ids) return Promise.resolve(data); + private recoverDataFromChangedItems(ids?: string): Promise { + if (!ids) { + return this.rosApi.write([this.pathVal + "/print"]) + .then((data) => Promise.resolve(this.treatMikrotikProperties(data).shift())); + } + const promises = []; const splittedIds = ids.split(","); for (const id of splittedIds) { @@ -516,7 +505,7 @@ export abstract class RouterOSAPICrud { if (!data) return Promise.resolve(data); data = flatten(data); data = this.treatMikrotikProperties(data); - if (!ids.includes(",")) return Promise.resolve(data[0]); + if (!ids.includes(",")) return Promise.resolve(data.shift()); return Promise.resolve(data); }); } From 4732bd1bcf8d604fe5c6666df20ae9b57ae5e15e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alu=C3=ADsio=20Amaral?= Date: Thu, 8 Mar 2018 11:02:32 -0300 Subject: [PATCH 2/7] querying for item removal, without providing id --- src/RosApiCrud.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/RosApiCrud.ts b/src/RosApiCrud.ts index 1c78073..2e29311 100644 --- a/src/RosApiCrud.ts +++ b/src/RosApiCrud.ts @@ -1,5 +1,5 @@ import { RouterOSAPI, RosException } from "node-routeros"; -import { flatten } from "lodash"; +import { flatten, reduce } from "lodash"; import * as utils from "./utils"; import * as Types from "./Types"; @@ -221,11 +221,31 @@ export abstract class RouterOSAPICrud { ids = this.stringfySearchQuery(ids); this.queryVal.push("=numbers=" + ids); } - const idsForRemoval = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - if (!idsForRemoval) return this.exec("remove"); + + let idsForRemoval = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + + let queryPromise; + + if (!idsForRemoval) { + this.queryVal.push("=.proplist=.id"); + const query = this.fullQuery("/print"); + queryPromise = this.write(query); + } else { + queryPromise = Promise.resolve([]); + } let responseData; - return this.recoverDataFromChangedItems(idsForRemoval).then((response: any) => { + return queryPromise.then((data: any[]) => { + data = reduce(data, (result, value, key) => { + result.push(value.id); + return result; + }, []); + idsForRemoval = data + ""; + this.queryVal.push("=numbers=" + idsForRemoval); + return Promise.resolve(); + }).then(() => { + return this.recoverDataFromChangedItems(idsForRemoval); + }).then((response: any) => { responseData = response; return this.exec("remove"); }).then(() => { From e42aab34a987b828e0bf58802b3d8020f1e0d971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alu=C3=ADsio=20Amaral?= Date: Thu, 8 Mar 2018 15:38:09 -0300 Subject: [PATCH 3/7] move, update, unset and remove querying --- src/RosApiCrud.ts | 140 +++++---- test/3.writing.spec.js | 679 ++++++++++++++++++++++++----------------- 2 files changed, 468 insertions(+), 351 deletions(-) diff --git a/src/RosApiCrud.ts b/src/RosApiCrud.ts index 2e29311..10fb13f 100644 --- a/src/RosApiCrud.ts +++ b/src/RosApiCrud.ts @@ -131,14 +131,28 @@ export abstract class RouterOSAPICrud { */ public move(from: Types.Id, to?: string | number): Types.SocPromise { if (!Array.isArray(from)) from = [from]; - from = this.stringfySearchQuery(from); - this.queryVal.push("=numbers=" + from); + + let movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + + if (!to && movedIds) { + to = from.shift(); + from = null; + } + + if (from) { + from = this.stringfySearchQuery(from); + this.queryVal.push("=numbers=" + from); + } + if (to) { to = this.stringfySearchQuery(to); this.queryVal.push("=destination=" + to); } - const movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - return this.exec("move").then((response: any[]) => { + + return this.queryForIdsIfNeeded(movedIds).then((ids: string) => { + movedIds = ids; + return this.exec("move"); + }).then(() => { return this.recoverDataFromChangedItems(movedIds); }); } @@ -154,9 +168,14 @@ export abstract class RouterOSAPICrud { ids = this.stringfySearchQuery(ids); this.queryVal.push("=numbers=" + ids); } - this.makeQuery(data); - const updatedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - return this.exec("set").then((response: any[]) => { + + let updatedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + + return this.queryForIdsIfNeeded(updatedIds).then((ids: string) => { + updatedIds = ids; + this.makeQuery(data); + return this.exec("set"); + }).then((response: any[]) => { return this.recoverDataFromChangedItems(updatedIds); }); } @@ -172,42 +191,30 @@ export abstract class RouterOSAPICrud { ids = this.stringfySearchQuery(ids); this.queryVal.push("=numbers=" + ids); } - if (typeof properties === "string") properties = [properties]; - const $q: Types.SocPromise[] = []; - - // Saving current queryVal for reuse, since running exec will reset it - const curQueryVal = this.queryVal.slice(); - const updatedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - // Cleaning current queryVal to prevent duplication - this.queryVal = []; - properties.forEach((property) => { - // Putting back queryVal after a cleanup - this.queryVal = curQueryVal.slice(); - this.queryVal.push("=value-name=" + utils.camelCaseOrSnakeCaseToDashedCase(property)); - $q.push(this.exec("unset")); - }); - return Promise.all($q).then((data: any[]) => { - data = flatten(data); - return Promise.resolve(data); - }).then((response: any[]) => { - if (!response || !updatedIds) return Promise.resolve(response); - const promises = []; - const ids = updatedIds.split(","); - for (const id of ids) { - const promise = this.rosApi.write([ - this.pathVal + "/print", - "?.id=" + id - ]); - promises.push(promise); - } - return Promise.all(promises); - }).then((data) => { - if (!data) return Promise.resolve(data); - data = flatten(data); - data = this.treatMikrotikProperties(data); - if (!updatedIds.includes(",")) return Promise.resolve(data[0]); - return Promise.resolve(data); + let updatedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + + return this.queryForIdsIfNeeded(updatedIds).then((ids: string) => { + updatedIds = ids; + + if (typeof properties === "string") properties = [properties]; + + const $q: Types.SocPromise[] = []; + + // Saving current queryVal for reuse, since running exec will reset it + const curQueryVal = this.queryVal.slice(); + + // Cleaning current queryVal to prevent duplication + this.queryVal = []; + properties.forEach((property) => { + // Putting back queryVal after a cleanup + this.queryVal = curQueryVal.slice(); + this.queryVal.push("=value-name=" + utils.camelCaseOrSnakeCaseToDashedCase(property)); + $q.push(this.exec("unset")); + }); + return Promise.all($q); + }).then(() => { + return this.recoverDataFromChangedItems(updatedIds); }); } @@ -222,30 +229,12 @@ export abstract class RouterOSAPICrud { this.queryVal.push("=numbers=" + ids); } - let idsForRemoval = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - - let queryPromise; - - if (!idsForRemoval) { - this.queryVal.push("=.proplist=.id"); - const query = this.fullQuery("/print"); - queryPromise = this.write(query); - } else { - queryPromise = Promise.resolve([]); - } + const idsForRemoval = utils.lookForIdParameterAndReturnItsValue(this.queryVal); let responseData; - return queryPromise.then((data: any[]) => { - data = reduce(data, (result, value, key) => { - result.push(value.id); - return result; - }, []); - idsForRemoval = data + ""; - this.queryVal.push("=numbers=" + idsForRemoval); - return Promise.resolve(); - }).then(() => { - return this.recoverDataFromChangedItems(idsForRemoval); - }).then((response: any) => { + return this.queryForIdsIfNeeded(idsForRemoval).then((ids: string) => { + return this.recoverDataFromChangedItems(ids); + }).then((response: any) => { responseData = response; return this.exec("remove"); }).then(() => { @@ -530,4 +519,27 @@ export abstract class RouterOSAPICrud { }); } + /** + * If trying do any action without providing any id, just + * a query. Find all their ids and return them + * + * @param ids + */ + private queryForIdsIfNeeded(ids?: string): Promise { + if (ids) return Promise.resolve(ids); + + this.queryVal.push("=.proplist=.id"); + const query = this.fullQuery("/print"); + let queriedIds; + return this.write(query).then((data: any[]) => { + data = reduce(data, (result, value, key) => { + result.push(value.id); + return result; + }, []); + queriedIds = data + ""; + if (queriedIds) this.queryVal.push("=numbers=" + queriedIds); + return Promise.resolve(queriedIds); + }); + } + } diff --git a/test/3.writing.spec.js b/test/3.writing.spec.js index ac72897..64b0dbb 100644 --- a/test/3.writing.spec.js +++ b/test/3.writing.spec.js @@ -8,7 +8,7 @@ const expect = chai.expect; let conn, api, menu, - item, + thirdRuleModel, firstRule, secondRule, thirdRule; @@ -32,335 +32,440 @@ describe("RouterOSAPICrud", () => { describe("handling crud operations", () => { - it("should add three firewall filter rules on chain forward", (done) => { - const data = { - chain: "forward", - protocol: "tcp", - action: "accept", - comment: "first rule" - }; - - menu = api.menu("/ip firewall filter"); - menu.add(data).then((response) => { - response.should.have.property("id").and.match(/^\*/); - response.should.have.property("comment").and.be.equal(data.comment); - response.should.have.property("chain").and.be.equal(data.chain); - response.should.have.property("protocol").and.be.equal(data.protocol); - response.should.have.property("action").and.be.equal(data.action); - firstRule = response.id; - return menu.add({ - chain: "forward", - protocol: "udp", - action: "accept", - disabled: true, - comment: "second rule" - }); - }).then((response) => { - response.should.have.property("id").and.match(/^\*/); - secondRule = response.id; - return menu.add({ + describe("#add()", () => { + + it("should add three firewall filter rules on chain forward", (done) => { + const data = { chain: "forward", - protocol: "udp", + protocol: "tcp", action: "accept", - disabled: true, - comment: "third rule" + comment: "first rule" + }; + + menu = api.menu("/ip firewall filter"); + menu.add(data).then((response) => { + response.should.have.property("id").and.match(/^\*/); + response.should.have.property("comment").and.be.equal(data.comment); + response.should.have.property("chain").and.be.equal(data.chain); + response.should.have.property("protocol").and.be.equal(data.protocol); + response.should.have.property("action").and.be.equal(data.action); + firstRule = response.id; + return menu.add({ + chain: "forward", + protocol: "udp", + action: "accept", + srcAddress: "7.7.7.7", + disabled: true, + comment: "second rule" + }); + }).then((response) => { + response.should.have.property("id").and.match(/^\*/); + response.should.have.property("srcAddress").and.be.equal("7.7.7.7"); + secondRule = response.id; + return menu.add({ + chain: "forward", + protocol: "udp", + action: "accept", + disabled: true, + comment: "third rule" + }); + }).then((response) => { + response.should.have.property("id").and.match(/^\*/); + thirdRule = response.id; + done(); + }).catch((err) => { + done(err); }); - }).then((response) => { - response.should.have.property("id").and.match(/^\*/); - thirdRule = response.id; - done(); - }).catch((err) => { - done(err); + }); - }); + it("should create a model from the third rule", (done) => { + menu.where({ id: thirdRule }).first().then((filterRule) => { + filterRule.should.have.property("id").and.match(/^\*/); + + expect(filterRule).to.not.have.property("update"); + expect(filterRule).to.not.have.property("delete"); + expect(filterRule).to.not.have.property("unset"); - it("should update the first rule", (done) => { - menu.where("id", firstRule).update({ disabled: true }).then((response) => { - response.should.have.property("disabled").and.be.equal(true); - done(); - }).catch((err) => { - done(err); + thirdRuleModel = api.model(filterRule); + + expect(thirdRuleModel).to.have.property("update"); + expect(thirdRuleModel).to.have.property("delete"); + expect(thirdRuleModel).to.have.property("unset"); + + done(); + + }).catch((err) => { + done(err); + }); }); - }); - it("should unset the protocol value of the second filter rule", (done) => { - menu.unset("protocol", secondRule).then((response) => { - response.should.have.property("id").and.be.equal(secondRule); - response.should.have.property("comment").and.be.equal("second rule"); - response.should.not.have.property("protocol"); - done(); - }).catch((err) => { - done(err); + it("should add 3 rules and insert a 4th rule after the first without id", (done) => { + const webproxyMenu = api.menu("/ip proxy access"); + const data1 = { + srcAddress: "192.168.88.11", + dstHost: ":random\\-site11\\.com", + comment: "random rule 11" + }; + const data2 = { + srcAddress: "192.168.88.22", + dstHost: ":random\\-site22\\.com", + comment: "random rule 22" + }; + const data3 = { + srcAddress: "192.168.88.33", + dstHost: ":random\\-site33\\.com", + comment: "random rule 33" + }; + const data4 = { + srcAddress: "192.168.88.44", + dstHost: ":random\\-site44\\.com", + comment: "random rule 44", + placeAfter: { ...data1 } + }; + + let firstRule; + let secondRule; + + webproxyMenu.add(data1).then(() => { + return webproxyMenu.add(data2); + }).then(() => { + return webproxyMenu.add(data3); + }).then(() => { + return webproxyMenu.add(data4); + }).then(() => { + return webproxyMenu.getAll(); + }).then((response) => { + response.forEach((res) => { + if (firstRule && !secondRule) { + secondRule = res; + } else if (res.comment === data1.comment) { + firstRule = res; + } + }); + return webproxyMenu.remove(data1); + }).then(() => { + return webproxyMenu.remove(data2); + }).then(() => { + return webproxyMenu.remove(data3); + }).then(() => { + delete data4.placeAfter; + return webproxyMenu.remove(data4); + }).then(() => { + secondRule.comment.should.be.equal(data4.comment); + done(); + }).catch((err) => { + done(err); + }); }); - }); - it("should move the second rule above first rule", (done) => { - menu.move(secondRule, firstRule).then((response) => { - response.should.have.property("id").and.be.equal(secondRule); - done(); - }).catch((err) => { - done(err); + it("should add 3 rules and insert a 4th rule after the first with id", (done) => { + const webproxyMenu = api.menu("/ip proxy access"); + const data1 = { + srcAddress: "192.168.90.11", + dstHost: ":random\\-site11\\.com", + comment: "random rule 11" + }; + const data2 = { + srcAddress: "192.168.90.22", + dstHost: ":random\\-site22\\.com", + comment: "random rule 22" + }; + const data3 = { + srcAddress: "192.168.90.33", + dstHost: ":random\\-site33\\.com", + comment: "random rule 33" + }; + const data4 = { + srcAddress: "192.168.90.44", + dstHost: ":random\\-site44\\.com", + comment: "random rule 44" + }; + + let firstRule; + let secondRule; + let firstRid; + + webproxyMenu.add(data1).then((response) => { + firstRid = response.id; + return webproxyMenu.add(data2); + }).then(() => { + return webproxyMenu.add(data3); + }).then(() => { + data4.placeAfter = firstRid; + return webproxyMenu.add(data4); + }).then(() => { + return webproxyMenu.getAll(); + }).then((response) => { + response.forEach((res) => { + if (firstRule && !secondRule) { + secondRule = res; + } else if (res.comment === data1.comment) { + firstRule = res; + } + }); + return webproxyMenu.remove(data1); + }).then(() => { + return webproxyMenu.remove(data2); + }).then(() => { + return webproxyMenu.remove(data3); + }).then(() => { + delete data4.placeAfter; + return webproxyMenu.remove(data4); + }).then(() => { + secondRule.comment.should.be.equal(data4.comment); + done(); + }).catch((err) => { + done(err); + }); }); + }); - it("should create a model from the third rule", (done) => { - menu.where({ id: thirdRule }).first().then((filterRule) => { - filterRule.should.have.property("id").and.match(/^\*/); + describe("#update()", () => { - expect(filterRule).to.not.have.property("update"); - expect(filterRule).to.not.have.property("delete"); - expect(filterRule).to.not.have.property("unset"); + it("should update the first rule", (done) => { + menu.where("id", firstRule).update({ disabled: true }).then((response) => { + response.should.have.property("disabled").and.be.equal(true); + done(); + }).catch((err) => { + done(err); + }); + }); - item = api.model(filterRule); + it("should add a source address to the third rule", (done) => { + thirdRuleModel.update({ srcAddress: "6.6.6.6" }).then((result) => { + thirdRuleModel.srcAddress.should.be.equal("6.6.6.6"); + result.srcAddress.should.be.equal("6.6.6.6"); + if (thirdRuleModel !== result) done("Not the same object"); + done(); + }).catch((err) => { + done(err); + }); + }); - expect(item).to.have.property("update"); - expect(item).to.have.property("delete"); - expect(item).to.have.property("unset"); + }); - done(); + describe("#unset()", () => { - }).catch((err) => { - done(err); + it("should unset the protocol value of the second filter rule", (done) => { + menu.unset("protocol", secondRule).then((response) => { + response.should.have.property("id").and.be.equal(secondRule); + response.should.have.property("comment").and.be.equal("second rule"); + response.should.not.have.property("protocol"); + done(); + }).catch((err) => { + done(err); + }); }); - }); - it("should add a source address to the third rule", (done) => { - item.update({srcAddress: "6.6.6.6"}).then((result) => { - item.srcAddress.should.be.equal("6.6.6.6"); - result.srcAddress.should.be.equal("6.6.6.6"); - if (item !== result) done("Not the same object"); - done(); - }).catch((err) => { - done(err); + it("should query unset the source address of the second filter rule", (done) => { + menu.where("comment", "second rule").unset("srcAddress").then((response) => { + response.should.have.property("id").and.be.equal(secondRule); + response.should.have.property("comment").and.be.equal("second rule"); + response.should.not.have.property("srcAddress"); + done(); + }).catch((err) => { + done(err); + }); }); - }); - it("should unset the source address from the third rule", (done) => { - item.unset("srcAddress").then((result) => { - item.should.not.have.property("srcAddress"); - if (item !== result) done("Not the same object"); - done(); - }).catch((err) => { - done(err); + it("should unset the source address from the third rule", (done) => { + thirdRuleModel.unset("srcAddress").then((result) => { + thirdRuleModel.should.not.have.property("srcAddress"); + if (thirdRuleModel !== result) done("Not the same object"); + done(); + }).catch((err) => { + done(err); + }); }); + }); - it("should move the third rule above the first rule", (done) => { - item.move(firstRule).then((result) => { - result.should.have.property("id").and.be.equal(item.id); - if (item !== result) done("Not the same object"); - done(); - }).catch((err) => { - done(err); + describe("#move()", () => { + + it("should move the second rule above first rule", (done) => { + let foundRule; + + menu.move(secondRule, firstRule).then((response) => { + response.should.have.property("id").and.be.equal(secondRule); + return menu.getAll(); + }).then((items) => { + for (const item of items) { + if (foundRule) { + item.id.should.be.equal(firstRule); + break; + } else if (item.id === secondRule) foundRule = item.id; + } + foundRule.should.exist; + done(); + }).catch((err) => { + done(err); + }); }); - }); - it("should remove the third rule", (done) => { - item.remove().then((result) => { - result.should.have.property("id").and.be.equal(item.id); - done(); - }).catch((err) => { - done(err); + it("should move the third rule above the second rule", (done) => { + let foundRule; + + thirdRuleModel.move(secondRule).then((result) => { + result.should.have.property("id").and.be.equal(thirdRuleModel.id); + if (thirdRuleModel !== result) done("Not the same object"); + return menu.getAll(); + }).then((items) => { + for (const item of items) { + if (foundRule) { + item.id.should.be.equal(secondRule); + break; + } else if (item.id === thirdRuleModel.id) foundRule = item.id; + } + foundRule.should.exist; + done(); + }).catch((err) => { + done(err); + }); }); - }); - it("should delete the added filter rules", (done) => { - let firstId, secondId; - menu.only("id").where({ comment: "first rule" }).orWhere({ comment: "second rule" }).get().then((response) => { - response.length.should.be.equal(2); - firstId = response[0].id; - secondId = response[1].id; - return menu.remove([firstId, secondId]); - }).then((response) => { - response.length.should.be.equal(2); - response[0].should.have.property("id").and.be.equal(firstId); - response[1].should.have.property("id").and.be.equal(secondId); - done(); - }).catch((err) => { - done(err); + it("should move the third rule above the first rule by querying its comment", (done) => { + let foundRule; + + menu.where("comment", "third rule").move(firstRule).then((response) => { + response.should.have.property("id").and.be.equal(thirdRule); + return menu.getAll(); + }).then((items) => { + for (const item of items) { + if (foundRule) { + item.id.should.be.equal(firstRule); + break; + } else if (item.id === thirdRule) foundRule = item.id; + } + foundRule.should.exist; + done(); + }).catch((err) => { + done(err); + }); }); + }); - it("should add a webproxy acl and remove it without id", (done) => { - const webproxyMenu = api.menu("/ip proxy access"); - const data = { - srcAddress: "192.168.88.10", - dstHost: ":random\\-site\\.com", - comment: "random rule" - }; - webproxyMenu.add(data).then((response) => { - return webproxyMenu.remove(data); - }).then((response) => { - return webproxyMenu.where(data).get(); - }).then((response) => { - response.length.should.be.equal(0); - done(); - }).catch((err) => { - done(err); + describe("#remove()", () => { + + it("should remove the third rule", (done) => { + thirdRuleModel.remove().then((result) => { + result.should.have.property("id").and.be.equal(thirdRuleModel.id); + done(); + }).catch((err) => { + done(err); + }); }); - }); - it("should add a webproxy acl and another before it, using place-before query, and then remove both without using id", (done) => { - const webproxyMenu = api.menu("/ip proxy access"); - const data1 = { - srcAddress: "192.168.88.12", - dstHost: ":random\\-site2\\.com", - comment: "random rule 2" - }; - const data2 = { - srcAddress: "192.168.88.13", - dstHost: ":random\\-site3\\.com", - comment: "random rule 3", - placeBefore: {...data1} - }; - - let firstRule; - let secondRule; - - webproxyMenu.add(data1).then(() => { - return webproxyMenu.add(data2); - }).then(() => { - return webproxyMenu.getAll(); - }).then((response) => { - response.forEach((res) => { - if (res.comment === data1.comment || res.comment === data2.comment) { - if (!firstRule) firstRule = {...res}; - else secondRule = {...res}; - } + it("should delete the added filter rules", (done) => { + let firstId, secondId; + menu.only("id").where({ comment: "first rule" }).orWhere({ comment: "second rule" }).get().then((response) => { + response.length.should.be.equal(2); + firstId = response[0].id; + secondId = response[1].id; + return menu.remove([firstId, secondId]); + }).then((response) => { + response.length.should.be.equal(2); + response[0].should.have.property("id").and.be.equal(firstId); + response[1].should.have.property("id").and.be.equal(secondId); + done(); + }).catch((err) => { + done(err); }); - return webproxyMenu.remove(data1); - }).then(() => { - delete data2.placeBefore; - return webproxyMenu.remove(data2); - }).then((response) => { - firstRule.comment.should.be.equal(data2.comment); - secondRule.comment.should.be.equal(data1.comment); - done(); - }).catch((err) => { - done(err); }); - }); - it("should add 3 rules and insert a 4th rule after the first without id", (done) => { - const webproxyMenu = api.menu("/ip proxy access"); - const data1 = { - srcAddress: "192.168.88.11", - dstHost: ":random\\-site11\\.com", - comment: "random rule 11" - }; - const data2 = { - srcAddress: "192.168.88.22", - dstHost: ":random\\-site22\\.com", - comment: "random rule 22" - }; - const data3 = { - srcAddress: "192.168.88.33", - dstHost: ":random\\-site33\\.com", - comment: "random rule 33" - }; - const data4 = { - srcAddress: "192.168.88.44", - dstHost: ":random\\-site44\\.com", - comment: "random rule 44", - placeAfter: {...data1} - }; - - let firstRule; - let secondRule; - - webproxyMenu.add(data1).then(() => { - return webproxyMenu.add(data2); - }).then(() => { - return webproxyMenu.add(data3); - }).then(() => { - return webproxyMenu.add(data4); - }).then(() => { - return webproxyMenu.getAll(); - }).then((response) => { - response.forEach((res) => { - if (firstRule && !secondRule) { - secondRule = res; - } else if (res.comment === data1.comment) { - firstRule = res; - } + it("should add a webproxy acl and remove it without id", (done) => { + const webproxyMenu = api.menu("/ip proxy access"); + const data = { + srcAddress: "192.168.88.10", + dstHost: ":random\\-site\\.com", + comment: "random rule" + }; + webproxyMenu.add(data).then((response) => { + return webproxyMenu.remove(data); + }).then((response) => { + return webproxyMenu.where(data).get(); + }).then((response) => { + response.length.should.be.equal(0); + done(); + }).catch((err) => { + done(err); }); - return webproxyMenu.remove(data1); - }).then(() => { - return webproxyMenu.remove(data2); - }).then(() => { - return webproxyMenu.remove(data3); - }).then(() => { - delete data4.placeAfter; - return webproxyMenu.remove(data4); - }).then(() => { - secondRule.comment.should.be.equal(data4.comment); - done(); - }).catch((err) => { - done(err); }); - }); - it("should add 3 rules and insert a 4th rule after the first with id", (done) => { - const webproxyMenu = api.menu("/ip proxy access"); - const data1 = { - srcAddress: "192.168.90.11", - dstHost: ":random\\-site11\\.com", - comment: "random rule 11" - }; - const data2 = { - srcAddress: "192.168.90.22", - dstHost: ":random\\-site22\\.com", - comment: "random rule 22" - }; - const data3 = { - srcAddress: "192.168.90.33", - dstHost: ":random\\-site33\\.com", - comment: "random rule 33" - }; - const data4 = { - srcAddress: "192.168.90.44", - dstHost: ":random\\-site44\\.com", - comment: "random rule 44" - }; - - let firstRule; - let secondRule; - let firstRid; - - webproxyMenu.add(data1).then((response) => { - firstRid = response.id; - return webproxyMenu.add(data2); - }).then(() => { - return webproxyMenu.add(data3); - }).then(() => { - data4.placeAfter = firstRid; - return webproxyMenu.add(data4); - }).then(() => { - return webproxyMenu.getAll(); - }).then((response) => { - response.forEach((res) => { - if (firstRule && !secondRule) { - secondRule = res; - } else if (res.comment === data1.comment) { - firstRule = res; - } + it("should add a webproxy acl and another before it, using place-before query, and then remove both without using id", (done) => { + const webproxyMenu = api.menu("/ip proxy access"); + const data1 = { + srcAddress: "192.168.88.12", + dstHost: ":random\\-site2\\.com", + comment: "random rule 2" + }; + const data2 = { + srcAddress: "192.168.88.13", + dstHost: ":random\\-site3\\.com", + comment: "random rule 3", + placeBefore: { ...data1 } + }; + + let firstRule; + let secondRule; + + webproxyMenu.add(data1).then(() => { + return webproxyMenu.add(data2); + }).then(() => { + return webproxyMenu.getAll(); + }).then((response) => { + response.forEach((res) => { + if (res.comment === data1.comment || res.comment === data2.comment) { + if (!firstRule) firstRule = { ...res }; + else secondRule = { ...res }; + } + }); + return webproxyMenu.remove(data1); + }).then(() => { + delete data2.placeBefore; + return webproxyMenu.remove(data2); + }).then((response) => { + firstRule.comment.should.be.equal(data2.comment); + secondRule.comment.should.be.equal(data1.comment); + done(); + }).catch((err) => { + done(err); + }); + }); + + it("should add 2 webproxy rules and remove then by querying using OR", (done) => { + const webproxyMenu = api.menu("/ip proxy access"); + const data1 = { + srcAddress: "192.168.90.1", + dstHost: ":random\\-site101\\.com", + comment: "random rule 1 so pick me up" + }; + const data2 = { + srcAddress: "192.168.90.2", + dstHost: ":random\\-site202\\.com", + comment: "random rule 2 so tear me down" + }; + + webproxyMenu.add(data1).then(() => { + return webproxyMenu.add(data2); + }).then(() => { + return webproxyMenu.where("comment", "random rule 1 so pick me up") + .orWhere("comment", "random rule 2 so tear me down") + .remove(); + }).then((removedItems) => { + removedItems.should.have.property("length").and.be.equal(2); + removedItems[0].should.have.property("comment").and.be.equal(data1.comment); + removedItems[1].should.have.property("comment").and.be.equal(data2.comment); + done(); + }).catch((err) => { + done(err); }); - return webproxyMenu.remove(data1); - }).then(() => { - return webproxyMenu.remove(data2); - }).then(() => { - return webproxyMenu.remove(data3); - }).then(() => { - delete data4.placeAfter; - return webproxyMenu.remove(data4); - }).then(() => { - secondRule.comment.should.be.equal(data4.comment); - done(); - }).catch((err) => { - done(err); }); + }); + }); after("should disconnect", (done) => { From 4ac70b09d9426681e6a760e7f69044bb2a700c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alu=C3=ADsio=20Amaral?= Date: Thu, 8 Mar 2018 15:54:42 -0300 Subject: [PATCH 4/7] reverted move method since it did not work by querying --- src/RosApiCrud.ts | 22 ++++------------------ test/3.writing.spec.js | 20 -------------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/src/RosApiCrud.ts b/src/RosApiCrud.ts index 10fb13f..76d9fc6 100644 --- a/src/RosApiCrud.ts +++ b/src/RosApiCrud.ts @@ -131,28 +131,14 @@ export abstract class RouterOSAPICrud { */ public move(from: Types.Id, to?: string | number): Types.SocPromise { if (!Array.isArray(from)) from = [from]; - - let movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - - if (!to && movedIds) { - to = from.shift(); - from = null; - } - - if (from) { - from = this.stringfySearchQuery(from); - this.queryVal.push("=numbers=" + from); - } - + from = this.stringfySearchQuery(from); + this.queryVal.push("=numbers=" + from); if (to) { to = this.stringfySearchQuery(to); this.queryVal.push("=destination=" + to); } - - return this.queryForIdsIfNeeded(movedIds).then((ids: string) => { - movedIds = ids; - return this.exec("move"); - }).then(() => { + const movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + return this.exec("move").then((response: any[]) => { return this.recoverDataFromChangedItems(movedIds); }); } diff --git a/test/3.writing.spec.js b/test/3.writing.spec.js index 64b0dbb..de13943 100644 --- a/test/3.writing.spec.js +++ b/test/3.writing.spec.js @@ -325,26 +325,6 @@ describe("RouterOSAPICrud", () => { }); }); - it("should move the third rule above the first rule by querying its comment", (done) => { - let foundRule; - - menu.where("comment", "third rule").move(firstRule).then((response) => { - response.should.have.property("id").and.be.equal(thirdRule); - return menu.getAll(); - }).then((items) => { - for (const item of items) { - if (foundRule) { - item.id.should.be.equal(firstRule); - break; - } else if (item.id === thirdRule) foundRule = item.id; - } - foundRule.should.exist; - done(); - }).catch((err) => { - done(err); - }); - }); - }); describe("#remove()", () => { From 431c446960b41ff84706d297ff75a128a2ca8177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alu=C3=ADsio=20Amaral?= Date: Thu, 8 Mar 2018 15:58:24 -0300 Subject: [PATCH 5/7] enable and disable querying --- src/RosApiCrud.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/RosApiCrud.ts b/src/RosApiCrud.ts index 76d9fc6..ee75c15 100644 --- a/src/RosApiCrud.ts +++ b/src/RosApiCrud.ts @@ -74,8 +74,11 @@ export abstract class RouterOSAPICrud { ids = this.stringfySearchQuery(ids); this.queryVal.push("=numbers=" + ids); } - const disabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - return this.exec("disable").then((response: any[]) => { + let disabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + return this.queryForIdsIfNeeded(disabledIds).then((ids: string) => { + disabledIds = ids; + return this.exec("disable"); + }).then((response: any[]) => { return this.recoverDataFromChangedItems(disabledIds); }); } @@ -99,8 +102,11 @@ export abstract class RouterOSAPICrud { ids = this.stringfySearchQuery(ids); this.queryVal.push("=numbers=" + ids); } - const enabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - return this.exec("enable").then((response: any[]) => { + let enabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + return this.queryForIdsIfNeeded(enabledIds).then((ids: string) => { + enabledIds = ids; + return this.exec("enable"); + }).then((response: any[]) => { return this.recoverDataFromChangedItems(enabledIds); }); } From 97cd5cb3e6da6e27bfd085fad8abe9c711a3e5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alu=C3=ADsio=20Amaral?= Date: Thu, 8 Mar 2018 16:27:04 -0300 Subject: [PATCH 6/7] moveAbove method for moving when querying --- src/RosApiCrud.ts | 18 +++++++++++++++++- test/3.writing.spec.js | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/RosApiCrud.ts b/src/RosApiCrud.ts index ee75c15..9f5c1a8 100644 --- a/src/RosApiCrud.ts +++ b/src/RosApiCrud.ts @@ -144,7 +144,23 @@ export abstract class RouterOSAPICrud { this.queryVal.push("=destination=" + to); } const movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); - return this.exec("move").then((response: any[]) => { + return this.exec("move").then(() => { + return this.recoverDataFromChangedItems(movedIds); + }); + } + + /** + * Move a queried rule above another. + * + * @param to where to move the queried rule + */ + public moveAbove(to?: string): Types.SocPromise { + let movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal); + return this.queryForIdsIfNeeded(movedIds).then((ids: string) => { + movedIds = ids; + if (to) this.queryVal.push("=destination=" + to); + return this.exec("move"); + }).then(() => { return this.recoverDataFromChangedItems(movedIds); }); } diff --git a/test/3.writing.spec.js b/test/3.writing.spec.js index de13943..2173289 100644 --- a/test/3.writing.spec.js +++ b/test/3.writing.spec.js @@ -325,6 +325,26 @@ describe("RouterOSAPICrud", () => { }); }); + it("should move the third rule above the first rule by querying its comment", (done) => { + let foundRule; + + menu.where("comment", "third rule").moveAbove(firstRule).then((response) => { + response.should.have.property("id").and.be.equal(thirdRule); + return menu.getAll(); + }).then((items) => { + for (const item of items) { + if (foundRule) { + item.id.should.be.equal(firstRule); + break; + } else if (item.id === thirdRule) foundRule = item.id; + } + foundRule.should.exist; + done(); + }).catch((err) => { + done(err); + }); + }); + }); describe("#remove()", () => { From 76fcd36a5c59afbec45e030bde2cd3ea863662b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alu=C3=ADsio=20Amaral?= Date: Thu, 8 Mar 2018 16:29:06 -0300 Subject: [PATCH 7/7] v0.10.0 Closes #1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9846b2e..60516a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "routeros-client", - "version": "0.9.0", + "version": "0.10.0", "description": "Abstraction layer over the node-routeros API", "main": "./dist/index", "types": "./dist/index",