Skip to content

Commit e4f6afe

Browse files
committed
Improve error handling for Maria cursor + isolate query handler.
1 parent e1c162c commit e4f6afe

File tree

3 files changed

+118
-105
lines changed

3 files changed

+118
-105
lines changed

internal/database/mariadb.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ func (mc *MariaDBCursor) Read(ctx context.Context, doc interface{}) error {
745745
case knowledge.NodeExprType:
746746
items, err := q.Get(3)
747747
if err != nil {
748-
return nil
748+
return fmt.Errorf("Unable to get 3 items to build a node: %v", err)
749749
}
750750

751751
asset := knowledge.Asset{
@@ -761,7 +761,7 @@ func (mc *MariaDBCursor) Read(ctx context.Context, doc interface{}) error {
761761
case knowledge.EdgeExprType:
762762
items, err := q.Get(3)
763763
if err != nil {
764-
return nil
764+
return fmt.Errorf("Unable to get 3 items to build an edge: %v", err)
765765
}
766766

767767
r := knowledge.RelationWithID{
@@ -773,7 +773,7 @@ func (mc *MariaDBCursor) Read(ctx context.Context, doc interface{}) error {
773773
case knowledge.PropertyExprType:
774774
items, err := q.Get(1)
775775
if err != nil {
776-
return nil
776+
return fmt.Errorf("Unable to get 1 property item: %v", err)
777777
}
778778
output[i] = items[0]
779779
}

internal/handlers/handler_query.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package handlers
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"time"
9+
10+
"github.com/clems4ever/go-graphkb/internal/history"
11+
"github.com/clems4ever/go-graphkb/internal/knowledge"
12+
)
13+
14+
// PostQuery post endpoint to query the graph
15+
func PostQuery(database knowledge.GraphDB, queryHistorizer history.Historizer) http.HandlerFunc {
16+
return func(w http.ResponseWriter, r *http.Request) {
17+
type QueryRequestBody struct {
18+
Query string `json:"q"`
19+
}
20+
21+
type ColumnType struct {
22+
Name string `json:"name"`
23+
Type string `json:"type"`
24+
}
25+
26+
type QueryResponseBody struct {
27+
Items [][]interface{} `json:"items"`
28+
Columns []ColumnType `json:"columns"`
29+
ExecutionTimeMs time.Duration `json:"execution_time_ms"`
30+
}
31+
32+
requestBody := QueryRequestBody{}
33+
err := json.NewDecoder(r.Body).Decode(&requestBody)
34+
if err != nil {
35+
ReplyWithInternalError(w, err)
36+
return
37+
}
38+
39+
if requestBody.Query == "" {
40+
w.WriteHeader(http.StatusBadRequest)
41+
fmt.Println("Empty query parameter")
42+
_, err = w.Write([]byte("Empty query parameter"))
43+
if err != nil {
44+
ReplyWithInternalError(w, err)
45+
}
46+
return
47+
}
48+
49+
querier := knowledge.NewQuerier(database, queryHistorizer)
50+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
51+
defer cancel()
52+
53+
res, err := querier.Query(ctx, requestBody.Query)
54+
if err != nil {
55+
ReplyWithInternalError(w, err)
56+
return
57+
}
58+
defer res.Cursor.Close()
59+
60+
columns := make([]ColumnType, 0)
61+
for _, p := range res.Projections {
62+
var colType string
63+
switch p.ExpressionType {
64+
case knowledge.NodeExprType:
65+
colType = "asset"
66+
case knowledge.EdgeExprType:
67+
colType = "relation"
68+
default:
69+
colType = "property"
70+
}
71+
columns = append(columns, ColumnType{
72+
Name: p.Alias,
73+
Type: colType,
74+
})
75+
}
76+
77+
items := make([][]interface{}, 0)
78+
for res.Cursor.HasMore() {
79+
var d interface{}
80+
err := res.Cursor.Read(context.Background(), &d)
81+
if err != nil {
82+
ReplyWithInternalError(w, err)
83+
return
84+
}
85+
86+
dCols := d.([]interface{})
87+
88+
rowDocs := make([]interface{}, 0)
89+
90+
for _, x := range dCols {
91+
switch v := x.(type) {
92+
case knowledge.AssetWithID:
93+
rowDocs = append(rowDocs, v)
94+
case knowledge.RelationWithID:
95+
rowDocs = append(rowDocs, v)
96+
default:
97+
rowDocs = append(rowDocs, v)
98+
}
99+
}
100+
items = append(items, rowDocs)
101+
}
102+
103+
response := QueryResponseBody{
104+
Items: items,
105+
Columns: columns,
106+
ExecutionTimeMs: res.Statistics.Execution / time.Millisecond,
107+
}
108+
109+
err = json.NewEncoder(w).Encode(response)
110+
if err != nil {
111+
ReplyWithInternalError(w, err)
112+
}
113+
}
114+
}

internal/server/server.go

Lines changed: 1 addition & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -118,107 +118,6 @@ func getDatabaseDetails(database knowledge.GraphDB) http.HandlerFunc {
118118
}
119119
}
120120

121-
func postQuery(database knowledge.GraphDB, queryHistorizer history.Historizer) http.HandlerFunc {
122-
return func(w http.ResponseWriter, r *http.Request) {
123-
type QueryRequestBody struct {
124-
Query string `json:"q"`
125-
}
126-
127-
type ColumnType struct {
128-
Name string `json:"name"`
129-
Type string `json:"type"`
130-
}
131-
132-
type QueryResponseBody struct {
133-
Items [][]interface{} `json:"items"`
134-
Columns []ColumnType `json:"columns"`
135-
ExecutionTimeMs time.Duration `json:"execution_time_ms"`
136-
}
137-
138-
requestBody := QueryRequestBody{}
139-
err := json.NewDecoder(r.Body).Decode(&requestBody)
140-
if err != nil {
141-
handlers.ReplyWithInternalError(w, err)
142-
return
143-
}
144-
145-
if requestBody.Query == "" {
146-
w.WriteHeader(http.StatusBadRequest)
147-
fmt.Println("Empty query parameter")
148-
_, err = w.Write([]byte("Empty query parameter"))
149-
if err != nil {
150-
handlers.ReplyWithInternalError(w, err)
151-
}
152-
return
153-
}
154-
155-
querier := knowledge.NewQuerier(database, queryHistorizer)
156-
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
157-
defer cancel()
158-
159-
res, err := querier.Query(ctx, requestBody.Query)
160-
if err != nil {
161-
handlers.ReplyWithInternalError(w, err)
162-
return
163-
}
164-
defer res.Cursor.Close()
165-
166-
columns := make([]ColumnType, 0)
167-
for _, p := range res.Projections {
168-
var colType string
169-
switch p.ExpressionType {
170-
case knowledge.NodeExprType:
171-
colType = "asset"
172-
case knowledge.EdgeExprType:
173-
colType = "relation"
174-
default:
175-
colType = "property"
176-
}
177-
columns = append(columns, ColumnType{
178-
Name: p.Alias,
179-
Type: colType,
180-
})
181-
}
182-
183-
items := make([][]interface{}, 0)
184-
for res.Cursor.HasMore() {
185-
var d interface{}
186-
err := res.Cursor.Read(context.Background(), &d)
187-
if err != nil {
188-
handlers.ReplyWithInternalError(w, err)
189-
return
190-
}
191-
192-
dCols := d.([]interface{})
193-
194-
rowDocs := make([]interface{}, 0)
195-
196-
for _, x := range dCols {
197-
switch v := x.(type) {
198-
case knowledge.AssetWithID:
199-
rowDocs = append(rowDocs, v)
200-
case knowledge.RelationWithID:
201-
rowDocs = append(rowDocs, v)
202-
default:
203-
rowDocs = append(rowDocs, v)
204-
}
205-
}
206-
items = append(items, rowDocs)
207-
}
208-
209-
response := QueryResponseBody{
210-
Items: items,
211-
Columns: columns,
212-
ExecutionTimeMs: res.Statistics.Execution / time.Millisecond,
213-
}
214-
215-
err = json.NewEncoder(w).Encode(response)
216-
if err != nil {
217-
handlers.ReplyWithInternalError(w, err)
218-
}
219-
}
220-
}
221-
222121
func getGraphRead(registry sources.Registry, graphDB knowledge.GraphDB) http.HandlerFunc {
223122
return func(w http.ResponseWriter, r *http.Request) {
224123
ok, source, err := handlers.IsTokenValid(registry, r)
@@ -288,7 +187,7 @@ func StartServer(listenInterface string,
288187
listSourcesHandler := listSources(sourcesRegistry)
289188
getSourceGraphHandler := getSourceGraph(sourcesRegistry, schemaPersistor)
290189
getDatabaseDetailsHandler := getDatabaseDetails(database)
291-
postQueryHandler := postQuery(database, queryHistorizer)
190+
postQueryHandler := handlers.PostQuery(database, queryHistorizer)
292191
flushDatabaseHandler := flushDatabase(database)
293192

294193
if viper.GetString("password") != "" {

0 commit comments

Comments
 (0)