-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathconn.go
149 lines (127 loc) · 2.98 KB
/
conn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Package cq provides a database/sql implementation for Neo4j's Cypher query language.
package cq
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/url"
)
type cypherDriver struct{}
func (d *cypherDriver) Open(name string) (driver.Conn, error) {
return Open(name)
}
func init() {
sql.Register("neo4j-cypher", &cypherDriver{})
}
var (
cqVersion = "0.1.0"
tr = &http.Transport{
DisableKeepAlives: true,
}
client = &http.Client{}
)
type conn struct {
baseURL string
userInfo *url.Userinfo
scheme string
cypherURL string
transactionURL string
transaction *cypherTransaction
}
type neo4jBase struct {
Data string `json:"data"`
}
type neo4jData struct {
Cypher string `json:"cypher"`
Transaction string `json:"transaction,omitempty"`
Version string `json:"neo4j_version"`
}
func setDefaultHeaders(req *http.Request) {
req.Header.Set("X-Stream", "true")
req.Header.Set("User-Agent", cqVersion)
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
}
// Open queries the base URL given to it for the Cypher
// and (optional) Transaction endpoints.
// It returns a connection handle, or an error if something went wrong.
func Open(baseURL string) (driver.Conn, error) {
// TODO
// cache the results of this lookup
// add support for multiple hosts (cluster)
c := &conn{}
base, err := url.Parse(baseURL)
if err != nil {
return nil, err
}
c.userInfo = base.User
c.scheme = base.Scheme
neoBase, err := getNeoBase(baseURL)
if err != nil {
return nil, err
}
dataURL, err := url.Parse(neoBase.Data)
if err != nil {
return nil, err
}
dataURL.User = base.User
dataURL.Scheme = base.Scheme
neoData, err := getNeoData(dataURL.String())
if err != nil {
return nil, err
}
cypherURL, err := url.Parse(neoData.Cypher)
cypherURL.User = base.User
cypherURL.Scheme = base.Scheme
c.cypherURL = cypherURL.String()
transURL, err := url.Parse(neoData.Transaction)
transURL.User = base.User
transURL.Scheme = base.Scheme
c.transactionURL = transURL.String()
return c, nil
}
func getNeoBase(url string) (*neo4jBase, error) {
res, err := http.Get(url)
if err != nil {
return nil, err
}
neoBase := neo4jBase{}
err = json.NewDecoder(res.Body).Decode(&neoBase)
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
if err != nil {
return nil, err
}
return &neoBase, nil
}
func getNeoData(url string) (*neo4jData, error) {
res, err := http.Get(url)
if err != nil {
return nil, err
}
neoData := neo4jData{}
err = json.NewDecoder(res.Body).Decode(&neoData)
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
if err != nil {
return nil, err
}
return &neoData, nil
}
func (c *conn) Close() error {
// TODO check if in transaction and rollback
return nil
}
func (c *conn) Prepare(query string) (driver.Stmt, error) {
if c.cypherURL == "" {
return nil, ErrNotConnected
}
stmt := &cypherStmt{
c: c,
query: &query,
}
return stmt, nil
}