[incubator-openwhisk-client-js] branch master updated: travis npm publish (#84)
This is an automated email from the ASF dual-hosted git repository. rabbah pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-client-js.git The following commit(s) were added to refs/heads/master by this push: new 19fdd98 travis npm publish (#84) 19fdd98 is described below commit 19fdd98401607eff5feae4bd1078c2a76e6cd227 Author: Carlos SantanaAuthorDate: Thu Nov 9 16:15:18 2017 -0500 travis npm publish (#84) --- .npmignore | 5 + .travis.yml | 33 ++--- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.npmignore b/.npmignore new file mode 100644 index 000..c812662 --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +tools/ +test/ +.gitignore +.travis.yml +CONTRIBUTIN.md diff --git a/.travis.yml b/.travis.yml index 3c040bf..1082c36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,25 @@ language: node_js node_js: - - "6" - +- '6' sudo: required - services: - - docker - +- docker before_install: - - cd $TRAVIS_BUILD_DIR - +- cd $TRAVIS_BUILD_DIR install: - - ./tools/travis/setupscan.sh - - cd $TRAVIS_BUILD_DIR - - ./tools/travis/setup.sh - - cd $TRAVIS_BUILD_DIR - +- "./tools/travis/setupscan.sh" +- cd $TRAVIS_BUILD_DIR +- "./tools/travis/setup.sh" +- cd $TRAVIS_BUILD_DIR script: - - ./tools/travis/scancode.sh - - cd $TRAVIS_BUILD_DIR - - ./tools/travis/build.sh +- "./tools/travis/scancode.sh" +- cd $TRAVIS_BUILD_DIR +- "./tools/travis/build.sh" +deploy: + provider: npm + email: apacheopenwh...@gmail.com + api_key: +secure: KGzgdBzEAm3hqascSOdu+V9NbTSPo+DxWblIXuAPxXhWb4s/Fej+FFd/WwHIlt8P1+n6azUcJqOACihtIe53Wvwvof4yPItBornHJifnQdSlPcjjOGPm5eldfqao4ubzx2V78yI92vwPad0KPEMvEjr2pa/ZTcsT7T67OaN89Swib2XS+pNOmTK3b8dcUOAKon6UkSz4/ssE0VLclaUwL54sEntQxhnM+OU1B5/4Ky9mwi8kkGlC9T4zS71e0kO9WKSvfMF31kpHF1EFsUHFdo8z1DsPoxD3z4oNHJCq4GOGeGUmIzGoI/WZYtod0gcCVfdE0/2JVaaegrO6jerntj90JXCX6HJgIVBUqJrOroooaniEB/oNNp8MmrNywHMRLIXNoC5mrv47KfFNB51OW6rHQVsre3rW86lpG0T5Q4B2CFZTEvGeTQbRUptiecPHuAlMc/qzR2wwzvPgyjGtDMFbeaEdvUoE [...] + on: +tags: true +repo: apache/incubator-openwhisk-client-js -- To stop receiving notification emails like this one, please contact ['"commits@openwhisk.apache.org" '].
[incubator-openwhisk-release] 01/01: first commit
This is an automated email from the ASF dual-hosted git repository. csantanapr pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-release.git commit af7a34ba92d37756e90f0c465c8e3bd59c5407a1 Author: Carlos SantanaAuthorDate: Thu Nov 9 11:13:39 2017 -0500 first commit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md new file mode 100644 index 000..47c3c02 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# incubator-openwhisk-release -- To stop receiving notification emails like this one, please contact "commits@openwhisk.apache.org" .
[incubator-openwhisk-release] branch master created (now af7a34b)
This is an automated email from the ASF dual-hosted git repository. csantanapr pushed a change to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-release.git. at af7a34b first commit This branch includes the following new commits: new af7a34b first commit The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "add" were already present in the repository and have only been added to this reference. -- To stop receiving notification emails like this one, please contact ['"commits@openwhisk.apache.org"'].
[incubator-openwhisk-apigateway] branch master updated: Implement request.path for path parameter mapping
This is an automated email from the ASF dual-hosted git repository. mhamann pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-apigateway.git The following commit(s) were added to refs/heads/master by this push: new cd556b2 Implement request.path for path parameter mapping cd556b2 is described below commit cd556b2267436440bb3eda2ce3da3f2427bfe344 Author: Alex SongAuthorDate: Wed Nov 8 14:08:49 2017 -0500 Implement request.path for path parameter mapping --- doc/v2/management_interface_v2.md | 2 ++ scripts/lua/policies/backendRouting.lua | 12 +--- scripts/lua/routing.lua | 3 ++- tools/travis/build.sh | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/doc/v2/management_interface_v2.md b/doc/v2/management_interface_v2.md index 8518ac6..e8dc851 100644 --- a/doc/v2/management_interface_v2.md +++ b/doc/v2/management_interface_v2.md @@ -308,6 +308,8 @@ See below for a list of policies that are supported in the gateway and how they } ``` * `target-url`: the backend url +* _optional:_ to pass the path of your managed url down to the `target-url`, append `/${request.path}` to the end of `target-url`. +Eg. `"target-url": "https://openwhisk.ng.bluemix.net/api/some/action/path.http/${request.path}"` * `verb`: the method to use when invoking the target-url (use "keep" to use the keep the same verb as the API) To set a different `target-url` for different paths, use the `operation-switch` policy inside `x-gateway-configuration`. diff --git a/scripts/lua/policies/backendRouting.lua b/scripts/lua/policies/backendRouting.lua index bbe21eb..b89f7d9 100644 --- a/scripts/lua/policies/backendRouting.lua +++ b/scripts/lua/policies/backendRouting.lua @@ -26,17 +26,23 @@ local logger = require "lib/logger" local _M = {} --- Set upstream based on the backendUrl -function _M.setRoute(backendUrl) +function _M.setRoute(backendUrl, gatewayPath) local u = url.parse(backendUrl) if u.scheme == nil then u = url.parse(utils.concatStrings({'http://', backendUrl})) end + -- pass down gateway path to upstream path if $(request.path) is specified at the end of backendUrl + if u.path:sub(-15) == '$(request.path)' then +u.path = utils.concatStrings({u.path:sub(1, -16), u.path:sub(-16, -16) == '/' and '' or '/', gatewayPath}) +ngx.req.set_uri(u.path) + else +ngx.req.set_uri(getUriPath(u.path)) + end ngx.var.backendUrl = backendUrl - ngx.req.set_uri(getUriPath(u.path)) setUpstream(u) end Set dynamic route based on the based on the header that is passed in +--- Set dynamic route based on the header that is passed in function _M.setDynamicRoute(obj) local whitelist = obj.whitelist for k in pairs(whitelist) do diff --git a/scripts/lua/routing.lua b/scripts/lua/routing.lua index 4eec452..3fea15b 100644 --- a/scripts/lua/routing.lua +++ b/scripts/lua/routing.lua @@ -22,6 +22,7 @@ local cjson = require "cjson" local url = require "url" local utils = require "lib/utils" local request = require "lib/request" +local logger = require "lib/logger" -- load policies local security = require "policies/security" local mapping = require "policies/mapping" @@ -85,7 +86,7 @@ function _M.processCall(dataStore) setVerb(opFields.backendMethod) end -- Set backend upstream and uri - backendRouting.setRoute(opFields.backendUrl) + backendRouting.setRoute(opFields.backendUrl, gatewayPath) -- Parse policies if opFields.policies ~= nil then parsePolicies(dataStore, opFields.policies, key) diff --git a/tools/travis/build.sh b/tools/travis/build.sh index 731a74f..114516c 100755 --- a/tools/travis/build.sh +++ b/tools/travis/build.sh @@ -50,7 +50,7 @@ export OPENWHISK_HOME=$WHISKDIR # Tests cd $WHISKDIR cat whisk.properties -WSK_TESTS_DEPS_EXCLUDE="-x :core:swift3Action:distDocker -x :core:pythonAction:distDocker -x :core:javaAction:distDocker -x :core:nodejsAction:distDocker -x :core:actionProxy:distDocker -x :sdk:docker:distDocker -x :core:python2Action:copyFiles -x :core:python2Action:distDocker -x :tests:dat:blackbox:badaction:distDocker -x :tests:dat:blackbox:badproxy:distDocker" +WSK_TESTS_DEPS_EXCLUDE="-x :core:swift3Action:distDocker -x :core:pythonAction:distDocker -x :core:javaAction:distDocker -x :core:nodejsAction:distDocker -x :core:actionProxy:distDocker -x :sdk:docker:distDocker -x :core:python2Action:distDocker -x :tests:dat:blackbox:badaction:distDocker -x :tests:dat:blackbox:badproxy:distDocker" TERM=dumb ./gradlew tests:test --tests apigw.healthtests.* ${WSK_TESTS_DEPS_EXCLUDE} sleep 60 TERM=dumb ./gradlew tests:test --tests whisk.core.apigw.* ${WSK_TESTS_DEPS_EXCLUDE} -- To stop receiving notification emails like this one, please contact ['"commits@openwhisk.apache.org" '].
[incubator-openwhisk-client-js] branch master updated: Support retrieving status and configuration of feed triggers (#80)
This is an automated email from the ASF dual-hosted git repository. csantanapr pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-client-js.git The following commit(s) were added to refs/heads/master by this push: new 784df05 Support retrieving status and configuration of feed triggers (#80) 784df05 is described below commit 784df0587b8b453738468d6b3b298a8ccc963b5e Author: Adnan BaruniAuthorDate: Thu Nov 9 09:41:39 2017 -0600 Support retrieving status and configuration of feed triggers (#80) * add ability to retrieve feeds * add to integration test --- README.md | 1 + lib/feeds.js | 4 test/integration/feeds.test.js | 13 - test/unit/feeds.test.js| 22 ++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 159b3e3..057e276 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,7 @@ ow.triggers.get({name: '...'}) ow.rules.get({name: '...'}) ow.namespaces.get({name: '...'}) ow.packages.get({name: '...'}) +ow.feeds.get({name: '...'}) ``` The following optional parameters are supported: diff --git a/lib/feeds.js b/lib/feeds.js index 37954cd..f8cdc28 100644 --- a/lib/feeds.js +++ b/lib/feeds.js @@ -21,6 +21,10 @@ class Feeds { return this.feed('CREATE', options) } + get (options) { +return this.feed('READ', options) + } + feed (event, options) { if (!this.feed_name(options)) { throw new Error(messages.MISSING_FEED_NAME_ERROR) diff --git a/test/integration/feeds.test.js b/test/integration/feeds.test.js index ec5c0c6..1ebd7b8 100644 --- a/test/integration/feeds.test.js +++ b/test/integration/feeds.test.js @@ -38,11 +38,14 @@ tempTest('create and delete a feed', t => { } return triggers.create({triggerName: 'sample_feed_trigger'}).then(() => feeds.create(feed_params)).then(result => { t.is(result.response.success, true) -return feeds.delete(feed_params).then(feed_result => { - t.is(feed_result.response.success, true) - return triggers.delete({triggerName: 'sample_feed_trigger'}).then(() => { -t.pass() - }) +return feeds.get(feed_params).then(get_result => { + t.is(get_result.response.success, true) + return feeds.delete(feed_params).then(feed_result => { +t.is(feed_result.response.success, true) +return triggers.delete({triggerName: 'sample_feed_trigger'}).then(() => { + t.pass() +}) + }).catch(errors) }).catch(errors) }).catch(errors) }) diff --git a/test/unit/feeds.test.js b/test/unit/feeds.test.js index e93f30f..d68ef6f 100644 --- a/test/unit/feeds.test.js +++ b/test/unit/feeds.test.js @@ -206,6 +206,28 @@ test('should be able to create feed using feedName with params', t => { return feeds.create({feedName: feed_name, trigger: trigger_name, params}) }) +test('should be able to get feed', t => { + const feed_name = 'feed_name' + const api_key = 'username:password' + const trigger_name = '/trigger_ns/trigger_name' + const client = {} + client.options = { api_key } + + const ns = '_' + const feeds = new Feeds(client) + + client.request = (method, path, options) => { +t.is(method, 'POST') +t.is(path, `namespaces/${ns}/actions/${feed_name}`) +t.deepEqual(options.qs, {blocking: true}) +t.deepEqual(options.body, {authKey: client.options.api_key, lifecycleEvent: 'READ', triggerName: `${trigger_name}`}) + } + + t.plan(4) + + return feeds.get({name: feed_name, trigger: trigger_name}) +}) + test('should throw errors without trigger parameter ', t => { const ns = '_' const client = { options: {} } -- To stop receiving notification emails like this one, please contact ['"commits@openwhisk.apache.org" '].
[incubator-openwhisk] branch master updated: Reduce memory consumption for action invocation (#2730)
This is an automated email from the ASF dual-hosted git repository. rabbah pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git The following commit(s) were added to refs/heads/master by this push: new 9be13e2 Reduce memory consumption for action invocation (#2730) 9be13e2 is described below commit 9be13e2c6d581f42b60351f05a11e4aa71ca31d9 Author: James DubeeAuthorDate: Thu Nov 9 09:55:19 2017 -0500 Reduce memory consumption for action invocation (#2730) --- .../core/database/ArtifactStoreProvider.scala | 2 + .../whisk/core/database/CouchDbRestStore.scala | 12 +- .../whisk/core/database/CouchDbStoreProvider.scala | 2 + .../core/database/RemoteCacheInvalidation.scala| 5 + .../DocumentReader.scala} | 22 +-- .../src/main/scala/whisk/core/entity/Exec.scala| 155 - .../main/scala/whisk/core/entity/WhiskAction.scala | 147 +++ .../main/scala/whisk/core/entity/WhiskEntity.scala | 14 ++ .../main/scala/whisk/core/entity/WhiskStore.scala | 11 +- .../src/main/scala/whisk/http/ErrorResponse.scala | 7 + .../main/scala/whisk/core/controller/Actions.scala | 16 ++- .../scala/whisk/core/controller/Controller.scala | 5 +- .../scala/whisk/core/controller/WebActions.scala | 14 +- .../controller/actions/PostActionActivation.scala | 4 +- .../core/controller/actions/PrimitiveActions.scala | 2 +- .../core/controller/actions/SequenceActions.scala | 16 +-- .../scala/whisk/core/entitlement/Entitlement.scala | 4 + .../core/loadBalancer/LoadBalancerService.scala| 12 +- .../core/controller/test/ActionsApiTests.scala | 13 ++ .../core/controller/test/ActivationsApiTests.scala | 7 +- .../controller/test/ControllerTestCommon.scala | 2 +- .../core/controller/test/WebActionsApiTests.scala | 8 +- .../scala/whisk/core/entity/test/ExecHelpers.scala | 9 ++ 23 files changed, 432 insertions(+), 57 deletions(-) diff --git a/common/scala/src/main/scala/whisk/core/database/ArtifactStoreProvider.scala b/common/scala/src/main/scala/whisk/core/database/ArtifactStoreProvider.scala index 2080d47..e8cac18 100644 --- a/common/scala/src/main/scala/whisk/core/database/ArtifactStoreProvider.scala +++ b/common/scala/src/main/scala/whisk/core/database/ArtifactStoreProvider.scala @@ -23,6 +23,7 @@ import spray.json.RootJsonFormat import whisk.common.Logging import whisk.core.WhiskConfig import whisk.spi.Spi +import whisk.core.entity.DocumentReader /** * An Spi for providing ArtifactStore implementations @@ -32,6 +33,7 @@ trait ArtifactStoreProvider extends Spi { name: WhiskConfig => String, useBatching: Boolean = false)( implicit jsonFormat: RootJsonFormat[D], +docReader: DocumentReader, actorSystem: ActorSystem, logging: Logging, materializer: ActorMaterializer): ArtifactStore[D] diff --git a/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala b/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala index 0e66aee..298cf09 100644 --- a/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala +++ b/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala @@ -36,6 +36,7 @@ import whisk.core.entity.DocInfo import whisk.core.entity.DocRevision import whisk.core.entity.WhiskDocument import whisk.http.Messages +import whisk.core.entity.DocumentReader /** * Basic client to put and delete artifacts in a data store. @@ -58,7 +59,8 @@ class CouchDbRestStore[DocumentAbstraction <: DocumentSerializer](dbProtocol: St implicit system: ActorSystem, val logging: Logging, jsonFormat: RootJsonFormat[DocumentAbstraction], - materializer: ActorMaterializer) + materializer: ActorMaterializer, + docReader: DocumentReader) extends ArtifactStore[DocumentAbstraction] with DefaultJsonProtocol { @@ -223,7 +225,13 @@ class CouchDbRestStore[DocumentAbstraction <: DocumentSerializer](dbProtocol: St e match { case Right(response) => transid.finished(this, start, s"[GET] '$dbName' completed: found document '$doc'") - val asFormat = jsonFormat.read(response) + + val asFormat = try { +docReader.read(ma, response) + } catch { +case e: Exception => jsonFormat.read(response) + } + if (asFormat.getClass != ma.runtimeClass) { throw DocumentTypeMismatchException( s"document type ${asFormat.getClass} did not match expected type ${ma.runtimeClass}.") diff --git a/common/scala/src/main/scala/whisk/core/database/CouchDbStoreProvider.scala b/common/scala/src/main/scala/whisk/core/database/CouchDbStoreProvider.scala index d2c08dd..08b915e 100644 --- a/common/scala/src/main/scala/whisk/core/database/CouchDbStoreProvider.scala
[incubator-openwhisk] branch master updated: Fix the example of default parameters (#2934)
This is an automated email from the ASF dual-hosted git repository. rabbah pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git The following commit(s) were added to refs/heads/master by this push: new c125df2 Fix the example of default parameters (#2934) c125df2 is described below commit c125df2a3378a7809b7c378a5ec95295f02c9037 Author: Jonathas AmaralAuthorDate: Thu Nov 9 12:37:42 2017 -0200 Fix the example of default parameters (#2934) Update example that has place name overwritten by 'Washington, DC' --- docs/actions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/actions.md b/docs/actions.md index 91d2bf8..38c1801 100644 --- a/docs/actions.md +++ b/docs/actions.md @@ -258,7 +258,7 @@ Rather than pass all the parameters to an action every time, you can bind certai ```json { "name": "Bernie", -"place": "Vermont" +"place": "Washington, DC" } ``` -- To stop receiving notification emails like this one, please contact ['"commits@openwhisk.apache.org" '].
[incubator-openwhisk] branch master updated: Cleanup view for entities and augment activations view. (#2760)
This is an automated email from the ASF dual-hosted git repository. cbickel pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git The following commit(s) were added to refs/heads/master by this push: new b605943 Cleanup view for entities and augment activations view. (#2760) b605943 is described below commit b605943856d82b4253fa4539137e4e106d2fb07b Author: rodric rabbahAuthorDate: Thu Nov 9 06:45:40 2017 -0500 Cleanup view for entities and augment activations view. (#2760) - Factor out design doc name to ansible. - Separate index view into its own design doc. This will cut the main view file size in half which will make activation list faster. It replicates the view in a separate design doc, so the total savings are zero. The additional compute overhead for the ddoc though is worth considering. The upshot: we can adjust filters separately. - Add package prefix if it exists to activation filter. - Allow for filtering activations by package name. --- ...ign_document_for_activations_db_filters_v2.json | 9 ++ ...isks_design_document_for_activations_db_v2.json | 9 ++ .../whisks_design_document_for_entities_db_v2.json | 21 + ansible/group_vars/all | 3 + ansible/roles/controller/tasks/deploy.yml | 5 +- ansible/roles/invoker/tasks/deploy.yml | 3 + ansible/tasks/recreateViews.yml| 3 + ansible/templates/whisk.properties.j2 | 5 +- .../src/main/scala/whisk/core/WhiskConfig.scala| 25 -- .../main/scala/whisk/core/entity/EntityPath.scala | 5 ++ .../scala/whisk/core/entity/WhiskActivation.scala | 60 ++--- .../main/scala/whisk/core/entity/WhiskStore.scala | 51 +--- .../src/main/scala/whisk/http/ErrorResponse.scala | 3 +- .../scala/whisk/core/controller/Activations.scala | 55 ++-- .../main/scala/whisk/core/invoker/Invoker.scala| 15 +++- .../core/controller/test/ActivationsApiTests.scala | 59 +++-- .../scala/whisk/core/database/test/DbUtils.scala | 11 ++- .../scala/whisk/core/entity/test/ViewTests.scala | 97 +- 18 files changed, 297 insertions(+), 142 deletions(-) diff --git a/ansible/files/whisks_design_document_for_activations_db_filters_v2.json b/ansible/files/whisks_design_document_for_activations_db_filters_v2.json new file mode 100644 index 000..14eb0b0 --- /dev/null +++ b/ansible/files/whisks_design_document_for_activations_db_filters_v2.json @@ -0,0 +1,9 @@ +{ + "_id": "_design/whisks-filters.v2", + "language": "javascript", + "views": { +"activations": { + "map": "function (doc) {\n var PATHSEP = \"/\";\n var isActivation = function (doc) { return (doc.activationId !== undefined) };\n var summarize = function (doc) {\nvar endtime = doc.end !== 0 ? doc.end : undefined;\n return {\nnamespace: doc.namespace,\nname: doc.name,\n version: doc.version,\npublish: doc.publish,\nannotations: doc.annotations,\nactivationId: doc.activationId,\nstart: doc.start,\nend: endtim [...] +} + } +} diff --git a/ansible/files/whisks_design_document_for_activations_db_v2.json b/ansible/files/whisks_design_document_for_activations_db_v2.json new file mode 100644 index 000..be57879 --- /dev/null +++ b/ansible/files/whisks_design_document_for_activations_db_v2.json @@ -0,0 +1,9 @@ +{ + "_id": "_design/whisks.v2", + "language": "javascript", + "views": { +"activations": { + "map": "function (doc) {\n var PATHSEP = \"/\";\n var isActivation = function (doc) { return (doc.activationId !== undefined) };\n var summarize = function (doc) {\nvar endtime = doc.end !== 0 ? doc.end : undefined;\n return {\nnamespace: doc.namespace,\nname: doc.name,\n version: doc.version,\npublish: doc.publish,\nannotations: doc.annotations,\nactivationId: doc.activationId,\nstart: doc.start,\nend: endtim [...] +} + } +} diff --git a/ansible/files/whisks_design_document_for_entities_db_v2.json b/ansible/files/whisks_design_document_for_entities_db_v2.json new file mode 100644 index 000..97ed91c --- /dev/null +++ b/ansible/files/whisks_design_document_for_entities_db_v2.json @@ -0,0 +1,21 @@ +{ + "_id": "_design/whisks.v2", + "language": "javascript", + "views": { +"rules": { + "map": "function (doc) {\n var PATHSEP = \"/\";\n var isRule = function (doc) { return (doc.trigger !== undefined) };\n if (isRule(doc)) try {\nvar ns = doc.namespace.split(PATHSEP);\nvar root = ns[0];\nvar date = doc.updated;\nvar value = {\n namespace: doc.namespace,\n name: doc.name,\n version: doc.version,\n publish: doc.publish,\n annotations: doc.annotations\n};\nemit([doc.namespace, date], value);\nif (root !==