Merge branch 'release' into 'master'
ReleaseVersion-0.1.0
See merge request agens_tech/agensbrowser-nodejs!33
diff --git a/backend/package-lock.json b/backend/package-lock.json
index 384407f..509a6d5 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -21,6 +21,18 @@
"pg": ">= 6.1.2"
}
},
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@@ -29,11 +41,48 @@
"color-convert": "^1.9.0"
}
},
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
+ "array.prototype.map": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
+ "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "is-string": "^1.0.4"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -47,6 +96,12 @@
"safe-buffer": "5.1.2"
}
},
+ "binary-extensions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+ "dev": true
+ },
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
@@ -73,6 +128,21 @@
"concat-map": "0.0.1"
}
},
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
"buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
@@ -83,6 +153,12 @@
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -93,6 +169,61 @@
"supports-color": "^5.3.0"
}
},
+ "chokidar": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
+ "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.3.0"
+ }
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -106,6 +237,21 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -140,6 +286,18 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
+ "cookiejar": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz",
+ "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -167,6 +325,12 @@
"ms": "2.0.0"
}
},
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -175,6 +339,12 @@
"object-keys": "^1.0.12"
}
},
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@@ -185,11 +355,23 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -221,6 +403,35 @@
"string.prototype.trimstart": "^1.0.1"
}
},
+ "es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+ "dev": true
+ },
+ "es-get-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
+ "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
+ "dev": true,
+ "requires": {
+ "es-abstract": "^1.17.4",
+ "has-symbols": "^1.0.1",
+ "is-arguments": "^1.0.4",
+ "is-map": "^2.0.1",
+ "is-set": "^2.0.1",
+ "is-string": "^1.0.5",
+ "isarray": "^2.0.5"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ }
+ }
+ },
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
@@ -241,6 +452,12 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -317,6 +534,21 @@
}
}
},
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
@@ -331,6 +563,42 @@
"unpipe": "~1.0.0"
}
},
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ }
+ },
+ "form-data": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+ "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "formidable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
+ "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==",
+ "dev": true
+ },
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -341,16 +609,64 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true
+ },
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
},
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -369,6 +685,12 @@
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
},
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
@@ -393,6 +715,16 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
@@ -403,11 +735,32 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
+ "is-arguments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+ "dev": true
+ },
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "dev": true
+ },
"is-callable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
@@ -418,6 +771,45 @@
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
},
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
+ "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+ "dev": true
+ },
"is-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz",
@@ -426,6 +818,18 @@
"has-symbols": "^1.0.1"
}
},
+ "is-set": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
+ "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
+ "dev": true
+ },
+ "is-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "dev": true
+ },
"is-symbol": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
@@ -434,11 +838,43 @@
"has-symbols": "^1.0.1"
}
},
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
+ "iterate-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
+ "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
+ "dev": true
+ },
+ "iterate-value": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
+ "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
+ "dev": true,
+ "requires": {
+ "es-get-iterator": "^1.0.2",
+ "iterate-iterator": "^1.0.1"
+ }
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@@ -455,11 +891,29 @@
"strip-bom": "^3.0.0"
}
},
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
"lodash": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
+ "log-symbols": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+ "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2"
+ }
+ },
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -506,6 +960,80 @@
"brace-expansion": "^1.1.7"
}
},
+ "mocha": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.0.tgz",
+ "integrity": "sha512-sI0gaI1I/jPVu3KFpnveWGadfe3JNBAENqgTUPgLZAUppu725zS2mrVztzAgIR8DUscuS4doEBTx9LATC+HSeA==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.3.1",
+ "debug": "3.2.6",
+ "diff": "4.0.2",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "4.1.0",
+ "glob": "7.1.6",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "3.0.0",
+ "minimatch": "3.0.4",
+ "ms": "2.1.2",
+ "object.assign": "4.1.0",
+ "promise.allsettled": "1.0.2",
+ "serialize-javascript": "4.0.0",
+ "strip-json-comments": "3.0.1",
+ "supports-color": "7.1.0",
+ "which": "2.0.2",
+ "wide-align": "1.1.3",
+ "workerpool": "6.0.0",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
"morgan": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
@@ -544,6 +1072,12 @@
"validate-npm-package-license": "^3.0.1"
}
},
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
"npm-run-all": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
@@ -560,6 +1094,12 @@
"string.prototype.padend": "^3.0.0"
}
},
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
"object-inspect": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
@@ -594,6 +1134,39 @@
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
"packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
@@ -613,6 +1186,18 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@@ -696,6 +1281,12 @@
"split": "^1.0.0"
}
},
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
"pidtree": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
@@ -729,6 +1320,25 @@
"xtend": "^4.0.0"
}
},
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "promise.allsettled": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
+ "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
+ "dev": true,
+ "requires": {
+ "array.prototype.map": "^1.0.1",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "iterate-value": "^1.0.0"
+ }
+ },
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@@ -748,6 +1358,15 @@
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -774,6 +1393,42 @@
"path-type": "^3.0.0"
}
},
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
+ "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.0.7"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
@@ -817,6 +1472,15 @@
"statuses": "~1.4.0"
}
},
+ "serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
@@ -828,6 +1492,12 @@
"send": "0.16.2"
}
},
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
@@ -887,11 +1557,27 @@
"through": "2"
}
},
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
"string.prototype.padend": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz",
@@ -919,11 +1605,89 @@
"es-abstract": "^1.17.5"
}
},
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
},
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ },
+ "superagent": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz",
+ "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "^1.2.0",
+ "cookiejar": "^2.1.0",
+ "debug": "^3.1.0",
+ "extend": "^3.0.0",
+ "form-data": "^2.3.1",
+ "formidable": "^1.2.0",
+ "methods": "^1.1.1",
+ "mime": "^1.4.1",
+ "qs": "^6.5.1",
+ "readable-stream": "^2.3.5"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "supertest": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz",
+ "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==",
+ "dev": true,
+ "requires": {
+ "methods": "^1.1.2",
+ "superagent": "^3.8.3"
+ }
+ },
+ "supertest-session": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/supertest-session/-/supertest-session-4.0.0.tgz",
+ "integrity": "sha512-9d7KAL+K9hnnicov7USv/Nu1tSl40qSrOsB8zZHOEtfEzHaAoID6tbl1NeCVUg7SJreJMlNn+KJ88V7FW8gD6Q==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1"
+ }
+ },
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -937,6 +1701,15 @@
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -959,6 +1732,12 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -986,10 +1765,276 @@
"isexe": "^2.0.0"
}
},
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "workerpool": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz",
+ "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yargs-unparser": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
+ "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.3.1",
+ "decamelize": "^1.2.0",
+ "flat": "^4.1.0",
+ "is-plain-obj": "^1.1.0",
+ "yargs": "^14.2.3"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "yargs": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
+ "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^15.0.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
+ "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
}
}
}
diff --git a/backend/package.json b/backend/package.json
index 32ea72f..34aa732 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,19 +1,28 @@
{
- "name": "agensbrowser",
- "version": "0.0.1",
- "private": true,
- "scripts": {
- "start": "node ./server/bin/www"
- },
- "dependencies": {
- "agensgraph": "git+https://github.com/bitnine-oss/agensgraph-nodejs.git",
- "cookie-parser": "~1.4.4",
- "debug": "~2.6.9",
- "express": "~4.16.1",
- "express-session": "^1.17.1",
- "morgan": "~1.9.1",
- "npm-run-all": "^4.1.5",
- "pegjs": "^0.10.0",
- "pg": "^8.3.0"
- }
+ "name": "agensbrowser",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "test": "mocha",
+ "test:connector": "mocha ./test/connector.api.test.js",
+ "test:cypher": "mocha ./test/cypher.api.test.js",
+ "test:query": "mocha ./test/cypher.service.test.js",
+ "start": "node ./server/bin/www"
+ },
+ "dependencies": {
+ "agensgraph": "git+https://github.com/bitnine-oss/agensgraph-nodejs.git",
+ "cookie-parser": "~1.4.4",
+ "debug": "~2.6.9",
+ "express": "~4.16.1",
+ "express-session": "^1.17.1",
+ "morgan": "~1.9.1",
+ "npm-run-all": "^4.1.5",
+ "pegjs": "^0.10.0",
+ "pg": "^8.3.0"
+ },
+ "devDependencies": {
+ "mocha": "^8.1.0",
+ "supertest": "^4.0.2",
+ "supertest-session": "^4.0.0"
+ }
}
diff --git a/backend/server/api/cypher/cypherController.js b/backend/server/api/cypher/cypherController.js
deleted file mode 100644
index bb64a82..0000000
--- a/backend/server/api/cypher/cypherController.js
+++ /dev/null
@@ -1,63 +0,0 @@
-let express = require('express');
-let ag = require('agensgraph');
-
-let router = express.Router();
-
-router.post('/', function (req, res, next) {
- if (!req.session.client) {
- res.status(500);
- } else {
- let cmd = req.body.cmd;
-
- const client = new ag.Client(req.session.client);
- client.connect();
- client.query(`set graph_path=${req.session.client.graph}`);
- client.query(cmd, (err, resultSet) => {
- let rows = resultSet.rows, columns = resultSet.fields.map((d) => d.name);
-
- let convertedRows = rows.map((row) => {
- let convetedObject = {};
- for (let k in row) {
- if (row[k].hasOwnProperty('start')) {
- convetedObject[k] = convertEdge(row[k]);
- } else if (row[k].hasOwnProperty('id')) {
- convetedObject[k] = convertVertex(row[k]);
- } else {
- convetedObject[k] = row[k];
- }
- }
- return convetedObject;
- });
-
- result = convertedRows;
- res.status(200).json({
- message: 'OK',
- data: {
- rows: convertedRows,
- columns: columns,
- },
- });
- client.end();
- });
- }
-});
-
-function convertEdge({ label, id, start, end, props }) {
- return {
- label: label,
- id: `${id.oid}.${id.id}`,
- start: `${start.oid}.${start.id}`,
- end: `${end.oid}.${end.id}`,
- properties: props,
- };
-}
-
-function convertVertex({ label, id, props }) {
- return {
- label: label,
- id: `${id.oid}.${id.id}`,
- properties: props,
- };
-}
-
-module.exports = router;
diff --git a/backend/server/api/database/databaseController.js b/backend/server/api/database/databaseController.js
deleted file mode 100644
index ed076d9..0000000
--- a/backend/server/api/database/databaseController.js
+++ /dev/null
@@ -1,60 +0,0 @@
-let express = require('express');
-let router = express.Router();
-
-router.get('/', (req, res, next) => {
- let message, data;
- if(!req.session.client) {
- message = 'Not Connected Database';
- data = {};
- } else {
- message = 'Connected Database';
- data = req.session.client;
- }
-
- res.status(200).send({
- message: message,
- data: data
- }).end();
-})
-
-router.post('/connect', (req, res, next) => {
- let connInfo = req.body;
- if(!req.session.client) {
- try {
- req.session.client = connInfo;
- res.status(200).send({
- message: 'Successful Connected',
- data: connInfo
- }).end();
- } catch (e) {
- console.log(e);
- res.status(500).send({
- message: 'Failed Connect',
- data: {
- 'Error': e
- }
- }).end();
- }
- } else {
- res.status(200).json({
- message: 'Already Connected',
- data: {
- host: req.session.client.host,
- port: req.session.client.port,
- database: req.session.client.database
- }
- }).end();
- }
-});
-
-router.get('/disconnect', (req, res, next) => {
- if(!!req.session.client) {
- req.session.client = null;
- res.status(200).send('disconnect database').end();
- } else {
-
- }
- res.send('disconnect database');
-});
-
-module.exports = router;
diff --git a/backend/server/app.js b/backend/server/app.js
index 0686bff..00a9822 100644
--- a/backend/server/app.js
+++ b/backend/server/app.js
@@ -1,20 +1,27 @@
-let express = require('express');
-let session = require('express-session')
-let path = require('path');
-let cookieParser = require('cookie-parser');
-let logger = require('morgan');
-
-let cypherRouter = require('./api/cypher/cypherController');
-let databaseRouter = require('./api/database/databaseController');
+const express = require('express');
+const session = require('express-session');
+const path = require('path');
+const cookieParser = require('cookie-parser');
+const logger = require('morgan');
+const supertest = require('supertest');
+const cypherRouter = require('./application/cypher/cypherController');
+const databaseRouter = require('./application/connector/connectorController');
let app = express();
-app.use(session({
- secret: 'bitnine123!',
- resave: true,
- saveUninitialized: true,
- proxy: true
-}));
+app.use(express.static(path.join(__dirname, '../../frontend/build')));
+app.get('/', function (req, res) {
+ res.sendFile(path.join(__dirname, '../../frontend/build', 'index.html'));
+});
+
+app.use(
+ session({
+ secret: 'bitnine123!',
+ resave: true,
+ saveUninitialized: true,
+ proxy: true,
+ })
+);
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
@@ -23,4 +30,8 @@
app.use('/api/v1/cypher', cypherRouter);
app.use('/api/v1/db', databaseRouter);
+process.on('uncaughtException', function (exception) {
+ console.log(exception);
+});
+
module.exports = app;
diff --git a/backend/server/application/connector/connectorController.js b/backend/server/application/connector/connectorController.js
new file mode 100644
index 0000000..81fefa4
--- /dev/null
+++ b/backend/server/application/connector/connectorController.js
@@ -0,0 +1,42 @@
+const express = require('express');
+const ConnectorService = require('./connectorService')
+const AgensDatabaseHelper = require('../db/agensDatabaseHelper');
+const router = express.Router();
+
+router.get('/', async (req, res, next) => {
+ let agensDatabaseHelper = new AgensDatabaseHelper(req.session.client);
+
+ let connectorService = new ConnectorService(req.session, agensDatabaseHelper);
+ let {status, data} = await connectorService.getConnectionStatus();
+
+ res.status(status).json(data).end();
+});
+
+router.post('/connect', async (req, res, next) => {
+ let agensDatabaseHelper = new AgensDatabaseHelper(req.body);
+
+ let connectorService = new ConnectorService(req.session, agensDatabaseHelper);
+ let {status, data} = await connectorService.connectDatabase();
+
+ res.status(status).json(data).end();
+});
+
+router.get('/disconnect', (req, res, next) => {
+ let agensDatabaseHelper = new AgensDatabaseHelper(req.session.client);
+
+ let connectorService = new ConnectorService(req.session, agensDatabaseHelper);
+ let {status, data} = connectorService.disconnectDatabase();
+
+ res.status(status).json(data).end();
+});
+
+router.get('/meta', async (req, res, next) => {
+ let agensDatabaseHelper = new AgensDatabaseHelper(req.session.client);
+
+ let connectorService = new ConnectorService(req.session, agensDatabaseHelper);
+ let metadata = await connectorService.getMetaData();
+
+ res.status(200).json(metadata).end();
+});
+
+module.exports = router;
diff --git a/backend/server/application/connector/connectorService.js b/backend/server/application/connector/connectorService.js
new file mode 100644
index 0000000..cac3ca6
--- /dev/null
+++ b/backend/server/application/connector/connectorService.js
@@ -0,0 +1,122 @@
+class ConnectorService {
+ constructor(session, agensDatabaseHelper) {
+ this._session = session;
+ this._agensDatabaseHelper = agensDatabaseHelper;
+ }
+
+ async getMetaData() {
+ let metadata = new Object();
+ metadata.nodes = await this.getNodes();
+ metadata.edges = await this.getEdges();
+ metadata.propertyKeys = await this.getPropertyKeys();
+ return metadata;
+ }
+
+ async getNodes(label) {
+ let agensDatabaseHelper = this._agensDatabaseHelper;
+ let query = [];
+ query.push("MATCH(v) RETURN DISTINCT '*' AS label, count(v) AS cnt");
+ query.push('UNION ALL');
+ query.push('MATCH(v) RETURN DISTINCT label(v) AS label, count(v) AS cnt');
+ query.push('ORDER BY label');
+
+ let queryResult = await agensDatabaseHelper.execute(query.join('\n'));
+ return queryResult.rows;
+ }
+
+ async getEdges(label) {
+ let agensDatabaseHelper = this._agensDatabaseHelper;
+ let query = [];
+ query.push("MATCH(v) - [e] - (v2) RETURN DISTINCT '*' AS label, count(e) AS cnt");
+ query.push('UNION ALL');
+ query.push('MATCH(v) - [e] - (v2) RETURN DISTINCT label(e) AS label, count(e) AS cnt');
+ query.push('ORDER BY label');
+
+ let queryResult = await agensDatabaseHelper.execute(query.join('\n'));
+ return queryResult.rows;
+ }
+
+ async getPropertyKeys() {
+ let agensDatabaseHelper = this._agensDatabaseHelper;
+ let query = [];
+ query.push('MATCH(v)');
+ query.push("RETURN DISTINCT jsonb_object_keys(v) AS key, 'v' AS key_type");
+ query.push('UNION ALL');
+ query.push('MATCH(v1) - [e] - (v2)');
+ query.push("RETURN DISTINCT jsonb_object_keys(e) AS key, 'e' AS key_type");
+
+ let queryResult = await agensDatabaseHelper.execute(query.join('\n'));
+ return queryResult.rows;
+ }
+
+ async connectDatabase() {
+ let agensDatabaseHelper = this._agensDatabaseHelper;
+ let status, data;
+
+ if (await agensDatabaseHelper.isHealth()) {
+ this._session.client = agensDatabaseHelper.toConnectionInfo();
+ data = agensDatabaseHelper.toConnectionInfo();
+ status = 200;
+ } else {
+ data = null;
+ status = 500;
+ }
+
+ return {
+ status: status,
+ data: data,
+ };
+ }
+
+ disconnectDatabase() {
+ let status = 200,
+ data = null;
+ try {
+ this._session.client = null;
+ } catch (err) {
+ console.log("Already Disconnected");
+ }
+ return {
+ status: status,
+ data: data,
+ };
+ }
+
+ async getConnectionStatus() {
+ let agensDatabaseHelper = this._agensDatabaseHelper;
+ let status, data;
+
+ if (await agensDatabaseHelper.isHealth()) {
+ data = agensDatabaseHelper.toConnectionInfo();
+ status = 200;
+ } else {
+ data = null;
+ status = 500;
+ }
+
+ return {
+ status: status,
+ data: data,
+ };
+ }
+
+ convertEdge({ label, id, start, end, props }) {
+ return {
+ label: label,
+ id: `${id.oid}.${id.id}`,
+ start: `${start.oid}.${start.id}`,
+ end: `${end.oid}.${end.id}`,
+ properties: props,
+ };
+ }
+
+ convertVertex({ label, id, props }) {
+ return {
+ label: label,
+ id: `${id.oid}.${id.id}`,
+ properties: props,
+ };
+ }
+}
+
+module.exports = ConnectorService;
diff --git a/backend/server/application/cypher/cypherController.js b/backend/server/application/cypher/cypherController.js
new file mode 100644
index 0000000..084cef0
--- /dev/null
+++ b/backend/server/application/cypher/cypherController.js
@@ -0,0 +1,20 @@
+const express = require('express');
+const ag = require('agensgraph');
+const CypherService = require('./cypherService')
+const AgensDatabaseHelper = require('../db/agensDatabaseHelper')
+
+let router = express.Router();
+
+router.post('/', async (req, res, next) => {
+
+ let agensDatabaseHelper = new AgensDatabaseHelper(req.session.client);
+
+ let cypherService = new CypherService(agensDatabaseHelper);
+ let {status, data} = await cypherService.executeCypher(req.body.cmd);
+
+ res.status(status).json(data).end();
+});
+
+
+
+module.exports = router;
diff --git a/backend/server/application/cypher/cypherService.js b/backend/server/application/cypher/cypherService.js
new file mode 100644
index 0000000..a5de70a
--- /dev/null
+++ b/backend/server/application/cypher/cypherService.js
@@ -0,0 +1,80 @@
+class CypherService {
+ constructor(agensDatabaseHelper) {
+ this._agensDatabaseHelper = agensDatabaseHelper;
+ }
+
+ async executeCypher(query) {
+ let agensDatabaseHelper = this._agensDatabaseHelper;
+ let result = {
+ status: 200,
+ data: null,
+ };
+
+ if (!query) {
+ result.status = 400;
+ result.data = { cmd: query };
+ } else {
+ if (await agensDatabaseHelper.isHealth()) {
+ result.status = 200;
+ result.data = await this.getExecuteResult(query);
+ } else {
+ result.data = agensDatabaseHelper.toConnectionInfo();
+ result.status = 500;
+ }
+ }
+
+ return result;
+ }
+
+ async getExecuteResult(query) {
+ let agensDatabaseHelper = this._agensDatabaseHelper;
+ try {
+ let queryResult = await agensDatabaseHelper.execute(query);
+ let result = {
+ rows: null,
+ columns: queryResult.fields.map((field) => field.name),
+ rowCount: queryResult.rowCount,
+ command: queryResult.command
+ }
+
+ result.rows = queryResult.rows.map((row) => {
+ let convetedObject = {};
+ for (let k in row) {
+ if (row[k].hasOwnProperty('start')) {
+ convetedObject[k] = this.convertEdge(row[k]);
+ } else if (row[k].hasOwnProperty('id')) {
+ convetedObject[k] = this.convertVertex(row[k]);
+ } else {
+ convetedObject[k] = row[k];
+ }
+ }
+ return convetedObject;
+ });
+
+ return result;
+ } catch (err) {
+ console.log(err);
+ throw err;
+ }
+ }
+
+ convertEdge({ label, id, start, end, props }) {
+ return {
+ label: label,
+ id: `${id.oid}.${id.id}`,
+ start: `${start.oid}.${start.id}`,
+ end: `${end.oid}.${end.id}`,
+ properties: props,
+ };
+ }
+
+ convertVertex({ label, id, props }) {
+ return {
+ label: label,
+ id: `${id.oid}.${id.id}`,
+ properties: props,
+ };
+ }
+}
+
+module.exports = CypherService;
diff --git a/backend/server/application/db/agensDatabaseHelper.js b/backend/server/application/db/agensDatabaseHelper.js
new file mode 100644
index 0000000..a35ef7e
--- /dev/null
+++ b/backend/server/application/db/agensDatabaseHelper.js
@@ -0,0 +1,85 @@
+const { Pool } = require('agensgraph');
+
+class AgensDatabaseHelper {
+ constructor({ host, port, database, graph, user, password } = {}) {
+ this._host = host;
+ this._port = port;
+ this._database = database;
+ this._graph = graph;
+ this._user = user;
+ this._password = password;
+ }
+
+ async isHealth() {
+ let result = false;
+ if(this.toPoolConnectionInfo() == null) {
+ return result;
+ }
+
+ let client = await this.getConnection();
+ try {
+ await client.query('SELECT 1');
+ result = true;
+ } catch (err) {
+ console.error('Error Occurred!!!: ', err);
+ } finally {
+ client.release();
+ }
+
+ return result;
+ }
+
+ async execute(query) {
+ let client = await this.getConnection();
+ let result = null;
+ try {
+ await client.query(`set graph_path=${this._graph}`);
+ result = await client.query(query);
+ } catch (err) {
+ console.error('Error Occurred!!!: ', err);
+ next(err);
+ } finally {
+ client.release();
+ }
+ return result;
+ }
+
+ getConnection() {
+ if (!this._pool) {
+ this._pool = new Pool(this.toPoolConnectionInfo());
+ }
+ return this._pool.connect();
+ }
+
+ toPoolConnectionInfo() {
+ if(!this._host || !this._port || !this._database) {
+ return null;
+ }
+ return {
+ host: this._host,
+ port: this._port,
+ database: this._database,
+ user: this._user,
+ password: this._password,
+ max: 10,
+ idleTimeoutMillis: 30000,
+ connectionTimeoutMillis: 2000,
+ };
+ }
+
+ toConnectionInfo() {
+ if(!this._host || !this._port || !this._database) {
+ return null;
+ }
+ return {
+ host: this._host,
+ port: this._port,
+ database: this._database,
+ user: this._user,
+ password: this._password,
+ graph: this._graph,
+ };
+ }
+}
+
+module.exports = AgensDatabaseHelper;
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 9907409..526325b 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -2104,6 +2104,11 @@
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
+ "@zeit/schemas": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz",
+ "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg=="
+ },
"abab": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
@@ -2231,6 +2236,43 @@
"resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM="
},
+ "ansi-align": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
+ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+ "requires": {
+ "string-width": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
"ansi-colors": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
@@ -2283,6 +2325,16 @@
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
+ "arch": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz",
+ "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ=="
+ },
+ "arg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz",
+ "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w=="
+ },
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -2477,6 +2529,14 @@
"postcss-value-parser": "^4.1.0"
}
},
+ "avsdf-base": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/avsdf-base/-/avsdf-base-1.0.0.tgz",
+ "integrity": "sha512-APhZNUFJwIwrLsSfE95QjobEntdUhFQgfNtC/BrYmjUpwHh5Y2fbRv8lxAlMr1hdf/CuQYsqJxK3dRzcCL77qw==",
+ "requires": {
+ "layout-base": "^1.0.0"
+ }
+ },
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -3093,6 +3153,54 @@
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
"integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
},
+ "boxen": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
+ "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
+ "requires": {
+ "ansi-align": "^2.0.0",
+ "camelcase": "^4.0.0",
+ "chalk": "^2.0.1",
+ "cli-boxes": "^1.0.0",
+ "string-width": "^2.0.0",
+ "term-size": "^1.2.0",
+ "widest-line": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3555,6 +3663,11 @@
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
},
+ "cli-boxes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
+ },
"cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@@ -3568,6 +3681,60 @@
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw=="
},
+ "clipboardy": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz",
+ "integrity": "sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==",
+ "requires": {
+ "arch": "^2.1.0",
+ "execa": "^0.8.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz",
+ "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=",
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+ }
+ }
+ },
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
@@ -4257,6 +4424,23 @@
"lodash.debounce": "^4.0.8"
}
},
+ "cytoscape-avsdf": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-avsdf/-/cytoscape-avsdf-1.0.0.tgz",
+ "integrity": "sha512-Wzd2wmJgr4dK5avHy4nKHp3D8TBZ8H/+5Hq5o24bRdVTvXlhSDzQhAMtzOuhRPYwHcWB+cBhEAXboAK8n7zPTQ==",
+ "requires": {
+ "avsdf-base": "^1.0.0"
+ }
+ },
+ "cytoscape-cise": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-cise/-/cytoscape-cise-1.0.0.tgz",
+ "integrity": "sha512-Y1NPaUo4fN992XJTEIDd4oPVkv8BsDSrFBHSB38caDu8PcmHUyl8/Q8K5wvqdTeti1mLR9IX4/o2RyuObh+P7Q==",
+ "requires": {
+ "avsdf-base": "^1.0.0",
+ "cose-base": "^1.0.0"
+ }
+ },
"cytoscape-cose-bilkent": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
@@ -4265,6 +4449,24 @@
"cose-base": "^1.0.0"
}
},
+ "cytoscape-cxtmenu": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/cytoscape-cxtmenu/-/cytoscape-cxtmenu-3.1.2.tgz",
+ "integrity": "sha512-ikPWyNRZ6IfIykXU9J6tXkwu2Gk3nYK5yNmNJI/wtLFCuELdcCqpEXobgGLDT7yEGO+jI1upxmb4Lex/DPZlgA=="
+ },
+ "cytoscape-d3-force": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/cytoscape-d3-force/-/cytoscape-d3-force-1.1.4.tgz",
+ "integrity": "sha512-8NjI/yEoB3YqVsdf7ud7Oh8Kyi+C9Lhh1fICmtemIo6EC1ZUtm8KcPNLkQySYO8nRS2mQKj5eVdCr7W0L8ONoQ==",
+ "requires": {
+ "d3-force": "^2.0.1"
+ }
+ },
+ "cytoscape-euler": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cytoscape-euler/-/cytoscape-euler-1.2.2.tgz",
+ "integrity": "sha512-A24ZGrFpqCOutTIlGoXA5kmjFj68iy7HvqXuhcZUL1a7Z8bL59Bl2bB7hkSvFcCZBVCNcArxKr+YlB8bJo9Ftw=="
+ },
"d": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
@@ -4274,6 +4476,31 @@
"type": "^1.0.1"
}
},
+ "d3-dispatch": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
+ "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA=="
+ },
+ "d3-force": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-2.0.1.tgz",
+ "integrity": "sha512-zh73/N6+MElRojiUG7vmn+3vltaKon7iD5vB/7r9nUaBeftXMzRo5IWEG63DLBCto4/8vr9i3m9lwr1OTJNiCg==",
+ "requires": {
+ "d3-dispatch": "1",
+ "d3-quadtree": "1",
+ "d3-timer": "1"
+ }
+ },
+ "d3-quadtree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
+ "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA=="
+ },
+ "d3-timer": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz",
+ "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw=="
+ },
"damerau-levenshtein": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
@@ -4340,6 +4567,11 @@
"regexp.prototype.flags": "^1.2.0"
}
},
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+ },
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -5741,6 +5973,21 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
+ "fast-url-parser": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
+ "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=",
+ "requires": {
+ "punycode": "^1.3.2"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ }
+ }
+ },
"faye-websocket": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
@@ -10292,6 +10539,11 @@
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
},
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+ },
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
@@ -10434,6 +10686,24 @@
}
}
},
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+ }
+ }
+ },
"react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
@@ -11276,6 +11546,23 @@
"unicode-match-property-value-ecmascript": "^1.2.0"
}
},
+ "registry-auth-token": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
+ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
+ "requires": {
+ "rc": "^1.1.6",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "registry-url": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+ "requires": {
+ "rc": "^1.0.1"
+ }
+ },
"regjsgen": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz",
@@ -11790,6 +12077,122 @@
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ=="
},
+ "serve": {
+ "version": "11.3.2",
+ "resolved": "https://registry.npmjs.org/serve/-/serve-11.3.2.tgz",
+ "integrity": "sha512-yKWQfI3xbj/f7X1lTBg91fXBP0FqjJ4TEi+ilES5yzH0iKJpN5LjNb1YzIfQg9Rqn4ECUS2SOf2+Kmepogoa5w==",
+ "requires": {
+ "@zeit/schemas": "2.6.0",
+ "ajv": "6.5.3",
+ "arg": "2.0.0",
+ "boxen": "1.3.0",
+ "chalk": "2.4.1",
+ "clipboardy": "1.2.3",
+ "compression": "1.7.3",
+ "serve-handler": "6.1.3",
+ "update-check": "1.5.2"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
+ "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "compression": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
+ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.14",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.1",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "serve-handler": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz",
+ "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==",
+ "requires": {
+ "bytes": "3.0.0",
+ "content-disposition": "0.5.2",
+ "fast-url-parser": "1.1.3",
+ "mime-types": "2.1.18",
+ "minimatch": "3.0.4",
+ "path-is-inside": "1.0.2",
+ "path-to-regexp": "2.2.1",
+ "range-parser": "1.2.0"
+ },
+ "dependencies": {
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
+ },
+ "mime-db": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
+ },
+ "mime-types": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+ "requires": {
+ "mime-db": "~1.33.0"
+ }
+ },
+ "path-to-regexp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
+ "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ=="
+ },
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+ }
+ }
+ },
"serve-index": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
@@ -12690,6 +13093,59 @@
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA=="
},
+ "term-size": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
+ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+ "requires": {
+ "execa": "^0.7.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+ }
+ }
+ },
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
@@ -13142,6 +13598,15 @@
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="
},
+ "update-check": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz",
+ "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==",
+ "requires": {
+ "registry-auth-token": "3.3.2",
+ "registry-url": "3.1.0"
+ }
+ },
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@@ -14006,6 +14471,43 @@
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
+ "widest-line": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
+ "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
+ "requires": {
+ "string-width": "^2.1.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 966b51f..d76b47d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,7 +11,12 @@
"axios": "^0.19.2",
"bootstrap": "^4.5.0",
"cytoscape": "^3.2.19",
+ "cytoscape-avsdf": "^1.0.0",
+ "cytoscape-cise": "^1.0.0",
"cytoscape-cose-bilkent": "^4.1.0",
+ "cytoscape-cxtmenu": "^3.1.2",
+ "cytoscape-d3-force": "^1.1.4",
+ "cytoscape-euler": "^1.2.2",
"react": "^16.13.1",
"react-bootstrap": "^1.2.2",
"react-cytoscapejs": "^1.2.1",
@@ -20,7 +25,8 @@
"react-scripts": "3.4.1",
"react-uuid": "^1.0.2",
"redux": "^4.0.5",
- "redux-thunk": "^2.3.0"
+ "redux-thunk": "^2.3.0",
+ "serve": "^11.3.2"
},
"scripts": {
"start": "react-scripts start",
@@ -45,7 +51,6 @@
]
},
"devDependencies": {
- "webpack": "^4.42.0",
"webpack-dev-server": "^3.10.3"
}
}
diff --git a/frontend/public/css/style.css b/frontend/public/css/style.css
index 81cf4bb..a7e9e47 100644
--- a/frontend/public/css/style.css
+++ b/frontend/public/css/style.css
@@ -86,9 +86,8 @@
width: 70px;
background-color: var(--navbar-color);
}
-
#sidebar {
- width: 250px;
+ width: 320px;
position: fixed;
top: 0;
left: 70px;
@@ -97,13 +96,28 @@
background-color: var(--sidebar-color);
color: #fff;
transition: all 0.3s;
- margin-left: -250px;
+ margin-left: -320px;
}
-
#sidebar.active {
margin-left: 0px;
}
+/*
+#sidebar {
+ width: 0;
+ position: fixed;
+ top: 0;
+ left: 70px;
+ height: 100vh;
+ background-color: var(--sidebar-color);
+ color: #fff;
+ transition: all 0.3s;
+}
+#sidebar.active {
+ width: 320px;
+}
+*/
+
a[data-toggle="collapse"] {
position: relative;
}
@@ -128,19 +142,25 @@
background-color: var(--bg-color);
color: var(--text-color);
}
+/*
+#sidebar.active~#content{
+ width: calc(100% - 390px);
+}
+*/
#content.active {
- width: calc(100% - 320px);
+ width: calc(100% - 390px);
}
+
/* ---------------------------------------------------
MEDIAQUERIES
----------------------------------------------------- */
@media (max-width: 768px) {
#sidebar {
- margin-left: -250px;
+ margin-left: -320px;
}
#sidebar.active {
margin-left: 0;
@@ -149,7 +169,7 @@
width: 100%;
}
#content.active {
- width: calc(100% - 250px);
+ width: calc(100% - 320px);
}
#sidebarCollapse span {
display: none;
@@ -160,6 +180,18 @@
color: gray !important;
}
+.card.fullscreen {
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 100vw;
+ height: 100vh;
+ z-index: 1040;
+}
+
+.card.fullscreen .chart-area {
+ height: calc(100% - 94px - 35px)
+}
.card-header {
border-bottom: 1px solid rgba(0,0,0,.125);
}
@@ -212,11 +244,116 @@
margin-bottom: 0px;
}
-.chart-area {
- height:100%;
+.chart-frame-area {
width:100%;
+ height:100%;
+
}
+.chart-area {
+ height: 317px;
+ width: 100%;
+ display: 'block';
+ overflow:hidden;
+}
+
+.chart-footer-area {
+ vertical-align: middle;
+ line-height: 37px;
+ min-height: 37px;
+ width: 100%;
+ background-color: rgba(0,0,0,.03);
+ border-top: 1px solid rgba(0,0,0,.125);
+}
+
.frame-head-button:hover {
color: #212121 !important;
+}
+
+.graph-tabpanel {
+ padding-left: 0px !important;
+ padding-right: 0px !important;
+}
+
+.nodeLegend, .edgeLegend {
+ height: 47px;
+ width: 100%;
+ padding-left: 15px;
+ padding-right: 15px;
+ background-color: rgba(0,0,0,.03);
+ border-bottom: 1px solid rgba(0,0,0,.125);
+}
+
+.nodeLabel, .edgeLabel {
+ cursor: pointer;
+}
+
+.colorSelector {
+ width: 15px;
+ height: 15px;
+ padding: 6px 0px;
+ border-radius: 15px;
+ font-size: 8px;
+ text-align: center;
+ margin-left: 5px;
+ opacity: 0.2;
+}
+
+.captionSelector {
+ padding: .1rem .2rem;
+ font-size: .575rem;
+ line-height: 1.5;
+ border-radius: .2rem;
+ margin-left: 5px;
+ border-color: darkgrey;
+ opacity: 0.5;
+}
+
+.sizeSelector.node {
+ background-color: darkgrey;
+ padding: 6px 0px;
+ border-radius: 15px;
+ font-size: 8px;
+ text-align: center;
+ margin-left: 5px;
+ opacity: 0.2;
+}
+
+.sizeSelector.edge {
+ background-color: darkgrey;
+ padding: 6px 0px;
+ font-size: 8px;
+ text-align: center;
+ margin-left: 5px;
+ opacity: 0.2;
+}
+
+.sizeSelector:hover {
+ opacity: 1;
+}
+
+.sizeSelector.selectedSize {
+ opacity: 1;
+}
+
+.colorSelector:hover {
+ opacity: 1;
+}
+
+.colorSelector.selectedColor {
+ opacity: 1;
+}
+
+.captionSelector.selectedCaption {
+ opacity: 1;
+}
+
+
+
+
+/* ---------------------------------------------------
+ Cytoscapejs-cxtmenu
+----------------------------------------------------- */
+.cxtmenu-content {
+ font-size: 10px;
}
\ No newline at end of file
diff --git a/frontend/public/index.html b/frontend/public/index.html
index 2c9722f..288146d 100644
--- a/frontend/public/index.html
+++ b/frontend/public/index.html
@@ -39,7 +39,9 @@
href="https://cdnjs.cloudflare.com/ajax/libs/malihu-custom-scrollbar-plugin/3.1.5/jquery.mCustomScrollbar.min.css">
<!-- Fontawesome CSS -->
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
+ <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> -->
+ <script src="https://kit.fontawesome.com/8919a0422e.js" crossorigin="anonymous"></script>
+
<!-- Google Poppins Webfont -->
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Poppins" />
diff --git a/frontend/src/app/reducers.js b/frontend/src/app/reducers.js
index 9df8112..79b7f0f 100644
--- a/frontend/src/app/reducers.js
+++ b/frontend/src/app/reducers.js
@@ -1,5 +1,6 @@
import { combineReducers } from 'redux'
import DatabaseReducer from '../features/database/DatabaseSlice'
+import MetadataReducer from '../features/database/MetadataSlice'
import FrameReducer from '../features/frame/FrameSlice'
import MenuReducer from '../features/menu/MenuSlice'
import SettingReducer from '../features/setting/SettingSlice'
@@ -11,6 +12,7 @@
navigator : MenuReducer,
setting : SettingReducer,
database : DatabaseReducer,
+ metadata : MetadataReducer,
frames : FrameReducer,
cypher: CypherReducer,
alerts: AlertReducer
diff --git a/frontend/src/components/alert/presentations/Alert.jsx b/frontend/src/components/alert/presentations/Alert.jsx
index a52cb72..b4f21e8 100644
--- a/frontend/src/components/alert/presentations/Alert.jsx
+++ b/frontend/src/components/alert/presentations/Alert.jsx
@@ -1,10 +1,15 @@
-import React, {useState} from 'react';
+import React, {useState, useEffect} from 'react';
import {Alert} from 'react-bootstrap'
const SingleAlert = ({key, alertKey, alertType}) => {
const [show, setShow] = useState(true)
+ useEffect(() => {
+ const timer = setTimeout(() => {setShow(false)} , 5000)
+ return () => clearTimeout(timer);
+ }, [])
+
if (alertType === 'NoticeServerDisconnected') {
return (
<Alert show={show} variant="warning" onClose={() => setShow(false)} dismissible>
diff --git a/frontend/src/components/contents/containers/Contents.js b/frontend/src/components/contents/containers/Contents.js
index c4b2358..8d64aab 100644
--- a/frontend/src/components/contents/containers/Contents.js
+++ b/frontend/src/components/contents/containers/Contents.js
@@ -1,6 +1,6 @@
import {connect} from 'react-redux'
+import {getConnectionStatus} from '../../../features/database/DatabaseSlice'
import {addFrame} from '../../../features/frame/FrameSlice'
-import {addAlert} from '../../../features/alert/AlertSlice'
import Contents from '../presentations/Contents'
/*
import React from 'react'
@@ -12,10 +12,8 @@
const mapStateToProps = (state) => {
return {
- activeMenuName: state.navigator.activeMenu,
- frameList: state.frames,
- alertList: state.alerts,
- database: state.database
+ database: state.database,
+ isActive: state.navigator.isActive
}
}
@@ -52,7 +50,7 @@
}
*/
-const mapDispatchToProps = { addFrame, addAlert }
+const mapDispatchToProps = { getConnectionStatus, addFrame }
export default connect(mapStateToProps, mapDispatchToProps)(Contents);
diff --git a/frontend/src/components/contents/containers/Editor.js b/frontend/src/components/contents/containers/Editor.js
new file mode 100644
index 0000000..5ead064
--- /dev/null
+++ b/frontend/src/components/contents/containers/Editor.js
@@ -0,0 +1,17 @@
+import {connect} from 'react-redux'
+import {addFrame} from '../../../features/frame/FrameSlice'
+import {addAlert} from '../../../features/alert/AlertSlice'
+import {getConnectionStatus} from '../../../features/database/DatabaseSlice'
+import {executeCypherQuery} from '../../../features/cypher/CypherSlice'
+import Editor from '../presentations/Editor'
+const mapStateToProps = (state) => {
+ return {
+ alertList: state.alerts,
+ database: state.database
+ }
+}
+
+
+const mapDispatchToProps = { addFrame, addAlert, getConnectionStatus, executeCypherQuery }
+
+export default connect(mapStateToProps, mapDispatchToProps)(Editor);
diff --git a/frontend/src/components/contents/containers/Frames.js b/frontend/src/components/contents/containers/Frames.js
new file mode 100644
index 0000000..9a78882
--- /dev/null
+++ b/frontend/src/components/contents/containers/Frames.js
@@ -0,0 +1,13 @@
+import {connect} from 'react-redux'
+import Frames from '../presentations/Frames'
+const mapStateToProps = (state) => {
+ return {
+ frameList: state.frames,
+ queryResult : state.cypher.queryResult,
+ database: state.database
+ }
+}
+
+const mapDispatchToProps = { }
+
+export default connect(mapStateToProps, mapDispatchToProps)(Frames);
diff --git a/frontend/src/components/contents/presentations/Contents.jsx b/frontend/src/components/contents/presentations/Contents.jsx
index 6a0065b..430c57d 100644
--- a/frontend/src/components/contents/presentations/Contents.jsx
+++ b/frontend/src/components/contents/presentations/Contents.jsx
@@ -1,12 +1,22 @@
import React from 'react';
-import Editor from '../presentations/Editor'
-import Frames from '../presentations/Frames'
+import {useDispatch} from 'react-redux'
+import EditorContainer from '../containers/Editor'
+import FramesContainer from '../containers/Frames'
-const Contents = ({ activeMenuName, frameList, alertList, addFrame, addAlert, database }) => {
+const Contents = ({ database, isActive, getConnectionStatus, addFrame }) => {
+ const dispatch = useDispatch();
+
+ if (database.status === 'init') {
+ dispatch(() => getConnectionStatus())
+ }
+ else if (database.status === 'disconnected') {
+ dispatch(() => addFrame(':server connect'))
+ }
+
return (
- <div id="content" className={activeMenuName !== "" ? " active " : ""}>
- <Editor onClick={addFrame} addAlert={addAlert} alertList={alertList} serverInfo={database} />
- <Frames frameList={frameList} serverInfo={database} />
+ <div id="content" className={isActive ? "active" : ""}>
+ <EditorContainer />
+ <FramesContainer />
</div>
);
}
diff --git a/frontend/src/components/contents/presentations/Editor.jsx b/frontend/src/components/contents/presentations/Editor.jsx
index 58a02c6..9e1fdba 100644
--- a/frontend/src/components/contents/presentations/Editor.jsx
+++ b/frontend/src/components/contents/presentations/Editor.jsx
@@ -1,32 +1,34 @@
import React, {useRef} from 'react';
import {useDispatch} from 'react-redux'
import AlertContainers from '../../alert/containers/AlertContainers'
+import uuid from 'react-uuid'
-const Editor = ({ onClick, addAlert, alertList, serverInfo }) => {
+const Editor = ({ addFrame, addAlert, alertList, database, executeCypherQuery }) => {
+
const dispatch = useDispatch();
let reqString = useRef()
+
const clearReqString = () => (reqString.current.value = '' );
const onEnter = (e) => {
if(e.keyCode === 13){
- addFrame()
- clearReqString()
+ onClick()
}
}
- const addFrame = () => {
- if (serverInfo.status === 'disconnected' && reqString.current.value.startsWith('match')) {
+ const onClick = () => {
+ const refKey = uuid()
+ if (database.status === 'disconnected' && reqString.current.value.match('(match|create).*')) {
dispatch(() => addAlert('ErrorNoDatabaseConnected'))
- return;
- } if (serverInfo.status === 'disconnected' && reqString.current.value === ':server status') {
+ } else if (database.status === 'disconnected' && reqString.current.value === ':server status') {
dispatch(() => addAlert('ErrorNoDatabaseConnected'))
- return;
} else {
- dispatch(() => onClick(reqString.current.value))
- return;
+ dispatch(() => [addFrame(reqString.current.value, refKey), executeCypherQuery([refKey, reqString.current.value])])
}
+ clearReqString()
};
+
const alerts = alertList.map((alert) => {
return <AlertContainers key={alert.alerProps.key} alertKey={alert.alerProps.key} alertType={alert.alertType}/>;
});
@@ -43,7 +45,7 @@
aria-hidden="true"></span></button>
<button className="frame-head-button btn btn-link" type="button"><span className="fa fa-eraser fa-lg"
aria-hidden="true"></span></button>
- <button className="frame-head-button btn btn-link" type="button" onClick={() => [addFrame(), clearReqString()]}><span className="fa fa-play-circle-o fa-lg"
+ <button className="frame-head-button btn btn-link" type="button" onClick={() => onClick()}><span className="fa fa-play-circle-o fa-lg"
aria-hidden="true"></span></button>
</div>
</div>
diff --git a/frontend/src/components/contents/presentations/Frames.jsx b/frontend/src/components/contents/presentations/Frames.jsx
index 98cdc32..6a9ce40 100644
--- a/frontend/src/components/contents/presentations/Frames.jsx
+++ b/frontend/src/components/contents/presentations/Frames.jsx
@@ -2,10 +2,11 @@
import ServerStatus from '../../../components/frame/containers/ServerStatusContainer'
import ServerConnect from '../../../components/frame/containers/ServerConnectContainer'
import ServerDisconnect from '../../../components/frame/containers/ServerDisconnectContainer'
-import CypherResult from '../../../components/frame/containers/CypherResultContainers'
+import CypherMatchResult from '../../../components/frame/containers/CypherMatchResultContainers'
+import CypherDmlResult from '../../../components/frame/containers/CypherDmlResultContainers'
-const Frames = ({ frameList }) => {
+const Frames = ({ frameList, queryResult }) => {
const frames = frameList.map((frame) => {
if (frame.frameName === 'ServerStatus') {
return <ServerStatus key={frame.frameProps.key} refKey={frame.frameProps.key} reqString={frame.frameProps.reqString}/>;
@@ -14,7 +15,12 @@
} else if (frame.frameName === 'ServerDisconnect') {
return <ServerDisconnect key={frame.frameProps.key} refKey={frame.frameProps.key} reqString={frame.frameProps.reqString}/>;
} else if (frame.frameName === 'CypherResultFrame') {
- return <CypherResult key={frame.frameProps.key} refKey={frame.frameProps.key} reqString={frame.frameProps.reqString}/>;
+ if (queryResult.hasOwnProperty(frame.frameProps.key) && queryResult[frame.frameProps.key]['command'].toUpperCase() === 'GRAPH') {
+ return <CypherDmlResult key={frame.frameProps.key} refKey={frame.frameProps.key} reqString={frame.frameProps.reqString}/>;
+ } else {
+ return <CypherMatchResult key={frame.frameProps.key} refKey={frame.frameProps.key} reqString={frame.frameProps.reqString}/>;
+ }
+
}
return '';
});
diff --git a/frontend/src/components/cypherresult/containers/CypherResultCytoscapeContainer.js b/frontend/src/components/cypherresult/containers/CypherResultCytoscapeContainer.js
index 003fab6..ce2f416 100644
--- a/frontend/src/components/cypherresult/containers/CypherResultCytoscapeContainer.js
+++ b/frontend/src/components/cypherresult/containers/CypherResultCytoscapeContainer.js
@@ -1,53 +1,24 @@
import { connect } from 'react-redux'
import CypherResultCytoscape from '../presentations/CypherResultCytoscape'
+import {setLabels} from '../../../features/cypher/CypherSlice'
+import { generateCytoscapeElement, reGenerateCytoscapeElements } from '../../../features/cypher/CypherUtil'
const mapStateToProps = (state, ownProps) => {
const { refKey } = ownProps
- const getRandomColor = () => {
- var letters = '0123456789ABCDEF';
- var color = '#';
- for (var i = 0; i < 6; i++) {
- color += letters[Math.floor(Math.random() * 16)];
+ const generateElements = () => {
+ if (Object.keys(state.cypher.labels.nodeLabels).length > 0) {
+ return reGenerateCytoscapeElements(state.cypher.queryResult[refKey], state.cypher.labels)
+ } else {
+ return generateCytoscapeElement(state.cypher.queryResult[refKey])
}
- return color;
+
}
-
- const generateCytoscapeElement = (data) => {
- let nodes = []
- let edges = []
- let nodeLegend = {}
- let edgeLegend = {}
-
- if (data) {
- data['response']['data']['rows'].forEach((row, index) => {
- for (const [alias, val] of Object.entries(row)) {
- let labelName = val['label']
- if (val['start'] && val['end']) {
- if (!edgeLegend.hasOwnProperty(labelName)) { edgeLegend[labelName] = getRandomColor() }
- edges.push(
- { group: 'edges', data: { id: val.id, source: val.start, target: val.end, label: val.label, backgroundColor: edgeLegend[labelName] }, alias: alias, backgroundColor: nodeLegend[labelName], classes: ['node'] }
- )
- } else {
- if (!nodeLegend.hasOwnProperty(labelName)) { nodeLegend[labelName] = getRandomColor() }
- nodes.push(
- { group: 'nodes', data: { id: val.id, label: val.label, backgroundColor: nodeLegend[labelName] }, alias: alias, backgroundColor: nodeLegend[labelName], classes: ['node'] }
- )
- }
- }
- });
-
- }
- return { legend : {nodeLegend: nodeLegend, edgeLegend: edgeLegend}, elements : { nodes: nodes, edges: edges }}
-
- }
-
-
return {
- data: generateCytoscapeElement(state.cypher.queryResult[refKey])
+ data: generateElements()
}
}
-const mapDispatchToProps = {}
+const mapDispatchToProps = { setLabels }
export default connect(mapStateToProps, mapDispatchToProps)(CypherResultCytoscape);
diff --git a/frontend/src/components/cypherresult/containers/CypherResultMetaContainer.js b/frontend/src/components/cypherresult/containers/CypherResultMetaContainer.js
index 79c8160..9f35142 100644
--- a/frontend/src/components/cypherresult/containers/CypherResultMetaContainer.js
+++ b/frontend/src/components/cypherresult/containers/CypherResultMetaContainer.js
@@ -9,8 +9,12 @@
let data = {}
if (state.cypher.queryResult[refKey]) {
database = state.database
- query = state.cypher.queryResult[refKey].response.query
- data = state.cypher.queryResult[refKey].response.data
+ query = state.cypher.queryResult[refKey].query
+ data = {columns : state.cypher.queryResult[refKey].columns
+ , command : state.cypher.queryResult[refKey].command
+ , rowCount : state.cypher.queryResult[refKey].rowCount
+ , rows : state.cypher.queryResult[refKey].rows
+ }
}
return {
diff --git a/frontend/src/components/cypherresult/containers/CypherResultTableContainer.js b/frontend/src/components/cypherresult/containers/CypherResultTableContainer.js
index c8b16da..4eefbae 100644
--- a/frontend/src/components/cypherresult/containers/CypherResultTableContainer.js
+++ b/frontend/src/components/cypherresult/containers/CypherResultTableContainer.js
@@ -4,13 +4,18 @@
const mapStateToProps = (state, ownProps) => {
const { refKey } = ownProps
const generateTableData = (data) => {
+ console.log(">>>>>>>>>>>>> ", data)
let columns = []
let rows = []
+ let command = null
+ let rowCount = null
if (data) {
- columns = data['response']['data']['columns']
- rows = data['response']['data']['rows']
+ columns = data['columns']
+ rows = data['rows']
+ command = data['command']
+ rowCount = data['rowCount']
}
- return { columns: columns, rows: rows }
+ return { command : command, rowCount : rowCount, columns: columns, rows: rows }
}
return {
data : generateTableData(state.cypher.queryResult[refKey])
diff --git a/frontend/src/components/cypherresult/containers/CypherResultTextContainer.js b/frontend/src/components/cypherresult/containers/CypherResultTextContainer.js
index 12fe6de..f29015c 100644
--- a/frontend/src/components/cypherresult/containers/CypherResultTextContainer.js
+++ b/frontend/src/components/cypherresult/containers/CypherResultTextContainer.js
@@ -7,8 +7,8 @@
let columns = []
let rows = []
if (data) {
- columns = data['response']['data']['columns']
- rows = data['response']['data']['rows']
+ columns = data['columns']
+ rows = data['rows']
}
return { columns: columns, rows: rows }
}
diff --git a/frontend/src/components/cypherresult/presentations/CypherResultCytoscape.jsx b/frontend/src/components/cypherresult/presentations/CypherResultCytoscape.jsx
index f7f3086..98acc09 100644
--- a/frontend/src/components/cypherresult/presentations/CypherResultCytoscape.jsx
+++ b/frontend/src/components/cypherresult/presentations/CypherResultCytoscape.jsx
@@ -1,95 +1,148 @@
-import React, {useRef, useLayoutEffect, useEffect, useState} from 'react';
-import Cytoscape from 'cytoscape';
-import COSEBilkent from 'cytoscape-cose-bilkent';
+import React, { forwardRef, useEffect, useRef, useState, useImperativeHandle } from 'react';
+import { useDispatch } from 'react-redux'
+import { labelColors, nodeLabelSizes, edgeLabelSizes } from '../../../features/cypher/CypherUtil'
+import CypherResultCytoscapeChart from './CypherResultCytoscapeChart'
import CypherResultCytoscapeLegend from './CypherResultCytoscapeLegend'
-
-Cytoscape.use(COSEBilkent);
-
-const CypherResultCytoscape = ({data}) => {
-
- const targetRef = useRef();
- const [dimensions, setDimensions] = useState({width: 0 , height: 0});
-
- useLayoutEffect(() => {
- if (targetRef.current) {
- setDimensions({
- width: targetRef.current.offsetWidth,
- height: targetRef.current.offsetHeight
- });
- }
- }, []);
+import CypherResultCytoscapeFooter from './CypherResultCytoscapeFooter'
- const containerRef = useRef();
+const CypherResultCytoscape = forwardRef((props, ref) => {
+ const [footerData, setFooterData] = useState({})
+ const [legendData, setLegendData] = useState({ edgeLegend: {}, nodeLegend: {} })
+ const [elements, setElements] = useState({ edges: [], nodes: [] })
+ const [isReloading, setIsReloading] = useState(false)
+ const dispatch = useDispatch()
+ const chartRef = useRef()
- useEffect(() => {
- const stylesheet = [
- {
- selector: 'node',
- style: {
- width: 70,
- height: 70,
- label: 'data(label)',
- 'background-color': function( ele ) { return ele == null ? '#FFF' : ele.data('backgroundColor'); },
- "text-valign" : "center",
- "text-halign" : "center"
- }
- },
- {
- selector: 'edge',
- style: {
- width: 6,
- 'line-color': function( ele ) { return ele == null ? '#FFF' : ele.data('backgroundColor'); }
- }
- }
- ]
+ useEffect(() => {
- const layout = { name : 'cose-bilkent' }
-
- const config = {
- // Common Options
- container: containerRef.current,
- style: stylesheet,
- elements: data['elements'],
- layout: layout,
- // Viewport Options
- zoom: 1,
- pan: { x: 0, y: 0 },
- // Interaction Options
- minZoom: 1e-50,
- maxZoom: 1e50,
- zoomingEnabled: true,
- userZoomingEnabled: true,
- panningEnabled: true,
- userPanningEnabled: true,
- boxSelectionEnabled: true,
- selectionType: 'single',
- touchTapThreshold: 8,
- desktopTapThreshold: 4,
- autolock: false,
- autoungrabify: false,
- autounselectify: false,
- // Rendering Options
- headless: false,
- styleEnabled: true,
- hideEdgesOnViewport: false,
- textureOnViewport: false,
- motionBlur: false,
- motionBlurOpacity: 0.2,
- wheelSensitivity: 1,
- pixelRatio: 'auto'
- };
-
- Cytoscape(config);
- }, [data]);
+ if (props.data['legend'] !== undefined && Object.keys(props.data['legend']['nodeLegend']).length > 0) {
+
+ setIsReloading(false)
+
+ setLegendData(props.data['legend'])
+
+ setElements(props.data.elements)
+ }
+ })
+
+ const getFooterData = (props) => {
+ if (props.type === 'labels') {
+
+ props.data['captions'] = ['gid', 'label'].concat(Array.from(chartRef.current.getCaptions(props.data.type, props.data.label)))
+
+ if (props.data.type === 'node') {
+ props.data['selectedCaption'] = legendData.nodeLegend[props.data.label].caption
+ } else {
+ props.data['selectedCaption'] = legendData.edgeLegend[props.data.label].caption
+ }
+ }
+
+ setFooterData(props)
+ }
+
+ const addLegendData = (addedLegendData) => {
+ setIsReloading(false)
+ setLegendData(addedLegendData)
+ }
+
+ const colorChange = (elementType, label, color) => {
+ let footerObj = footerData.data
+ footerObj.backgroundColor = color.color
+ footerObj.fontColor = color.fontColor
+ setIsReloading(false)
+ setFooterData(Object.assign({}, footerData, { data: footerObj }))
+
+ if (elementType === 'node') {
+ let nodeLegendObj = legendData.nodeLegend
+ if (nodeLegendObj.hasOwnProperty(label)) {
+ nodeLegendObj[label]['color'] = color.color
+ nodeLegendObj[label]['borderColor'] = color.borderColor
+ nodeLegendObj[label]['fontColor'] = color.fontColor
+ }
+ setLegendData(Object.assign({}, legendData, { nodeLegend: nodeLegendObj }))
+ chartRef.current.colorChange(elementType, label, color);
- return <div className="chart-area" ref={targetRef}>
- <CypherResultCytoscapeLegend legendData={data['legend']}/>
- <div ref={containerRef} style={ { width: dimensions.width , height: dimensions.height, position:'absolute', 'zIndex':1 } } />
- </div>
-}
+ } else if (elementType === 'edge') {
+ let edgeLegendObj = legendData.edgeLegend
+ if (edgeLegendObj.hasOwnProperty(label)) {
+ edgeLegendObj[label]['color'] = color.color
+ edgeLegendObj[label]['borderColor'] = color.borderColor
+ edgeLegendObj[label]['fontColor'] = color.fontColor
+ }
+ setLegendData(Object.assign({}, legendData, { edgeLegend: edgeLegendObj }))
+ chartRef.current.colorChange(elementType, label, Object.assign(color, { fontColor: '#2A2C34' }));
+ }
+
+ dispatch(() => props.setLabels(elementType, label, { borderColor: color.borderColor, color: color.color, fontColor: color.fontColor }))
+ }
+
+ const sizeChange = (elementType, label, size) => {
+ let footerObj = footerData.data
+ footerObj.size = size
+ setFooterData(Object.assign({}, footerData, { data: footerObj }))
+ setIsReloading(false)
+ chartRef.current.sizeChange(elementType, label, size);
+
+ if (elementType === 'node') {
+ let nodeLegendObj = legendData.nodeLegend
+ if (nodeLegendObj.hasOwnProperty(label)) {
+ nodeLegendObj[label].size = size
+ }
+ setLegendData(Object.assign({}, legendData, { nodeLegend: nodeLegendObj }))
+ } else if (elementType === 'edge') {
+ let edgeLegendObj = legendData.edgeLegend
+ if (edgeLegendObj.hasOwnProperty(label)) {
+ edgeLegendObj[label].size = size
+ }
+ setLegendData(Object.assign({}, legendData, { edgeLegend: edgeLegendObj }))
+ }
+ dispatch(() => props.setLabels(elementType, label, { size: size }))
+ }
+
+ const captionChange = (elementType, label, caption) => {
+ chartRef.current.captionChange(elementType, label, caption);
+ let footerObj = footerData.data
+ footerObj.captions = ['gid', 'label'].concat(Array.from(chartRef.current.getCaptions(elementType, label)))
+ footerObj.selectedCaption = caption
+ setFooterData(Object.assign({}, footerData, { data: footerObj }))
+
+ if (elementType === 'node') {
+ let nodeLegendObj = legendData.nodeLegend
+ if (nodeLegendObj.hasOwnProperty(label)) {
+ nodeLegendObj[label].caption = caption
+ }
+ setLegendData(Object.assign({}, legendData, { nodeLegend: nodeLegendObj }))
+ } else if (elementType === 'edge') {
+ let edgeLegendObj = legendData.edgeLegend
+ if (edgeLegendObj.hasOwnProperty(label)) {
+ edgeLegendObj[label].caption = caption
+ }
+ setLegendData(Object.assign({}, legendData, { edgeLegend: edgeLegendObj }))
+ }
+ dispatch(() => props.setLabels(elementType, label, { caption: caption }))
+ }
+
+
+ useImperativeHandle(ref, () => ({
+
+ getCy() {
+ return chartRef.current.getCy();
+ },
+
+ resetChart() {
+ return chartRef.current.resetChart();
+ }
+ }));
+
+ return <div className="chart-frame-area">
+ <CypherResultCytoscapeLegend onLabelClick={getFooterData} isReloading={isReloading} legendData={legendData} />
+ <CypherResultCytoscapeChart onElementsMouseover={getFooterData} ref={chartRef} legendData={legendData} elements={elements} addLegendData={addLegendData} />
+ <CypherResultCytoscapeFooter colorChange={colorChange} sizeChange={sizeChange} captionChange={captionChange} footerData={footerData} nodeLabelSizes={nodeLabelSizes} edgeLabelSizes={edgeLabelSizes} labelColors={labelColors} />
+ </div>
+})
export default CypherResultCytoscape
\ No newline at end of file
diff --git a/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeChart.jsx b/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeChart.jsx
new file mode 100644
index 0000000..f593c2a
--- /dev/null
+++ b/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeChart.jsx
@@ -0,0 +1,413 @@
+import React, { Component } from 'react';
+import cytoscape from 'cytoscape';
+import cxtmenu from 'cytoscape-cxtmenu'
+import { generateCytoscapeElement } from '../../../features/cypher/CypherUtil'
+import COSEBilkent from 'cytoscape-cose-bilkent';
+
+
+cytoscape.use(COSEBilkent);
+cytoscape.use(cxtmenu);
+
+const getLabel = (ele, captionProp) => {
+ if (captionProp === 'gid') {
+ ele.isNode() ? selectedLabel.node[ele.data('label')] = 'gid' : selectedLabel.edge[ele.data('label')] = 'gid'
+ return "[ " + ele.data('id') + " ]"
+ } else if (captionProp === 'label') {
+ ele.isNode() ? selectedLabel.node[ele.data('label')] = 'label' : selectedLabel.edge[ele.data('label')] = 'label'
+ return "[ :" + ele.data('label') + " ]"
+ } else {
+ const props = ele.data('properties')
+ if (props[captionProp] === undefined) {
+ ele.isNode() ? selectedLabel.node[ele.data('label')] = 'gid' : selectedLabel.edge[ele.data('label')] = 'gid'
+ return "[ " + ele.data('id') + " ]"
+ }
+ else {
+ ele.isNode() ? selectedLabel.node[ele.data('label')] = captionProp : selectedLabel.edge[ele.data('label')] = captionProp
+ return props[captionProp]
+ }
+ }
+
+}
+
+let selectedLabel = {
+ node: {},
+ edge: {}
+}
+
+let initLocation = {}
+
+const stylesheet = [
+ {
+ selector: 'node',
+ style: {
+ width: function (ele) { return ele == null ? 55 : ele.data('size'); },
+ height: function (ele) { return ele == null ? 55 : ele.data('size'); },
+ label: function (ele) { const captionProp = ele.data('caption'); return ele == null ? '' : getLabel(ele, captionProp); },
+ 'background-color': function (ele) { return ele == null ? '#FFF' : ele.data('backgroundColor'); },
+ 'border-width': "3px",
+ 'border-color': function (ele) { return ele == null ? '#FFF' : ele.data('borderColor'); },
+ 'border-opacity': 0.6,
+ "text-valign": "center",
+ "text-halign": "center",
+ color: function (ele) { return ele == null ? '#FFF' : ele.data('fontColor'); },
+ "font-size": "10px",
+ "text-wrap": "ellipsis",
+ "text-max-width": function (ele) { return ele == null ? 55 : ele.data('size'); }
+ }
+ },
+ {
+ selector: 'node.highlight',
+ style: {
+ 'border-width': "6px",
+ 'border-color': "#B2EBF4"
+ }
+ },
+ {
+ selector: 'node:selected',
+ style: {
+ 'border-width': "6px",
+ 'border-color': "#B2EBF4"
+ }
+ },
+ {
+ selector: 'edge',
+ style: {
+ width: function (ele) { return ele == null ? 1 : ele.data('size'); },
+ label: function (ele) { const captionProp = ele.data('caption'); return ele == null ? '' : getLabel(ele, captionProp); },
+ 'text-background-color': '#FFF',
+ 'text-background-opacity': 1,
+ 'text-background-padding': '3px',
+ 'line-color': function (ele) { return ele == null ? '#FFF' : ele.data('backgroundColor'); },
+ 'target-arrow-color': function (ele) { return ele == null ? '#FFF' : ele.data('backgroundColor'); },
+ 'target-arrow-shape': 'triangle',
+ 'curve-style': 'bezier',
+ color: function (ele) { return ele == null ? '#FFF' : ele.data('fontColor'); },
+ "font-size": "10px",
+ "text-rotation": "autorotate"
+ }
+ },
+ {
+ selector: 'edge.highlight',
+ style: {
+ width: function (ele) { return ele == null ? 1 : ele.data('size'); },
+ 'line-color': "#B2EBF4",
+ 'target-arrow-color': "#B2EBF4",
+ 'target-arrow-shape': 'triangle',
+ 'curve-style': 'bezier'
+ }
+ },
+ {
+ selector: 'edge:selected',
+ style: {
+ width: function (ele) { return ele == null ? 1 : ele.data('size'); },
+ 'line-color': "#B2EBF4",
+ 'target-arrow-color': "#B2EBF4",
+ 'target-arrow-shape': 'triangle',
+ 'curve-style': 'bezier'
+ }
+ }
+]
+
+const coseBilkentLayout = {
+ name: 'cose-bilkent'
+ , idealEdgeLength: 100
+ , nodeDimensionsIncludeLabels: true
+ , fit: true
+ , padding: 10
+ , stop: function (event) {
+ event.cy.nodes().forEach(function (ele) {
+ initLocation[ele.id()] = { x: ele.position().x, y: ele.position().y }
+ });
+ }
+}
+
+const ciseLayout = {
+ name: 'cise',
+ animate: true
+}
+
+const d3Layout = {
+ name: 'd3-force',
+ animate: true, // whether to show the layout as it's running; special 'end' value makes the layout animate like a discrete layout
+ fit: false,
+ fixedAfterDragging: true,
+ linkId: function id(d) {
+ return d.id;
+ },
+ linkDistance: 80,
+ linkStrength: -300,
+ manyBodyStrength: 0,
+ ready: function () { },
+ stop: function (event) {
+ event.cy.nodes().forEach(function (ele) {
+ initLocation[ele.id()] = { x: ele.position().x, y: ele.position().y }
+ });
+ },
+ randomize: true,
+ infinite: false
+ // some more options here...
+}
+
+const conf = {
+ // Common Options
+ style: stylesheet,
+ layout: coseBilkentLayout,
+ // Viewport Options
+ zoom: 1,
+ // Interaction Options
+ minZoom: 0.5,
+ maxZoom: 4,
+ zoomingEnabled: false, //true
+ userZoomingEnabled: false, //true
+ panningEnabled: true,
+ userPanningEnabled: true,
+ boxSelectionEnabled: false, //true
+ selectionType: 'single',
+ touchTapThreshold: 8,
+ desktopTapThreshold: 4,
+ autolock: false,
+ autoungrabify: false,
+ autounselectify: false,
+ // Rendering Options
+ headless: false,
+ styleEnabled: true,
+ hideEdgesOnViewport: false,
+ textureOnViewport: false,
+ motionBlur: false,
+ motionBlurOpacity: 0.2,
+ wheelSensitivity: 1,
+ pixelRatio: 'auto'
+};
+
+
+
+
+class CytoscapeComponent extends Component {
+ constructor(props) {
+ super(props);
+ this.cy = ''
+ this.menu = ''
+ this.addElements = this.addElements.bind(this)
+ }
+
+ closeCxtmenu() {
+ this.menu.destroy()
+ }
+
+ addElements(d) {
+ const generatedData = generateCytoscapeElement( d )
+ if (generatedData.elements.nodes.length === 0) {
+ alert("No data to extend.")
+ return
+ }
+ this.cy.add(generatedData.elements, generatedData.legend)
+ this.cy.layout(coseBilkentLayout).run()
+
+ this.handleUserAction(this.props)
+ this.props.addLegendData(generatedData.legend)
+ }
+
+ handleUserAction(props) {
+ this.cy.elements().bind('mouseover', (e) => {
+ props.onElementsMouseover({ type: 'elements', data: e.target.data() })
+ e.target.addClass('highlight')
+ })
+
+ this.cy.elements().bind('mouseout', (e) => {
+ if (this.cy.elements(':selected').length === 0) {
+ props.onElementsMouseover({ type: 'background', data: { nodeCount: this.cy.nodes().size(), edgeCount: this.cy.edges().size() } })
+ } else {
+ props.onElementsMouseover({ type: 'elements', data: this.cy.elements(':selected')[0].data() })
+ }
+
+ e.target.removeClass('highlight')
+ })
+
+ this.cy.elements().bind('click', (e) => {
+ const ele = e.target
+ this.cy.elements(':selected').unselect()
+ ele.select()
+ })
+
+ this.cy.bind('click', (e) => {
+ if (e.target === this.cy) {
+ props.onElementsMouseover({ type: 'background', data: { nodeCount: this.cy.nodes().size(), edgeCount: this.cy.edges().size() } })
+ }
+ })
+ }
+
+ componentDidMount() {
+ conf.container = this.refs.cyelement;
+ conf.pan = { x: this.refs.cyelement.offsetWidth / 3, y: 50 }
+ let initCy = cytoscape(conf);
+ this.cy = initCy
+
+
+ this.cxtMenuConf = {
+ menuRadius: 75,
+ selector: 'node',
+ commands: [
+ {
+ content: '<span class="" ><i class="fas fa-lock-open fa-lg"></i></span>',
+ select: function (ele) {
+ ele.animate({ position: initLocation[ele.id()] })
+ }
+ },
+
+ {
+ content: '<span class=""><i class="fas fa-project-diagram fa-lg"></i></span>',
+ select: function (ele) {
+ fetch('/api/v1/cypher',
+ {
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ cmd: 'MATCH (S)-[R]-(T) WHERE id(S) = \'' + ele.id() + '\' RETURN S, R, T' })
+ })
+ .then(res => res.json())
+ .then(data => {
+ this.addElements(data)
+ })
+ }.bind(this)
+ },
+
+ {
+ content: '<span class=""><i class="fas fa-eye-slash fa-lg"></i></span>',
+ select: function (ele) {
+ ele.remove()
+ }
+ },
+
+ {
+ content: '<span class=""><i class="far fa-window-close fa-lg"></i></span>',
+ select: function (ele) {
+ }
+ }
+ ],
+ fillColor: 'rgba(210, 213, 218, 1)',
+ activeFillColor: 'rgba(166, 166, 166, 1)',
+ activePadding: 0,
+ indicatorSize: 0,
+ separatorWidth: 4,
+ spotlightPadding: 3,
+ minSpotlightRadius: 11,
+ maxSpotlightRadius: 99,
+ openMenuEvents: 'click',
+ itemColor: '#2A2C34',
+ itemTextShadowColor: 'transparent',
+ zIndex: 9999,
+ atMouse: false
+ }
+ this.menu = initCy.cxtmenu(this.cxtMenuConf)
+ }
+
+ shouldComponentUpdate() {
+ return false;
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.props.elements.nodes.length === 0) {
+ this.cy.add(nextProps.elements)
+ this.cy.layout(coseBilkentLayout).run()
+
+ this.handleUserAction(nextProps)
+
+ } else {
+ if (nextProps.legendData !== undefined) {
+
+ for (const [label, legend] of Object.entries(nextProps.legendData.nodeLegend)) {
+ this.colorChange('node', label, {color : legend.color, borderColor : legend.borderColor, fontColor : legend.fontColor})
+ this.sizeChange('node', label, legend.size)
+ this.captionChange('node', label, legend.caption)
+ }
+
+ for (const [label, legend] of Object.entries(nextProps.legendData.edgeLegend)) {
+ this.colorChange('edge', label, {color : legend.color, borderColor : legend.borderColor, fontColor : legend.fontColor})
+ this.sizeChange('edge', label, legend.size)
+ this.captionChange('edge', label, legend.caption)
+ }
+ }
+ this.cy.resize()
+ }
+ }
+
+ componentWillUnmount() {
+ this.menu.destroy()
+ this.cy.destroy()
+ }
+
+
+ resetChart() {
+ this.props.elements.nodes = []
+ this.props.elements.edges = []
+ this.cy.elements().remove()
+ }
+
+ getCaptions(elementType, label) {
+ const eles = this.cy.elements(elementType + '[label = "' + label + '"]').jsons()
+ let extendedSet = new Set([])
+ eles.forEach((ele) => {
+ extendedSet = new Set([...extendedSet, ...Object.keys(ele.data.properties)])
+ })
+ return extendedSet
+ }
+
+ getCurrecntCaption(elementType, label) {
+ if (elementType === 'edge' && selectedLabel[elementType][label] === undefined) {
+ selectedLabel[elementType][label] = 'label'
+ }
+
+ return selectedLabel[elementType][label]
+ }
+
+ getCy() {
+ return this.cy;
+ }
+
+ colorChange(elementType, label, color) {
+ let c = {}
+
+ if (Array.isArray(color)) {
+ c['color'] = color[0]
+ c['borderColor'] = color[1]
+ c['fontColor'] = color[2]
+ } else {
+ c = color
+ }
+
+ if (elementType === 'node') {
+ this.cy.nodes('[label = "' + label + '"]').data("backgroundColor", c.color).data("borderColor", c.borderColor).data("fontColor", c.fontColor)
+ } else if (elementType === 'edge') {
+ this.cy.edges('[label = "' + label + '"]').data("backgroundColor", c.color).data("fontColor", c.fontColor)
+ }
+
+ }
+
+ sizeChange(elementType, label, size) {
+ const changedData = this.cy.elements(elementType + '[label = "' + label + '"]').data("size", size)
+
+ if (size > 6) {
+ changedData.style('text-background-opacity', 0)
+ } else {
+ changedData.style('text-background-opacity', 1)
+ }
+ }
+
+ captionChange(elementType, label, caption) {
+ if (caption === 'gid') {
+ this.cy.elements(elementType + '[label = "' + label + '"]').style('label', function (ele) { return ele == null ? '' : "[ " + ele.data('id') + " ]"; })
+ } else if (caption === 'label') {
+ this.cy.elements(elementType + '[label = "' + label + '"]').style('label', function (ele) { return ele == null ? '' : "[ :" + ele.data('label') + " ]"; })
+ } else {
+ this.cy.elements(elementType + '[label = "' + label + '"]').style('label', function (ele) { return ele == null ? '' : (ele.data('properties')[caption] == null ? '' : ele.data('properties')[caption]) })
+ }
+ }
+
+ render() {
+ return <div id="cyto" className="chart-area" ref="cyelement" />
+ }
+}
+
+export default CytoscapeComponent;
\ No newline at end of file
diff --git a/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeFooter.jsx b/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeFooter.jsx
new file mode 100644
index 0000000..4723484
--- /dev/null
+++ b/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeFooter.jsx
@@ -0,0 +1,93 @@
+import React, {useState} from 'react';
+import { Badge } from 'react-bootstrap'
+import uuid from 'react-uuid'
+import { updateLabelColor, updateNodeLabelSize, updateEdgeLabelSize, updateLabelCaption } from '../../../features/cypher/CypherUtil'
+
+const CypherResultCytoscapeFooter = ({ footerData, labelColors, nodeLabelSizes, edgeLabelSizes, colorChange, sizeChange, captionChange }) => {
+ const extractData = (d) => {
+ let extractedData = []
+ for (const [alias, val] of Object.entries(d)) {
+ extractedData.push(<span key={uuid()} className="label"><strong className="pl-3">{alias} : </strong> {val}</span>)
+ }
+ return extractedData
+ }
+
+ const displayFooterData = () => {
+
+ if (footerData.type === 'elements') {
+ const isEdge = footerData.data.source ? {} : { pill: true }
+
+ return (
+ <div className="pl-3">
+ <Badge className="px-3 py-1" {...isEdge} style={{ backgroundColor: footerData.data.backgroundColor, color: footerData.data.fontColor }}>{footerData.data.label}</Badge>
+ <span className="label"><strong className="pl-3"><gid> : </strong> {footerData.data.id}</span>
+ {extractData(footerData.data.properties)}
+ </div>
+ )
+
+ } else if (footerData.type === 'background') {
+ return <span className="label pl-3">Displaying <strong>{footerData.data.nodeCount}</strong> nodes, <strong>{footerData.data.edgeCount}</strong> edges</span>
+ } else if (footerData.type === 'labels') {
+ const isEdge = footerData.data.type === 'edge' ? {} : { pill: true }
+
+
+ const generateButton = () => {
+ if (footerData.data.type === 'node') {
+ return nodeLabelSizes.map((labelSize, i) => {
+ return nodeSizeButton(labelSize.size, i)
+ })
+ } else if (footerData.data.type === 'edge') {
+ return edgeLabelSizes.map((labelSize, i) => {
+ return edgeSizeButton(labelSize.size, i)
+ })
+ }
+ }
+
+ const nodeSizeButton = (nodeSize, i) => {
+ let size = (i * 3) + 12;
+ return <button onClick={() => [updateNodeLabelSize(footerData.data.label, nodeSize), sizeChange(footerData.data.type, footerData.data.label, nodeSize)]}
+ key={uuid()}
+ type="button"
+ className={"btn sizeSelector node " + (footerData.data.size >= nodeSize ? " selectedSize " : "")}
+ style={{width: size+'px', height: size+'px'}} />
+ }
+
+ const edgeSizeButton = (edgeSize, i) => {
+ let size = (i * 3) + 12;
+ return <button onClick={() => [updateEdgeLabelSize(footerData.data.label, edgeSize), sizeChange(footerData.data.type, footerData.data.label, edgeSize)]}
+ key={uuid()}
+ type="button"
+ className={"btn sizeSelector edge " + (footerData.data.size >= edgeSize ? " selectedSize " : "")}
+ style={{width: size+18+'px', height: size+'px'}} />
+ }
+
+ return (
+ <div className="pl-3">
+ <Badge className="px-3 py-1" {...isEdge} style={{ backgroundColor: footerData.data.backgroundColor, color: footerData.data.fontColor }}>{footerData.data.label}</Badge>
+ <span className="label">
+ <span className="pl-3">Color : </span>
+ {labelColors.map((color)=>{return <button onClick={() => [updateLabelColor(footerData.data.label, color), colorChange(footerData.data.type, footerData.data.label, color)]} key={uuid()} type="button" className={"btn colorSelector " + (footerData.data.backgroundColor === color.color ? " selectedColor " : "")} style={{backgroundColor:color.color}}></button> })}
+ </span>
+ <span className="label">
+ <span className="pl-3">Size : </span>
+ {generateButton()}
+ </span>
+ <span className="label">
+ <span className="pl-3">Caption : </span>
+ {footerData.data.captions.map((caption) => {
+ return <button onClick={() => [updateLabelCaption(footerData.data.type, footerData.data.label, caption), captionChange(footerData.data.type, footerData.data.label, caption)]} key={uuid()} type="button" class={"btn captionSelector " + (footerData.data.selectedCaption === caption ? " btn-secondary " : " btn-outline-dark ")}><strong><{caption}></strong></button>
+ })}
+
+ </span>
+ </div>
+ )
+ }
+ }
+
+ return <div className="chart-footer-area text-muted">
+ {displayFooterData()}
+ </div>
+}
+
+
+export default CypherResultCytoscapeFooter
\ No newline at end of file
diff --git a/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeLegend.jsx b/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeLegend.jsx
index 07145ea..3155f2f 100644
--- a/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeLegend.jsx
+++ b/frontend/src/components/cypherresult/presentations/CypherResultCytoscapeLegend.jsx
@@ -1,30 +1,71 @@
-import React from 'react';
-import {Badge} from 'react-bootstrap'
+import React, { Component } from 'react';
+import { Badge } from 'react-bootstrap'
+import uuid from 'react-uuid'
-const CypherResultCytoscapeLegend = ({legendData}) => {
- const {nodeLegend, edgeLegend} = legendData
-
- const nodeBadges = []
- const edgeBadges = []
-
-
- for (const [label, color] of Object.entries(nodeLegend)) {
- nodeBadges.push(<Badge className="px-3 mx-1" pill key={label} style={{ backgroundColor : color, fontSize : '0.9rem' }}>{label}</Badge>)
+class CypherResultCytoscapeLegend extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ nodeBadges: new Map(),
+ edgeBadges: new Map()
+ }
}
- for (const [label, color] of Object.entries(edgeLegend)) {
- edgeBadges.push(<Badge className="px-3 mx-1" key={label} style={{ backgroundColor : color, fontSize : '0.9rem' }}>{label}</Badge>)
+ componentDidMount() {
}
-
- return <div className="legend-area pt-2" style={{ width: '100%', height:'50px', position:'absolute', 'zIndex':100}}>
- <div className="nodeLegend" style={{ width: '100%' }}>
- {nodeBadges}
+
+ shouldComponentUpdate() {
+ return true;
+ }
+
+ componentWillReceiveProps(nextProps) {
+ let newNodeBadges = this.state.nodeBadges
+ let newEdgeBadges = this.state.edgeBadges
+ if (nextProps.isReloading) {
+ newNodeBadges = new Map()
+ newEdgeBadges = new Map()
+ }
+
+ for (const [label, legend] of Object.entries(nextProps.legendData.nodeLegend)) {
+ newNodeBadges.set(label, <Badge className="nodeLabel px-3 py-2 mx-1 my-2" pill key={uuid()} onClick={() => nextProps.onLabelClick({ type: 'labels', data: { type: 'node', backgroundColor: legend.color, fontColor: legend.fontColor, size: legend.size, label: label } })} style={{ backgroundColor: legend.color, color: legend.fontColor }}>{label}</Badge>)
+ }
+
+ for (const [label, legend] of Object.entries(nextProps.legendData.edgeLegend)) {
+ newEdgeBadges.set(label, <Badge className="edgeLabel px-3 py-2 mx-1 my-2" key={uuid()} onClick={() => nextProps.onLabelClick({ type: 'labels', data: { type: 'edge', backgroundColor: legend.color, fontColor: legend.fontColor, size: legend.size, label: label } })} style={{ backgroundColor: legend.color, color: legend.fontColor }}>{label}</Badge>)
+ }
+
+ this.setState({nodeBadges : newNodeBadges})
+ this.setState({edgeBadges : newEdgeBadges})
+
+
+ }
+
+ componentWillUnmount() {
+ }
+
+
+ render() {
+ let nodeLedgend = []
+ let edgeLedgend = []
+
+ this.state.nodeBadges.forEach((value, key, mapObj) => {
+ return nodeLedgend.push(value)
+ })
+
+ this.state.edgeBadges.forEach((value, key, mapObj) => {
+ return edgeLedgend.push(value)
+ })
+
+ return <div className="legend-area" style={{ width: '100%' }}>
+ <div className="nodeLegend">
+ {nodeLedgend}
</div>
- <div className="edgeLegend" style={{ width: '100%' }}>
- {edgeBadges}
+ <div className="edgeLegend">
+ {edgeLedgend}
</div>
+
</div>
+ }
}
+export default CypherResultCytoscapeLegend
-
-export default CypherResultCytoscapeLegend
\ No newline at end of file
diff --git a/frontend/src/components/cypherresult/presentations/CypherResultTable.jsx b/frontend/src/components/cypherresult/presentations/CypherResultTable.jsx
index 116dc8b..c54e50e 100644
--- a/frontend/src/components/cypherresult/presentations/CypherResultTable.jsx
+++ b/frontend/src/components/cypherresult/presentations/CypherResultTable.jsx
@@ -1,7 +1,13 @@
import React from 'react';
import Table from 'react-bootstrap/Table'
const CypherResultTable = ({data}) => {
- return (
+ console.log("????????????", data)
+ if (data.command && data.command.toUpperCase() === 'GRAPH') {
+ return <span style={{margin:'25px'}}>Affected {data.rowCount === null ? 0 : data.rowCount} </span>
+
+
+ } else {
+ return (
<Table className="table table-hover">
<thead>
<tr >
@@ -24,6 +30,7 @@
</tbody>
</Table>
)
+ }
}
diff --git a/frontend/src/components/frame/containers/CypherDmlResultContainers.js b/frontend/src/components/frame/containers/CypherDmlResultContainers.js
new file mode 100644
index 0000000..8e41b68
--- /dev/null
+++ b/frontend/src/components/frame/containers/CypherDmlResultContainers.js
@@ -0,0 +1,12 @@
+import {connect} from 'react-redux'
+import {removeFrame} from '../../../features/frame/FrameSlice'
+import CypherDmlResultFrame from '../presentations/CypherDmlResultFrame'
+
+const mapStateToProps = (state) => {
+ return {
+ }
+}
+
+const mapDispatchToProps = { removeFrame }
+
+export default connect(mapStateToProps, mapDispatchToProps)(CypherDmlResultFrame);
diff --git a/frontend/src/components/frame/containers/CypherMatchResultContainers.js b/frontend/src/components/frame/containers/CypherMatchResultContainers.js
new file mode 100644
index 0000000..5bb06f5
--- /dev/null
+++ b/frontend/src/components/frame/containers/CypherMatchResultContainers.js
@@ -0,0 +1,12 @@
+import {connect} from 'react-redux'
+import {removeFrame} from '../../../features/frame/FrameSlice'
+import CypherMatchResultFrame from '../presentations/CypherMatchResultFrame'
+
+const mapStateToProps = (state) => {
+ return {
+ }
+}
+
+const mapDispatchToProps = { removeFrame }
+
+export default connect(mapStateToProps, mapDispatchToProps)(CypherMatchResultFrame);
diff --git a/frontend/src/components/frame/containers/CypherResultContainers.js b/frontend/src/components/frame/containers/CypherResultContainers.js
deleted file mode 100644
index dd2ca92..0000000
--- a/frontend/src/components/frame/containers/CypherResultContainers.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import {connect} from 'react-redux'
-import {executeCypherQuery} from '../../../features/cypher/CypherSlice'
-import {removeFrame} from '../../../features/frame/FrameSlice'
-import CypherResultFrame from '../presentations/CypherResultFrame'
-
-const mapStateToProps = (state) => {
- return {
- }
-}
-
-const mapDispatchToProps = { executeCypherQuery, removeFrame }
-
-export default connect(mapStateToProps, mapDispatchToProps)(CypherResultFrame);
diff --git a/frontend/src/components/frame/containers/ServerConnectContainer.js b/frontend/src/components/frame/containers/ServerConnectContainer.js
index 47b7cdf..2a4e834 100644
--- a/frontend/src/components/frame/containers/ServerConnectContainer.js
+++ b/frontend/src/components/frame/containers/ServerConnectContainer.js
@@ -1,5 +1,6 @@
import {connect} from 'react-redux'
import {connectToAgensGraph} from '../../../features/database/DatabaseSlice'
+import {getMetaData} from '../../../features/database/MetadataSlice'
import {removeFrame} from '../../../features/frame/FrameSlice'
import {addAlert} from '../../../features/alert/AlertSlice'
import ServerConnectFrame from '../presentations/ServerConnectFrame'
@@ -9,7 +10,7 @@
}
}
-const mapDispatchToProps = { connectToAgensGraph, removeFrame, addAlert }
+const mapDispatchToProps = { connectToAgensGraph, removeFrame, addAlert, getMetaData }
export default connect(mapStateToProps, mapDispatchToProps)(ServerConnectFrame);
diff --git a/frontend/src/components/frame/presentations/CypherResultFrame.jsx b/frontend/src/components/frame/presentations/CypherDmlResultFrame.jsx
similarity index 67%
copy from frontend/src/components/frame/presentations/CypherResultFrame.jsx
copy to frontend/src/components/frame/presentations/CypherDmlResultFrame.jsx
index 2ed56ad..6f05e7c 100644
--- a/frontend/src/components/frame/presentations/CypherResultFrame.jsx
+++ b/frontend/src/components/frame/presentations/CypherDmlResultFrame.jsx
@@ -1,5 +1,6 @@
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useState, createRef } from 'react';
import { useDispatch } from 'react-redux'
+import uuid from 'react-uuid'
import { Tab, Nav, Collapse } from 'react-bootstrap';
import CypherResultCytoscapeContainer from '../../cypherresult/containers/CypherResultCytoscapeContainer'
import CypherResultTableContainer from '../../cypherresult/containers/CypherResultTableContainer'
@@ -7,28 +8,27 @@
import CypherResultMetaContainer from '../../cypherresult/containers/CypherResultMetaContainer'
const CypherResultFrame = ({ refKey, reqString, removeFrame, executeCypherQuery }) => {
+ const chartAreaRef = createRef()
const [isExpanded, setIsExpanded] = useState(true)
+ const [isFullScreen, setIsFullScreen] = useState(false)
+
+ const expandFrame = () => {
+ setIsFullScreen(!isFullScreen)
+ }
- const dispatch = useDispatch();
-
- useEffect(() => {
- dispatch(() => executeCypherQuery([refKey, reqString]));
- }, [refKey, reqString, executeCypherQuery, dispatch])
return (
- <div className="card mt-3">
+ <div className={"card " + (isFullScreen ? " fullscreen " : "mt-3")}>
<div className="card-header">
<div className="d-flex card-title text-muted">
<div className="mr-auto"><strong> $ {reqString} </strong></div>
- <button className="frame-head-button btn btn-link px-3"><span className="fa fa-download fa-lg"
- aria-hidden="true"></span></button>
+ <button className="frame-head-button btn btn-link px-3">
+ <span className="fa fa-expand fa-lg" aria-hidden="true" onClick={() => expandFrame()}></span></button>
<button className="frame-head-button btn btn-link px-3"><span className="fa fa-paperclip fa-lg"
aria-hidden="true"></span></button>
<button className="frame-head-button btn btn-link px-3" data-toggle="collapse"
aria-expanded={isExpanded} onClick={() => setIsExpanded(!isExpanded)} aria-controls={refKey}>
<span className="fa fa-lg" aria-hidden="true"></span></button>
- <button className="frame-head-button btn btn-link px-3">
- <span className="fa fa-refresh fa-lg" aria-hidden="true"></span></button>
<button className="frame-head-button btn btn-link pl-3">
<span className="fa fa-times fa-lg" aria-hidden="true" onClick={() => removeFrame(refKey)}></span></button>
</div>
@@ -36,41 +36,25 @@
<Collapse in={isExpanded}>
<div className="card-body card-body-graph collapse" id={refKey}>
<div className="d-flex h-100">
- <Tab.Container defaultActiveKey="graph">
+ <Tab.Container defaultActiveKey="table">
<Nav variant="pills" className="flex-column graph-card-nav">
<Nav.Item>
- <Nav.Link eventKey="graph"><span className="fa fa-paperclip" aria-hidden="true"></span><br />Graph</Nav.Link>
- </Nav.Item>
-
- <Nav.Item>
<Nav.Link eventKey="table"><span className="fa fa-table" aria-hidden="true"></span><br />Table</Nav.Link>
</Nav.Item>
<Nav.Item>
- <Nav.Link eventKey="text"><span className="fa fa-font" aria-hidden="true"></span><br />Text</Nav.Link>
- </Nav.Item>
-
- <Nav.Item>
<Nav.Link eventKey="code"><span className="fa fa-terminal" aria-hidden="true"></span><br />Meta</Nav.Link>
</Nav.Item>
</Nav>
- <Tab.Content className="graph-card-content container-fluid" >
-
- <Tab.Pane eventKey="graph" style={{ height: '100%' }}>
- <CypherResultCytoscapeContainer refKey={refKey} />
- </Tab.Pane>
+ <Tab.Content className="graph-card-content container-fluid graph-tabpanel">
<Tab.Pane eventKey="table">
<CypherResultTableContainer refKey={refKey} />
</Tab.Pane>
- <Tab.Pane eventKey="text">
- <CypherResultTextContainer refKey={refKey} />
- </Tab.Pane>
-
<Tab.Pane eventKey="code">
<CypherResultMetaContainer refKey={refKey} />
</Tab.Pane>
diff --git a/frontend/src/components/frame/presentations/CypherResultFrame.jsx b/frontend/src/components/frame/presentations/CypherMatchResultFrame.jsx
similarity index 71%
rename from frontend/src/components/frame/presentations/CypherResultFrame.jsx
rename to frontend/src/components/frame/presentations/CypherMatchResultFrame.jsx
index 2ed56ad..ca35bca 100644
--- a/frontend/src/components/frame/presentations/CypherResultFrame.jsx
+++ b/frontend/src/components/frame/presentations/CypherMatchResultFrame.jsx
@@ -1,5 +1,6 @@
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useState, createRef } from 'react';
import { useDispatch } from 'react-redux'
+import uuid from 'react-uuid'
import { Tab, Nav, Collapse } from 'react-bootstrap';
import CypherResultCytoscapeContainer from '../../cypherresult/containers/CypherResultCytoscapeContainer'
import CypherResultTableContainer from '../../cypherresult/containers/CypherResultTableContainer'
@@ -7,28 +8,56 @@
import CypherResultMetaContainer from '../../cypherresult/containers/CypherResultMetaContainer'
const CypherResultFrame = ({ refKey, reqString, removeFrame, executeCypherQuery }) => {
+ const chartAreaRef = createRef()
const [isExpanded, setIsExpanded] = useState(true)
+ const [isFullScreen, setIsFullScreen] = useState(false)
+ const [zoomRate, setZoomRate] = useState(0)
+ const [pan, setPan] = useState({x : 0, y : 0})
+ const [cyZoomingEnabled, setCyZoomingEnabled] = useState(false)
+ const [cytoscapeContainerKey, setCytoscapeContainerKey] = useState(uuid())
const dispatch = useDispatch();
useEffect(() => {
- dispatch(() => executeCypherQuery([refKey, reqString]));
+ //dispatch(() => executeCypherQuery([refKey, reqString]));
+ setZoomRate(chartAreaRef.current.getCy().zoom())
+ setPan(chartAreaRef.current.getCy().pan())
}, [refKey, reqString, executeCypherQuery, dispatch])
+
+ const expandFrame = () => {
+ setIsFullScreen(!isFullScreen)
+ setCyZoomingEnabled(!cyZoomingEnabled)
+ const ref = chartAreaRef.current
+ window.setTimeout(resize, 500)
+ function resize() {
+ ref.getCy().resize()
+ ref.getCy().zoom({level : zoomRate, position: { x: 0, y: 0 }})
+ ref.getCy().zoomingEnabled(!cyZoomingEnabled)
+ ref.getCy().userZoomingEnabled(!cyZoomingEnabled)
+ }
+ }
+
+ const refreshFrame = () => {
+ setCytoscapeContainerKey(uuid())
+ }
+
return (
- <div className="card mt-3">
+ <div className={"card " + (isFullScreen ? " fullscreen " : "mt-3")}>
<div className="card-header">
<div className="d-flex card-title text-muted">
<div className="mr-auto"><strong> $ {reqString} </strong></div>
<button className="frame-head-button btn btn-link px-3"><span className="fa fa-download fa-lg"
aria-hidden="true"></span></button>
+ <button className="frame-head-button btn btn-link px-3">
+ <span className="fa fa-expand fa-lg" aria-hidden="true" onClick={() => expandFrame()}></span></button>
+ <button className="frame-head-button btn btn-link px-3">
+ <span className="fa fa-refresh fa-lg" aria-hidden="true" onClick={() => refreshFrame()}></span></button>
<button className="frame-head-button btn btn-link px-3"><span className="fa fa-paperclip fa-lg"
aria-hidden="true"></span></button>
<button className="frame-head-button btn btn-link px-3" data-toggle="collapse"
aria-expanded={isExpanded} onClick={() => setIsExpanded(!isExpanded)} aria-controls={refKey}>
<span className="fa fa-lg" aria-hidden="true"></span></button>
- <button className="frame-head-button btn btn-link px-3">
- <span className="fa fa-refresh fa-lg" aria-hidden="true"></span></button>
<button className="frame-head-button btn btn-link pl-3">
<span className="fa fa-times fa-lg" aria-hidden="true" onClick={() => removeFrame(refKey)}></span></button>
</div>
@@ -57,10 +86,10 @@
</Nav.Item>
</Nav>
- <Tab.Content className="graph-card-content container-fluid" >
+ <Tab.Content className="graph-card-content container-fluid graph-tabpanel">
<Tab.Pane eventKey="graph" style={{ height: '100%' }}>
- <CypherResultCytoscapeContainer refKey={refKey} />
+ <CypherResultCytoscapeContainer key={cytoscapeContainerKey} forwardedRef={chartAreaRef} refKey={refKey} isFullScreen={isFullScreen} />
</Tab.Pane>
<Tab.Pane eventKey="table">
diff --git a/frontend/src/components/frame/presentations/ServerConnectFrame.jsx b/frontend/src/components/frame/presentations/ServerConnectFrame.jsx
index a96d14f..9e37067 100644
--- a/frontend/src/components/frame/presentations/ServerConnectFrame.jsx
+++ b/frontend/src/components/frame/presentations/ServerConnectFrame.jsx
@@ -1,7 +1,7 @@
import React, {useState} from 'react'
import {Collapse} from 'react-bootstrap'
-const ServerConnectFrame = ({refKey, reqString, connectToAgensGraph, removeFrame, addAlert}) => {
+const ServerConnectFrame = ({refKey, reqString, connectToAgensGraph, removeFrame, addAlert, getMetaData}) => {
const [formData, setFormData] = useState({})
const [isExpanded, setIsExpanded] = useState(true)
@@ -66,7 +66,7 @@
</fieldset>
</form>
- <button className="btn btn-info" onClick={() => [connectToAgensGraph(formData), addAlert('NoticeServerConnected')]}>CONNECT</button>
+ <button className="btn btn-info" onClick={() => [connectToAgensGraph(formData), addAlert('NoticeServerConnected'), getMetaData()]}>CONNECT</button>
</div>
</div>
</div>
diff --git a/frontend/src/components/navigator/presentations/NavigatorItem.jsx b/frontend/src/components/navigator/presentations/NavigatorItem.jsx
index 637c3f4..6115895 100644
--- a/frontend/src/components/navigator/presentations/NavigatorItem.jsx
+++ b/frontend/src/components/navigator/presentations/NavigatorItem.jsx
@@ -5,7 +5,7 @@
return (
<li className="nav-item">
<a id={"side-"+ menuName + "-tab"} className={"nav-link" + (activeMenuName === menuName ? " active show " : "") } data-classname="fixed-left" data-toggle="pill"
- href="/#" role="tab" aria-controls={"side-" + menuName} aria-selected="true" onClick={() => onClick(activeMenuName, menuName)}><i
+ href="/#" role="tab" aria-controls={"side-" + menuName} aria-selected="true" onClick={() => onClick(menuName)}><i
className={"fa fa-" + fwCode}></i></a>
</li>
);
diff --git a/frontend/src/components/sidebar/containers/Sidebar.js b/frontend/src/components/sidebar/containers/Sidebar.js
index 78f52fb..9da84ef 100644
--- a/frontend/src/components/sidebar/containers/Sidebar.js
+++ b/frontend/src/components/sidebar/containers/Sidebar.js
@@ -4,7 +4,11 @@
const mapStateToProps = (state) => {
return {
- activeMenuName: state.navigator.activeMenu
+ activeMenuName: state.navigator.activeMenu,
+ isActive: state.navigator.isActive,
+ edges : state.metadata.edges,
+ nodes : state.metadata.nodes,
+ propertyKeys : state.metadata.propertyKeys
}
}
diff --git a/frontend/src/components/sidebar/presentations/Sidebar.jsx b/frontend/src/components/sidebar/presentations/Sidebar.jsx
index a9c645c..475e612 100644
--- a/frontend/src/components/sidebar/presentations/Sidebar.jsx
+++ b/frontend/src/components/sidebar/presentations/Sidebar.jsx
@@ -2,19 +2,18 @@
import SidebarHome from './SidebarHome'
import SidebarSetting from './SidebarSetting'
-
-const Sidebar = ({ activeMenuName, changeTheme }) => {
- return (
- <div id="sidebar" className={activeMenuName !== "" ? " active " : ""}>
- <div className="tab-content">
- <div className={"tab-pane fade" + (activeMenuName === "home" ? " active show " : "") } role="tabpanel" aria-labelledby="side-home-tab">
- <SidebarHome />
- </div>
- <div className={"tab-pane fade" + (activeMenuName === "setting" ? " active show " : "") } role="tabpanel" aria-labelledby="side-setting-tab">
- <SidebarSetting changeTheme={changeTheme} />
- </div>
+const Sidebar = ({ activeMenuName, isActive, changeTheme, edges, nodes, propertyKeys }) => {
+ return (
+ <div id="sidebar" className={isActive ? " active " : ""} style={{overflowY: 'scroll'}}>
+ <div className="tab-content">
+ <div className={"tab-pane fade" + (activeMenuName === "home" ? " active show " : "") } role="tabpanel" aria-labelledby="side-home-tab">
+ <SidebarHome edges={edges} nodes={nodes} propertyKeys={propertyKeys} />
+ </div>
+ <div className={"tab-pane fade" + (activeMenuName === "setting" ? " active show " : "") } role="tabpanel" aria-labelledby="side-setting-tab">
+ <SidebarSetting changeTheme={changeTheme} />
</div>
</div>
+ </div>
);
}
diff --git a/frontend/src/components/sidebar/presentations/SidebarHome.jsx b/frontend/src/components/sidebar/presentations/SidebarHome.jsx
index 1b23701..6cc4ea8 100644
--- a/frontend/src/components/sidebar/presentations/SidebarHome.jsx
+++ b/frontend/src/components/sidebar/presentations/SidebarHome.jsx
@@ -1,13 +1,150 @@
-import React from 'react'
+import React, { useReducer } from 'react'
+import { Badge } from 'react-bootstrap'
+import { Fragment } from 'react';
+import uuid from 'react-uuid';
-const SidebarHome = () => {
+const ColoredLine = () => (
+ <hr
+ style={{
+ color: '#B0B0B0',
+ backgroundColor: '#B0B0B0',
+ marginTop: 0,
+ height: 0.3
+ }}
+ />
+);
+
+const NodeList = ({nodes}) => {
+ let list;
+ if(nodes) {
+ list = nodes.map(item => (
+ <NodeItems
+ key={uuid()}
+ label={item.label}
+ cnt={item.cnt}
+ />
+ ));
+ return (
+ <div style={{display: 'flex', flexWrap: 'wrap'}}>
+ {list}
+ </div>
+ )
+ }
+ else {
+ return null;
+ }
+};
+
+const NodeItems = ({label, cnt}) => (
+ <Fragment>
+ <h5 style={{paddingRight: '0.3em'}}><span className="badge badge-pill badge-light">{label}({cnt})</span></h5>
+ </Fragment>
+);
+
+const EdgeList = ({edges}) => {
+ let list;
+ if(edges) {
+ list = edges.map(item => (
+ <EdgeItems
+ key={uuid()}
+ label={item.label}
+ cnt={item.cnt}
+ />
+ ));
+ return (
+ <div style={{display: 'flex', flexWrap: 'wrap'}}>
+ {list}
+ </div>
+ )
+ }
+ else {
+ return null;
+ }
+};
+
+const EdgeItems = ({label, cnt}) => (
+ <Fragment>
+ <h5 style={{paddingRight: '0.3em'}}><span className="badge badge-light">{label}({cnt})</span></h5>
+ </Fragment>
+);
+
+const PropertyList = ({propertyKeys}) => {
+ let list;
+ if(propertyKeys) {
+ list = propertyKeys.map(item => (
+ <PropertyItems
+ key={uuid()}
+ propertyName={item.key}
+ classNames={item.key_type === 'v' ? 'badge badge-dark' : 'badge badge-light'}
+ />
+ ));
+ return (
+ <div style={{display: 'flex', flexWrap: 'wrap'}}>
+ {list}
+ </div>
+ )
+ }
+ else {
+ return null;
+ }
+};
+
+const PropertyItems =({propertyName, classNames}) => (
+ <Fragment>
+ <h5 style={{paddingRight: '0.3em'}}><span className={classNames}>{propertyName}</span></h5>
+ </Fragment>
+);
+
+const ConnectedText =() => (
+ <div>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Username:</div><div className="col-sm-6"></div></h6>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Roles:</div><div className="col-sm-6"></div></h6>
+ </div>
+);
+
+const DBMSText =() => (
+ <div>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Version:</div><div className="col-sm-6"></div></h6>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Edition:</div><div className="col-sm-6"></div></h6>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Name:</div><div className="col-sm-6"></div></h6>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Databases:</div><div className="col-sm-6"></div></h6>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Information:</div><div className="col-sm-6"></div></h6>
+ <h6><div className="col-sm-6" style={{textAlign:'right'}}>Query List:</div><div className="col-sm-6"></div></h6>
+ </div>
+);
+
+const SidebarHome = ({edges, nodes, propertyKeys}) => {
return (
<div className="sidebar-home">
<div className="sidebar sidebar-header">
- <h3>HOME</h3>
+ <h3>Database Information</h3>
</div>
<div className="sidebar sidebar-body">
- Content....
+ <div className="form-group">
+ <label htmlFor="exampleFormControlSelect1"><b>Vertex Label</b></label>
+ <ColoredLine />
+ <NodeList nodes={nodes} />
+ </div>
+ <div className="form-group">
+ <label htmlFor="exampleFormControlSelect1"><b>Edge Label</b></label>
+ <ColoredLine />
+ <EdgeList edges={edges} />
+ </div>
+ <div className="form-group">
+ <label htmlFor="exampleFormControlSelect1"><b>Properties</b></label>
+ <ColoredLine />
+ <PropertyList propertyKeys={propertyKeys} />
+ </div>
+ <div className="form-group">
+ <label htmlFor="exampleFormControlSelect1"><b>Connected as</b></label>
+ <ColoredLine />
+ <ConnectedText />
+ </div>
+ <div className="form-group">
+ <label htmlFor="exampleFormControlSelect1"><b>DBMS</b></label>
+ <ColoredLine />
+ <DBMSText />
+ </div>
</div>
</div>
);
diff --git a/frontend/src/features/cypher/CypherSlice.js b/frontend/src/features/cypher/CypherSlice.js
index 6441ace..b053c3f 100644
--- a/frontend/src/features/cypher/CypherSlice.js
+++ b/frontend/src/features/cypher/CypherSlice.js
@@ -3,48 +3,65 @@
export const executeCypherQuery = createAsyncThunk(
'cypher/executeCypherQuery',
async (args) => {
- const response = await fetch('/api/v1/cypher',
- {
- method: 'POST',
- headers: {
+ const response = await fetch('/api/v1/cypher',
+ {
+ method: 'POST',
+ headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
- },
- body: JSON.stringify({cmd: args[1]})
- })
+ },
+ body: JSON.stringify({ cmd: args[1] })
+ })
if (response.ok) {
- const resData = {}
- resData['key'] = args[0];
- resData['query'] = args[1];
- const res = await response.json();
- resData['data'] = res['data']
- return resData;
+ const res = await response.json();
+ return Object.assign({key : args[0], query : args[1], ...res})
} else {
alert("Connection Error")
return {};
}
}
-
+
)
const CypherSlice = createSlice({
name: 'cypher',
initialState: {
- queryResult : {}
+ queryResult: {},
+ labels: { nodeLabels : {}, edgeLabels : {} }
},
reducers: {
+ setLabels: {
+ reducer: (state, action) => {
+ if (action.payload.elementType === 'node') {
+ if (state.labels.nodeLabels[action.payload.label] === undefined ) {state.labels.nodeLabels[action.payload.label] = action.payload.property}
+ else {Object.assign(state.labels.nodeLabels[action.payload.label], action.payload.property)}
+
+ } else if (action.payload.elementType === 'edge') {
+ if (state.labels.edgeLabels[action.payload.label] === undefined ) {state.labels.edgeLabels[action.payload.label] = action.payload.property}
+ else {Object.assign(state.labels.edgeLabels[action.payload.label], action.payload.property)}
+ }
+
+ },
+ prepare: (elementType, label, property) => {
+ return { payload: { elementType, label, property } }
+ }
+ }
},
extraReducers: {
[executeCypherQuery.pending]: (state, action) => {
},
[executeCypherQuery.fulfilled]: (state, action) => {
state.queryResult[action.payload.key] = {}
- state.queryResult[action.payload.key].response = action.payload
+ //state.queryResult[action.payload.key].response = action.payload
+ Object.assign(state.queryResult[action.payload.key], action.payload)
},
[executeCypherQuery.rejectd]: (state, action) => {
}
}
})
+
+export const { setLabels } = CypherSlice.actions
+
export default CypherSlice.reducer
\ No newline at end of file
diff --git a/frontend/src/features/cypher/CypherUtil.js b/frontend/src/features/cypher/CypherUtil.js
new file mode 100644
index 0000000..8cee0a1
--- /dev/null
+++ b/frontend/src/features/cypher/CypherUtil.js
@@ -0,0 +1,285 @@
+
+
+
+export const labelColors = [
+ { color: '#604A0E', borderColor: '#423204', fontColor: '#FFF', labels: new Set([]), index: 0 },
+ { color: '#C990C0', borderColor: '#B261A5', fontColor: '#FFF', labels: new Set([]), index: 1 },
+ { color: '#F79767', borderColor: '#F36924', fontColor: '#FFF', labels: new Set([]), index: 2 },
+ { color: '#57C7E3', borderColor: '#23B3D7', fontColor: '#2A2C34', labels: new Set([]), index: 3 },
+ { color: '#F16667', borderColor: '#EB2728', fontColor: '#FFF', labels: new Set([]), index: 4 },
+ { color: '#D9C8AE', borderColor: '#C0A378', fontColor: '#2A2C34', labels: new Set([]), index: 5 },
+ { color: '#8DCC93', borderColor: '#5DB665', fontColor: '#2A2C34', labels: new Set([]), index: 6 },
+ { color: '#ECB5C9', borderColor: '#DA7298', fontColor: '#2A2C34', labels: new Set([]), index: 7 },
+ { color: '#498EDA', borderColor: '#2870C2', fontColor: '#FFF', labels: new Set([]), index: 8 },
+ { color: '#FFC454', borderColor: '#D7A013', fontColor: '#2A2C34', labels: new Set([]), index: 9 },
+ { color: '#DA7194', borderColor: '#CC3C6C', fontColor: '#FFF', labels: new Set([]), index: 10 },
+ { color: '#569480', borderColor: '#447666', fontColor: '#FFF', labels: new Set([]), index: 11 }
+]
+
+export const nodeLabelSizes = [
+ { size: 11, labels: new Set([]), index: 0 },
+ { size: 33, labels: new Set([]), index: 0 },
+ { size: 55, labels: new Set([]), index: 0 },
+ { size: 77, labels: new Set([]), index: 0 },
+ { size: 99, labels: new Set([]), index: 0 }
+]
+
+
+export const edgeLabelSizes = [
+ { size: 1, labels: new Set([]), index: 0 },
+ { size: 6, labels: new Set([]), index: 0 },
+ { size: 11, labels: new Set([]), index: 0 },
+ { size: 16, labels: new Set([]), index: 0 },
+ { size: 21, labels: new Set([]), index: 0 }
+]
+
+export let nodelabelCaptions = {}
+export let edgelabelCaptions = {}
+
+
+const getCaption = (valType, val) => {
+ if (valType === 'node' && nodelabelCaptions.hasOwnProperty(val.label)) {
+ return nodelabelCaptions[val.label]
+ } else if (valType === 'edge' && nodelabelCaptions.hasOwnProperty(val.label)) {
+ return edgelabelCaptions[val.label]
+
+ }
+
+ let caption = valType === 'node' ? 'gid' : 'label'
+ const properties = val.properties
+ if (properties !== undefined) {
+ if (properties.hasOwnProperty('name')) {caption = 'name'}
+ else if (properties.hasOwnProperty('id')) {caption = 'id'}
+ }
+
+ return caption
+}
+
+const getColor = (labelName) => {
+ let selectedColor = {}
+ labelColors.forEach((labelColor) => {
+ if (labelColor.labels.has(labelName)) {
+ selectedColor = { color: labelColor.color, borderColor : labelColor.borderColor, fontColor : labelColor.fontColor}
+ }
+ })
+
+ if (Object.keys(selectedColor).length === 0) {
+ const randomIndex = Math.floor(Math.random() * (11 - 0 + 1)) + 0
+ labelColors[randomIndex].labels.add(labelName)
+ selectedColor = { color: labelColors[randomIndex].color, borderColor : labelColors[randomIndex].borderColor, fontColor : labelColors[randomIndex].fontColor}
+ }
+ return selectedColor
+}
+
+const getNodeSize = (labelName) => {
+ let selectedSize = 0
+
+ nodeLabelSizes.forEach((labelSize) => {
+ if (labelSize.labels.has(labelName)) {
+ selectedSize = labelSize.size
+ }
+ })
+
+ if (selectedSize === 0 ) {
+ nodeLabelSizes[2].labels.add(labelName)
+ selectedSize = nodeLabelSizes[2].size
+ }
+
+ return selectedSize
+}
+
+const getEdgeSize = (labelName) => {
+ let selectedSize = 0
+
+ edgeLabelSizes.forEach((labelSize) => {
+ if (labelSize.labels.has(labelName)) {
+ selectedSize = labelSize.size
+ }
+ })
+
+ if (selectedSize === 0 ) {
+ edgeLabelSizes[0].labels.add(labelName)
+ selectedSize = edgeLabelSizes[0].size
+ }
+
+ return selectedSize
+}
+
+const sortByKey = (data) => {
+ const sorted = {};
+ if (data === undefined) {
+ return sorted
+ }
+ Object.keys(data).sort().forEach(function(key) {
+ sorted[key] = data[key];
+ });
+ return sorted;
+}
+
+export const updateLabelColor = (labelName, newLabelColor) => {
+ labelColors.forEach((labelColor) => {
+ if (labelColor.labels.has(labelName)) {
+ labelColor.labels.delete(labelName)
+ }
+
+ if (labelColor.color === newLabelColor.color) {
+ labelColor.labels.add(labelName)
+ }
+ })
+}
+
+export const updateNodeLabelSize = (labelName, newLabelSize) => {
+ nodeLabelSizes.forEach((labelSize) => {
+ if (labelSize.labels.has(labelName)) {
+ labelSize.labels.delete(labelName)
+ }
+
+ if (labelSize.size === newLabelSize) {
+ labelSize.labels.add(labelName)
+ }
+ })
+}
+
+
+export const updateEdgeLabelSize = (labelName, newLabelSize) => {
+ edgeLabelSizes.forEach((labelSize) => {
+ if (labelSize.labels.has(labelName)) {
+ labelSize.labels.delete(labelName)
+ }
+
+ if (labelSize.size === newLabelSize) {
+ labelSize.labels.add(labelName)
+ }
+ })
+}
+
+
+export const updateLabelCaption = (labelType, labelName, newLabelCaption) => {
+ if (labelType === 'node') {
+ nodelabelCaptions[labelName] = newLabelCaption
+ } else {
+ edgelabelCaptions[labelName] = newLabelCaption
+ }
+}
+
+export const reGenerateCytoscapeElements = (data, labels) => {
+ let nodes = []
+ let edges = []
+ let nodeLegend = {}
+ let edgeLegend = {}
+
+ if (data) {
+ data['rows'].forEach((row, index) => {
+ for (const [alias, val] of Object.entries(row)) {
+ let labelName = val['label']
+ if (val['start'] && val['end']) {
+ if (!edgeLegend.hasOwnProperty(labelName)) { edgeLegend[labelName] = { color: '#8C8C8C', borderColor : '#8C8C8C', fontColor:'#2A2C34', size: getEdgeSize(labelName), caption: getCaption('edge', val) } }
+ if (!edgelabelCaptions.hasOwnProperty(labelName)) {edgelabelCaptions[labelName] = 'label'}
+ edges.push(
+ {
+ group: 'edges'
+ , data: {
+ id: val.id
+ , source: val.start
+ , target: val.end
+ , label: val.label
+ , backgroundColor: edgeLegend[labelName].color
+ , borderColor: edgeLegend[labelName].borderColor
+ , fontColor: edgeLegend[labelName].fontColor
+ , size: edgeLegend[labelName].size
+ , properties: val.properties
+ , caption: edgeLegend[labelName].caption
+ }
+ , alias: alias
+ , classes: ['node']
+ }
+ )
+ } else {
+ if (!nodeLegend.hasOwnProperty(labelName)) { nodeLegend[labelName] = Object.assign({size: getNodeSize(labelName), caption: getCaption('node', val)}, getColor(labelName)) }
+ if (!nodelabelCaptions.hasOwnProperty(labelName)) {nodelabelCaptions[labelName] = 'gid'}
+ nodes.push(
+ {
+ group: 'nodes'
+ , data: {
+ id: val.id
+ , label: val.label
+ , backgroundColor: nodeLegend[labelName].color
+ , borderColor: nodeLegend[labelName].borderColor
+ , fontColor: nodeLegend[labelName].fontColor
+ , size: nodeLegend[labelName].size
+ , properties: val.properties
+ , caption: nodeLegend[labelName].caption
+ }
+ , alias: alias
+ , classes: ['node']
+ }
+ )
+ }
+ }
+ });
+ }
+
+ return { legend: { nodeLegend: sortByKey(nodeLegend), edgeLegend: sortByKey(edgeLegend) }, elements: { nodes: nodes, edges: edges } }
+}
+
+export const generateCytoscapeElement = (data) => {
+ let nodes = []
+ let edges = []
+ let nodeLegend = {}
+ let edgeLegend = {}
+ console.log("data>> " , data)
+
+ if (data) {
+ data['rows'].forEach((row, index) => {
+ for (const [alias, val] of Object.entries(row)) {
+ let labelName = val['label']
+ if (val['start'] && val['end']) {
+ if (!edgeLegend.hasOwnProperty(labelName)) { edgeLegend[labelName] = { color: '#8C8C8C', borderColor : '#8C8C8C', fontColor:'#2A2C34', size: getEdgeSize(labelName), caption: getCaption('edge', val) } }
+ if (!edgelabelCaptions.hasOwnProperty(labelName)) {edgelabelCaptions[labelName] = 'label'}
+ edges.push(
+ {
+ group: 'edges'
+ , data: {
+ id: val.id
+ , source: val.start
+ , target: val.end
+ , label: val.label
+ , backgroundColor: edgeLegend[labelName].color
+ , borderColor: edgeLegend[labelName].borderColor
+ , fontColor: edgeLegend[labelName].fontColor
+ , size: edgeLegend[labelName].size
+ , properties: val.properties
+ , caption: edgeLegend[labelName].caption
+ }
+ , alias: alias
+ , classes: ['node']
+ }
+ )
+ } else {
+ if (!nodeLegend.hasOwnProperty(labelName)) { nodeLegend[labelName] = Object.assign({size: getNodeSize(labelName), caption: getCaption('node', val)}, getColor(labelName)) }
+ if (!nodelabelCaptions.hasOwnProperty(labelName)) {nodelabelCaptions[labelName] = 'gid'}
+ nodes.push(
+ {
+ group: 'nodes'
+ , data: {
+ id: val.id
+ , label: val.label
+ , backgroundColor: nodeLegend[labelName].color
+ , borderColor: nodeLegend[labelName].borderColor
+ , fontColor: nodeLegend[labelName].fontColor
+ , size: nodeLegend[labelName].size
+ , properties: val.properties
+ , caption: nodeLegend[labelName].caption
+ }
+ , alias: alias
+ , classes: ['node']
+ }
+ )
+ }
+ }
+ });
+ }
+
+ return { legend: { nodeLegend: sortByKey(nodeLegend), edgeLegend: sortByKey(edgeLegend) }, elements: { nodes: nodes, edges: edges } }
+
+}
\ No newline at end of file
diff --git a/frontend/src/features/database/DatabaseSlice.js b/frontend/src/features/database/DatabaseSlice.js
index a401ab3..38164cf 100644
--- a/frontend/src/features/database/DatabaseSlice.js
+++ b/frontend/src/features/database/DatabaseSlice.js
@@ -3,58 +3,87 @@
export const connectToAgensGraph = createAsyncThunk(
'database/connectToAgensGraph',
async (formData) => {
- const response = await fetch('/api/v1/db/connect',
- {method: 'POST',
- headers: {
+ const response = await fetch('/api/v1/db/connect',
+ {
+ method: 'POST',
+ headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
- },
- body: JSON.stringify(formData)
- })
- const res = await response.json();
- return res.data
+ },
+ body: JSON.stringify(formData)
+ })
+ return await response.json();
}
)
export const disconnectToAgensGraph = createAsyncThunk(
'database/disconnectToAgensGraph',
async () => {
- const response = await fetch('/api/v1/db/disconnect')
- return response.data
+ return await fetch('/api/v1/db/disconnect')
+ }
+)
+
+export const getConnectionStatus = createAsyncThunk(
+ 'database/getConnectionStatus',
+ async () => {
+ const response = await fetch('/api/v1/db')
+ return await response.json();
}
)
const DatabaseSlice = createSlice({
name: 'database',
initialState: {
- host: '',
- port: '',
- user: '',
- password: '',
- database: '',
- graph: '',
- status: 'disconnected'
+ status: 'init'
},
reducers: {
},
extraReducers: {
[connectToAgensGraph.fulfilled]: (state, action) => {
- state.host = action.payload.host !== '' ? action.payload.host : state.host
- state.port = action.payload.port !== '' ? action.payload.port : state.port
- state.user = action.payload.user !== '' ? action.payload.user : state.user
- state.password = action.payload.password !== '' ? action.payload.password : state.password
- state.database = action.payload.database !== '' ? action.payload.database : state.database
- state.graph = action.payload.graph !== '' ? action.payload.graph : state.graph
- state.status = 'connected'
- },
+ return {
+ host: action.payload.host
+ , port: action.payload.port
+ , user: action.payload.user
+ , password: action.payload.password
+ , database: action.payload.database
+ , graph: action.payload.graph
+ , status: 'connected'
+ }
+ },
[disconnectToAgensGraph.fulfilled]: (state, action) => {
- state.host = ''
- state.port = ''
- state.user = ''
- state.password = ''
- state.database = ''
- state.graph = ''
- state.status = 'disconnected'
+ return {
+ host: ''
+ , port: ''
+ , user: ''
+ , password: ''
+ , database: ''
+ , graph: ''
+ , status: 'disconnected'
+ }
+ },
+ [getConnectionStatus.fulfilled]: (state, action) => {
+ if (action.payload) {
+ return {
+ host: action.payload.host
+ , port: action.payload.port
+ , user: action.payload.user
+ , password: action.payload.password
+ , database: action.payload.database
+ , graph: action.payload.graph
+ , status: 'connected'
+ }
+ } else {
+ return {
+ host: ''
+ , port: ''
+ , user: ''
+ , password: ''
+ , database: ''
+ , graph: ''
+ , status: 'disconnected'
+ }
+ }
+
}
}
})
diff --git a/frontend/src/features/database/MetadataSlice.js b/frontend/src/features/database/MetadataSlice.js
new file mode 100644
index 0000000..3ce1970
--- /dev/null
+++ b/frontend/src/features/database/MetadataSlice.js
@@ -0,0 +1,38 @@
+import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
+
+export const getMetaData = createAsyncThunk(
+ 'database/getMetaData',
+ async () => {
+ const response = await fetch('/api/v1/db/meta')
+ const res = await response.json();
+ return res
+ }
+)
+
+const MetadataSlice = createSlice({
+ name: 'metadata',
+ initialState: {
+ status: 'init'
+ },
+ reducers: {
+ },
+ extraReducers: {
+ [getMetaData.fulfilled]: (state, action) => {
+ if (action.payload) {
+ return {
+ edges: action.payload.edges,
+ nodes: action.payload.nodes,
+ propertyKeys: action.payload.propertyKeys
+ }
+ } else {
+ return {
+ edges: [],
+ nodes: [],
+ propertyKeys: []
+ }
+ }
+ }
+ }
+})
+
+export default MetadataSlice.reducer
\ No newline at end of file
diff --git a/frontend/src/features/frame/FrameSlice.js b/frontend/src/features/frame/FrameSlice.js
index 4c44a06..b8187f3 100644
--- a/frontend/src/features/frame/FrameSlice.js
+++ b/frontend/src/features/frame/FrameSlice.js
@@ -3,27 +3,28 @@
const FrameSlice = createSlice({
name: 'frames',
- initialState: [{ frameName: 'ServerConnect', frameProps: { key: uuid(), reqString: ':server connect' } }],
+ initialState: [],
reducers: {
addFrame: {
reducer: (state, action) => {
const reqString = action.payload.reqString.trim().toLowerCase()
-
+ let refKey = action.payload.refKey ? action.payload.refKey : uuid()
if (reqString === ':server status') {
- state.unshift({ frameName: 'ServerStatus', frameProps: { key: uuid(), reqString: reqString } })
+ state.unshift({ frameName: 'ServerStatus', frameProps: { key: refKey, reqString: reqString } })
} else if (reqString === ':server connect') {
- state.unshift({ frameName: 'ServerConnect', frameProps: { key: uuid(), reqString: reqString } })
+ state.unshift({ frameName: 'ServerConnect', frameProps: { key: refKey, reqString: reqString } })
} else if (reqString === ':server disconnect') {
- state.unshift({ frameName: 'ServerDisconnect', frameProps: { key: uuid(), reqString: reqString } })
- } else if (reqString.startsWith('match')) {
- state.unshift({ frameName: 'CypherResultFrame', frameProps: { key: uuid(), reqString: reqString } })
+ state.unshift({ frameName: 'ServerDisconnect', frameProps: { key: refKey, reqString: reqString } })
+ } else if (reqString.match("(match|create).*")) {
+ state.unshift({ frameName: 'CypherResultFrame', frameProps: { key: refKey, reqString: reqString } })
} else {
alert("Can't understand your command")
return;
}
},
- prepare: (reqString) => {
- return { payload: { reqString } }
+ prepare: (reqString, refKey) => {
+ console.log("reqString, refKey >> ", reqString, refKey)
+ return { payload: { reqString, refKey } }
}
},
removeFrame: {
diff --git a/frontend/src/features/menu/MenuSlice.js b/frontend/src/features/menu/MenuSlice.js
index b42614c..10b52da 100644
--- a/frontend/src/features/menu/MenuSlice.js
+++ b/frontend/src/features/menu/MenuSlice.js
@@ -4,18 +4,22 @@
name: 'navigator',
initialState: {
menuList: [['home', 'home'], ['setting', 'cog']],
- activeMenu: ''
+ activeMenu: 'init',
+ isActive: false
},
reducers: {
toggleMenu: {
reducer: (state, action) => {
+ let isActive = true
if (state.activeMenu === action.payload.selectedMenuName) {
action.payload.selectedMenuName = ''
+ isActive = false
}
state.activeMenu = action.payload.selectedMenuName
+ state.isActive = isActive
},
- prepare: (activeMenuName, selectedMenuName) => {
- return { payload : {activeMenuName, selectedMenuName}}
+ prepare: (selectedMenuName) => {
+ return { payload : {selectedMenuName}}
}
}
}
diff --git a/package.json b/package.json
index a1337b6..dc4b940 100644
--- a/package.json
+++ b/package.json
@@ -3,12 +3,13 @@
"version": "0.0.1",
"private": true,
"scripts": {
- "setup-main": "npm install",
+ "setup": "npm install && npm-run-all setup-front setup-backend",
"setup-front": "cd frontend && npm install",
"setup-backend": "cd backend && npm install",
- "setup": "npm-run-all --parallel setup-main setup-front setup-backend",
"front": "cd frontend && npm run start",
+ "build-front": "cd frontend && yarn build",
"backend": "cd backend && npm run start",
+ "deploy": "npm-run-all build-front backend",
"start": "npm-run-all --parallel backend front"
},
"dependencies": {