diff --git a/desktop/panel/database.test.js b/desktop/panel/database.test.js index 502cbcdf5..e2f73fa95 100644 --- a/desktop/panel/database.test.js +++ b/desktop/panel/database.test.js @@ -725,6 +725,47 @@ for (const subprocess of RUNNERS) { }); }); + describe('basic neo4j tests', () => { + test('basic test', async () => { + if (process.platform !== 'linux') { + return; + } + + const connectors = [ + new DatabaseConnectorInfo({ + type: 'neo4j', + database: '', + username: 'neo4j', + password_encrypt: new Encrypt('password'), + }), + ]; + const dp = new DatabasePanelInfo(); + dp.database.connectorId = connectors[0].id; + dp.content = 'MATCH (n) RETURN count(n) AS count'; + + let finished = false; + const panels = [dp]; + await withSavedPanels( + panels, + async (project) => { + const panelValueBuffer = fs.readFileSync( + getProjectResultsFile(project.projectName) + dp.id + ); + + const v = JSON.parse(panelValueBuffer.toString()); + expect(v).toStrictEqual([{ count: 1 }]); + + finished = true; + }, + { evalPanels: true, connectors, subprocessName: subprocess } + ); + + if (!finished) { + throw new Error('Callback did not finish'); + } + }); + }); + describe('basic snowflake tests', () => { test('basic test', async () => { if (process.platform !== 'linux') { diff --git a/runner/database.go b/runner/database.go index ae612cd57..de54c5181 100644 --- a/runner/database.go +++ b/runner/database.go @@ -67,6 +67,7 @@ var defaultPorts = map[DatabaseConnectorInfoType]string{ TimescaleDatabase: "5432", YugabyteDatabase: "5433", QuestDatabase: "8812", + Neo4jDatabase: "7687", } type urlParts struct { @@ -224,8 +225,35 @@ func (ec EvalContext) getConnectionString(dbInfo DatabaseConnectorInfoDatabase) case SQLiteDatabase: // defined in database_sqlite.go, includes regexp support return "sqlite3_extended", resolvePath(u.database), nil - } + case Neo4jDatabase: + addr, err := url.Parse(u.address) + if err != nil { + return "", "", err + } + // prevent localhost from registerring as a scheme in localhost:7687 + if addr.Opaque != "" { + addr, err = url.Parse("//" + u.address) + if err != nil { + return "", "", err + } + } + + if addr.Scheme == "" { + addr.Scheme = Neo4jDatabase + } + + if addr.Port() == "" { + addr.Host += ":" + "7687" + } + + _, _, err = net.SplitHostPort(addr.Host) + if err != nil { + return "", "", err + } + + return "neo4j", addr.String(), nil + } return "", "", nil } @@ -387,6 +415,8 @@ func (ec EvalContext) EvalDatabasePanel( return ec.evalGoogleSheets(panel, dbInfo, w) case AirtableDatabase: return ec.evalAirtable(panel, dbInfo, w) + case Neo4jDatabase: + return ec.evalNeo4j(panel, dbInfo, server, w) } mangleInsert := defaultMangleInsert diff --git a/runner/database_neo4j.go b/runner/database_neo4j.go new file mode 100644 index 000000000..e33d09660 --- /dev/null +++ b/runner/database_neo4j.go @@ -0,0 +1,58 @@ +package runner + +import ( + "io" + + jsonutil "github.com/multiprocessio/go-json" + "github.com/neo4j/neo4j-go-driver/v4/neo4j" +) + +func (ec EvalContext) evalNeo4j(panel *PanelInfo, dbInfo DatabaseConnectorInfoDatabase, server *ServerInfo, w io.Writer) error { + _, conn, err := ec.getConnectionString(dbInfo) + if err != nil { + return err + } + + password, err := ec.decrypt(&dbInfo.Password) + if err != nil { + return err + } + + driver, err := neo4j.NewDriver(conn, neo4j.BasicAuth(dbInfo.Username, password, "")) + if err != nil { + return err + } + defer driver.Close() + + sess := driver.NewSession(neo4j.SessionConfig{}) + + result, err := sess.Run(panel.Content, nil) + if err != nil { + return err + } + + return withJSONArrayOutWriterFile(w, func(w *jsonutil.StreamEncoder) error { + records, err := result.Collect() + if err != nil { + return nil + } + + row := map[string]any{} + for _, record := range records { + for _, key := range record.Keys { + value, ok := record.Get(key) + if ok { + row[key] = value + } else { + row[key] = nil + } + } + + if err := w.EncodeRow(row); err != nil { + return err + } + } + + return result.Err() + }) +} diff --git a/runner/database_test.go b/runner/database_test.go index f78622b40..fe8434ca7 100644 --- a/runner/database_test.go +++ b/runner/database_test.go @@ -115,6 +115,24 @@ func Test_getConnectionString(t *testing.T) { "9001", "", }, + { + DatabaseConnectorInfoDatabase{Type: "neo4j", Password: Encrypt{Encrypted: false, Value: ""}, Database: "test", Address: "localhost:7687"}, + "neo4j", + "neo4j://localhost:7687", + nil, + "localhost", + "7687", + "", + }, + { + DatabaseConnectorInfoDatabase{Type: "neo4j", Password: Encrypt{Encrypted: false, Value: ""}, Database: "test", Address: "neo4j+s://localhost"}, + "neo4j", + "neo4j+s://localhost:7687", + nil, + "localhost", + "7687", + "", + }, } ec, cleanup := makeTestEvalContext() @@ -126,7 +144,7 @@ func Test_getConnectionString(t *testing.T) { assert.Equal(t, test.expConnStr, connStr) assert.Equal(t, test.expErr, err) - if test.expVendor == "sqlite" || test.expVendor == "snowflake" { + if test.expVendor == "sqlite" || test.expVendor == "snowflake" || test.expVendor == "neo4j" { continue } diff --git a/runner/go.mod b/runner/go.mod index 4bc90e923..651d06546 100644 --- a/runner/go.mod +++ b/runner/go.mod @@ -79,6 +79,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/neo4j/neo4j-go-driver/v4 v4.4.1 // indirect github.com/paulmach/orb v0.4.0 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect diff --git a/runner/go.sum b/runner/go.sum index 8d2641ed8..eab3e821d 100644 --- a/runner/go.sum +++ b/runner/go.sum @@ -205,6 +205,9 @@ github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzP github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= @@ -236,6 +239,7 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/goccy/go-json v0.9.6 h1:5/4CtRQdtsX0sal8fdVhTaiMN01Ri8BExZZ8iRmHQ6E= github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c= @@ -345,6 +349,7 @@ github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMW github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/influxdata/influxdb-client-go/v2 v2.8.1 h1:DahMl2iYvr9hEtGZcV4Ud8Z7AQ8cQVbPlX6gEOBBCrE= @@ -435,7 +440,18 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncw/swift v1.0.52/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/neo4j/neo4j-go-driver/v4 v4.4.1 h1:krjSEqon9w4q+5OhWhx2C2jtZUN7w4fyMVH0YWpNpyI= +github.com/neo4j/neo4j-go-driver/v4 v4.4.1/go.mod h1:NexOfrm4c317FVjekrhVV8pHBXgtMG5P6GeweJWCyo4= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/paulmach/orb v0.4.0 h1:ilp1MQjRapLJ1+qcays1nZpe0mvkCY+b8JU/qBKRZ1A= github.com/paulmach/orb v0.4.0/go.mod h1:FkcWtplUAIVqAuhAOV2d3rpbnQyliDOjOcLW9dUrfdU= github.com/paulmach/protoscan v0.2.1-0.20210522164731-4e53c6875432/go.mod h1:2sV+uZ/oQh66m4XJVZm5iqUZ62BN88Ex1E+TTS0nLzI= @@ -613,6 +629,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -636,6 +653,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -648,6 +666,7 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -691,6 +710,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -703,9 +723,12 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -733,6 +756,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -831,6 +855,7 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= @@ -1025,6 +1050,7 @@ gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= @@ -1032,6 +1058,7 @@ gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eR gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/runner/state.go b/runner/state.go index 8b1525b0a..f115292dd 100644 --- a/runner/state.go +++ b/runner/state.go @@ -284,6 +284,7 @@ const ( AthenaDatabase = "athena" AirtableDatabase = "airtable" GoogleSheetsDatabase = "google-sheets" + Neo4jDatabase = "neo4j" ) type DatabaseConnectorInfoDatabase struct { diff --git a/scripts/ci/prepare_linux_integration_test_setup_only.sh b/scripts/ci/prepare_linux_integration_test_setup_only.sh index 84cf6e8e3..d81d734a1 100755 --- a/scripts/ci/prepare_linux_integration_test_setup_only.sh +++ b/scripts/ci/prepare_linux_integration_test_setup_only.sh @@ -109,6 +109,9 @@ docker run -d -p 8086:8086 -e "DOCKER_INFLUXDB_INIT_MODE=setup" -e "DOCKER_INFLU # Start up influx (1 for influxql) docker run -d -p 8087:8086 -e "INFLUXDB_HTTP_AUTH_ENABLED=true" -e "INFLUXDB_ADMIN_USER=test" -e "INFLUXDB_ADMIN_PASSWORD=testtest" influxdb:1.7 +# Start up neo4j +neo4j="$(docker run -d -p 7687:7687 -e "NEO4J_AUTH=neo4j/password" neo4j)" + # Start up mongodb and install mongosh (shell) #docker run -d -e "MONGO_INITDB_ROOT_USERNAME=test" -e "MONGO_INITDB_DATABASE=test" -e "MONGO_INITDB_ROOT_PASSWORD=test" -p 27017:27017 mongo:5 #curl -LO https://github.com/mongodb-js/mongosh/releases/download/v1.1.9/mongodb-mongosh_1.1.9_amd64.deb @@ -161,6 +164,9 @@ docker exec "$scyllacontainer" cqlsh -u cassandra -p cassandra \ docker exec "$scyllacontainer" cqlsh -u cassandra -p cassandra \ -e "CREATE ROLE test WITH PASSWORD = 'test' AND LOGIN = true AND SUPERUSER = true;" +# Configure neo4j +docker exec "$neo4j" bin/cypher-shell -u neo4j -p password "CREATE (u:User { name : 'test' });" + # Load Mongodb documents #for t in $(ls testdata/documents/*.json); do # mongosh "mongodb://test:test@localhost:27017" --eval "db.test.insertOne($(cat $t))" diff --git a/shared/state.ts b/shared/state.ts index 5b16a5d14..191293b3d 100644 --- a/shared/state.ts +++ b/shared/state.ts @@ -201,7 +201,8 @@ export type DatabaseConnectorInfoType = | 'splunk' | 'prometheus' | 'influx' - | 'influx-flux'; + | 'influx-flux' + | 'neo4j'; export class DatabaseConnectorInfo extends ConnectorInfo { database: { diff --git a/ui/connectors/Neo4jDetails.tsx b/ui/connectors/Neo4jDetails.tsx new file mode 100644 index 000000000..4fb55c3dd --- /dev/null +++ b/ui/connectors/Neo4jDetails.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { DatabaseConnectorInfo, ServerInfo } from '../../shared/state'; +import { FormGroup } from '../components/FormGroup'; +import { ServerPicker } from '../components/ServerPicker'; +import { Host } from './Host'; +import { Password } from './Password'; +import { Username } from './Username'; + +export function Neo4jDetails(props: { + connector: DatabaseConnectorInfo; + updateConnector: (c: DatabaseConnectorInfo) => void; + servers: Array; +}) { + const { servers, connector, updateConnector } = props; + + return ( + + + + + + + { + connector.serverId = serverId; + updateConnector(connector); + }} + /> + + ); +} diff --git a/ui/connectors/index.ts b/ui/connectors/index.ts index b39e56aa3..4083c7b39 100644 --- a/ui/connectors/index.ts +++ b/ui/connectors/index.ts @@ -6,6 +6,7 @@ import { CassandraDetails } from './CassandraDetails'; import { ElasticsearchDetails } from './ElasticsearchDetails'; import { FluxDetails } from './FluxDetails'; import { GenericDetails, GenericNoDatabaseDetails } from './GenericDetails'; +import { Neo4jDetails } from './Neo4jDetails'; import { SnowflakeDetails } from './SnowflakeDetails'; import { SQLiteDetails } from './SQLiteDetails'; @@ -141,6 +142,11 @@ export const VENDORS: { id: 'google-sheets', details: BigQueryDetails, }, + neo4j: { + name: 'Neo4j', + id: 'neo4j', + details: Neo4jDetails, + }, }; export const VENDOR_GROUPS: Array<{ @@ -181,6 +187,10 @@ export const VENDOR_GROUPS: Array<{ 'yugabyte', ], }, + { + group: 'Graph', + vendors: ['neo4j'], + }, { group: 'Metrics', vendors: ['prometheus', 'influx', 'influx-flux'],