Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Commit 4362f60

Browse files
committed
api: add function to retrieve list of indexes in the database
This is called using the path "/v1/indexes" on the API server.
1 parent a138690 commit 4362f60

File tree

2 files changed

+82
-75
lines changed

2 files changed

+82
-75
lines changed

api/handlers.go

Lines changed: 78 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ import (
1111
com "github.com/sqlitebrowser/dbhub.io/common"
1212
)
1313

14-
// collectInfo is an internal function which checks the authentication of incoming requests, extracts
15-
// the database owner, name, & commitID, then fetches the Minio bucket and ID for the database file.
14+
// collectInfo is an internal function which:
15+
// 1. Authenticates incoming requests
16+
// 2. Extracts the database owner, name, & commitID from the request
17+
// 3. Fetches the database from Minio (with appropriate permission checks)
18+
// 4. Opens the database, returning the connection handle
1619
// This function exists purely because this code is commonly to most of the handlers
17-
func collectInfo(w http.ResponseWriter, r *http.Request) (bucket, id string, err error, httpStatus int) {
20+
func collectInfo(w http.ResponseWriter, r *http.Request) (sdb *sqlite.Conn, err error, httpStatus int) {
1821
var loggedInUser string
1922
loggedInUser, err = checkAuth(w, r)
2023
if err != nil {
@@ -32,6 +35,7 @@ func collectInfo(w http.ResponseWriter, r *http.Request) (bucket, id string, err
3235
dbFolder := "/"
3336

3437
// Check if the user has access to the requested database
38+
var bucket, id string
3539
bucket, id, _, err = com.MinioLocation(dbOwner, dbFolder, dbName, commitID, loggedInUser)
3640
if err != nil {
3741
httpStatus = http.StatusInternalServerError
@@ -46,9 +50,61 @@ func collectInfo(w http.ResponseWriter, r *http.Request) (bucket, id string, err
4650
httpStatus = http.StatusNotFound
4751
return
4852
}
53+
54+
// Retrieve database file from Minio, using locally cached version if it's already there
55+
newDB, err := com.RetrieveDatabaseFile(bucket, id)
56+
if err != nil {
57+
httpStatus = http.StatusNotFound
58+
return
59+
}
60+
61+
// Open the SQLite database in read only mode
62+
sdb, err = sqlite.Open(newDB, sqlite.OpenReadOnly)
63+
if err != nil {
64+
log.Printf("Couldn't open database in viewsHandler(): %s", err)
65+
httpStatus = http.StatusInternalServerError
66+
return
67+
}
68+
if err = sdb.EnableExtendedResultCodes(true); err != nil {
69+
log.Printf("Couldn't enable extended result codes in viewsHandler(): %v\n", err.Error())
70+
httpStatus = http.StatusInternalServerError
71+
return
72+
}
4973
return
5074
}
5175

76+
// indexesHandler returns the list of indexes present in a SQLite database
77+
// This can be run from the command line using curl, like this:
78+
// $ curl -F apikey="YOUR_API_KEY_HERE" -F dbowner="justinclift" -F dbname="Join Testing.sqlite" https://api.dbhub.io/v1/indexes
79+
// * "apikey" is one of your API keys. These can be generated from your Settings page once logged in
80+
// * "dbowner" is the owner of the database being queried
81+
// * "dbname" is the name of the database being queried
82+
func indexesHandler(w http.ResponseWriter, r *http.Request) {
83+
// Do auth check, grab request info, open the database
84+
sdb, err, httpStatus := collectInfo(w, r)
85+
if err != nil {
86+
jsonErr(w, err.Error(), httpStatus)
87+
return
88+
}
89+
defer sdb.Close()
90+
91+
// Retrieve the list of indexes
92+
idx, err := sdb.Indexes("")
93+
if err != nil {
94+
jsonErr(w, err.Error(), http.StatusInternalServerError)
95+
return
96+
}
97+
98+
// Return the results
99+
jsonData, err := json.Marshal(idx)
100+
if err != nil {
101+
log.Printf("Error when JSON marshalling returned data in indexesHandler(): %v\n", err)
102+
jsonErr(w, err.Error(), http.StatusInternalServerError)
103+
return
104+
}
105+
fmt.Fprintf(w, string(jsonData))
106+
}
107+
52108
// queryHandler executes a SQL query on a SQLite database, returning the results to the caller
53109
// This can be run from the command line using curl, like this:
54110
// $ curl -kD headers.out -F apikey="YOUR_API_KEY_HERE" -F dbowner="justinclift" -F dbname="Join Testing.sqlite" \
@@ -61,14 +117,14 @@ func collectInfo(w http.ResponseWriter, r *http.Request) (bucket, id string, err
61117
func queryHandler(w http.ResponseWriter, r *http.Request) {
62118
loggedInUser, err := checkAuth(w, r)
63119
if err != nil {
64-
w.WriteHeader(http.StatusUnauthorized)
120+
jsonErr(w, err.Error(), http.StatusUnauthorized)
65121
return
66122
}
67123

68124
// Extract the database owner name, database name, and (optional) commit ID for the database from the request
69125
dbOwner, dbName, commitID, err := com.GetFormODC(r)
70126
if err != nil {
71-
w.WriteHeader(http.StatusInternalServerError)
127+
jsonErr(w, err.Error(), http.StatusInternalServerError)
72128
return
73129
}
74130
dbFolder := "/"
@@ -77,39 +133,35 @@ func queryHandler(w http.ResponseWriter, r *http.Request) {
77133
rawInput := r.FormValue("sql")
78134
decodedStr, err := com.CheckUnicode(rawInput)
79135
if err != nil {
80-
w.WriteHeader(http.StatusBadRequest)
81-
fmt.Fprint(w, err)
136+
jsonErr(w, err.Error(), http.StatusBadRequest)
82137
return
83138
}
84139

85140
// Check if the requested database exists
86141
exists, err := com.CheckDBExists(loggedInUser, dbOwner, dbFolder, dbName)
87142
if err != nil {
88-
w.WriteHeader(http.StatusInternalServerError)
89-
fmt.Fprint(w, err)
143+
jsonErr(w, err.Error(), http.StatusInternalServerError)
90144
return
91145
}
92146
if !exists {
93-
w.WriteHeader(http.StatusNotFound)
94-
fmt.Fprintf(w, "Database '%s%s%s' doesn't exist", dbOwner, dbFolder, dbName)
147+
jsonErr(w, fmt.Sprintf("Database '%s%s%s' doesn't exist", dbOwner, dbFolder, dbName),
148+
http.StatusNotFound)
95149
return
96150
}
97151

98152
// Run the query
99153
var data com.SQLiteRecordSet
100154
data, err = com.SQLiteRunQueryDefensive(w, r, com.API, dbOwner, dbFolder, dbName, commitID, loggedInUser, decodedStr)
101155
if err != nil {
102-
w.WriteHeader(http.StatusInternalServerError)
103-
fmt.Fprint(w, err)
156+
jsonErr(w, err.Error(), http.StatusInternalServerError)
104157
return
105158
}
106159

107160
// Return the results
108161
jsonData, err := json.Marshal(data.Records)
109162
if err != nil {
110-
errMsg := fmt.Sprintf("Error when JSON marshalling the returned data: %v\n", err)
111-
log.Print(errMsg)
112-
http.Error(w, errMsg, http.StatusBadRequest)
163+
jsonErr(w, fmt.Sprintf("Error when JSON marshalling the returned data: %v\n", err),
164+
http.StatusBadRequest)
113165
return
114166
}
115167
fmt.Fprintf(w, string(jsonData))
@@ -148,46 +200,23 @@ func rootHandler(w http.ResponseWriter, r *http.Request) {
148200
// * "dbowner" is the owner of the database being queried
149201
// * "dbname" is the name of the database being queried
150202
func tablesHandler(w http.ResponseWriter, r *http.Request) {
151-
// Do auth check, database existence check, and grab it's Minio bucket and ID
152-
bucket, id, err, httpStatus := collectInfo(w, r)
203+
// Do auth check, grab request info, open the database
204+
sdb, err, httpStatus := collectInfo(w, r)
153205
if err != nil {
154206
jsonErr(w, err.Error(), httpStatus)
155207
return
156208
}
157-
158-
// Retrieve database file from Minio, using locally cached version if it's already there
159-
newDB, err := com.RetrieveDatabaseFile(bucket, id)
160-
if err != nil {
161-
jsonErr(w, err.Error(), http.StatusNotFound)
162-
return
163-
}
164-
165-
// Open the SQLite database in read only mode
166-
var sdb *sqlite.Conn
167-
sdb, err = sqlite.Open(newDB, sqlite.OpenReadOnly)
168-
if err != nil {
169-
log.Printf("Couldn't open database in tablesHandler(): %s", err)
170-
jsonErr(w, err.Error(), http.StatusInternalServerError)
171-
return
172-
}
173-
if err = sdb.EnableExtendedResultCodes(true); err != nil {
174-
log.Printf("Couldn't enable extended result codes in tablesHandler(): %v\n", err.Error())
175-
jsonErr(w, err.Error(), http.StatusInternalServerError)
176-
return
177-
}
209+
defer sdb.Close()
178210

179211
// Retrieve the list of tables
180-
var returnData struct {
181-
Tables []string `json:"tables"`
182-
}
183-
returnData.Tables, err = com.Tables(sdb)
212+
tables, err := com.Tables(sdb)
184213
if err != nil {
185214
jsonErr(w, err.Error(), http.StatusInternalServerError)
186215
return
187216
}
188217

189218
// Return the results
190-
jsonData, err := json.Marshal(returnData.Tables)
219+
jsonData, err := json.Marshal(tables)
191220
if err != nil {
192221
log.Printf("Error when JSON marshalling returned data in tablesHandler(): %v\n", err)
193222
jsonErr(w, err.Error(), http.StatusInternalServerError)
@@ -203,46 +232,23 @@ func tablesHandler(w http.ResponseWriter, r *http.Request) {
203232
// * "dbowner" is the owner of the database being queried
204233
// * "dbname" is the name of the database being queried
205234
func viewsHandler(w http.ResponseWriter, r *http.Request) {
206-
// Do auth check, database existence check, and grab it's Minio bucket and ID
207-
bucket, id, err, httpStatus := collectInfo(w, r)
235+
// Do auth check, grab request info, open the database
236+
sdb, err, httpStatus := collectInfo(w, r)
208237
if err != nil {
209238
jsonErr(w, err.Error(), httpStatus)
210239
return
211240
}
212-
213-
// Retrieve database file from Minio, using locally cached version if it's already there
214-
newDB, err := com.RetrieveDatabaseFile(bucket, id)
215-
if err != nil {
216-
jsonErr(w, err.Error(), http.StatusNotFound)
217-
return
218-
}
219-
220-
// Open the SQLite database in read only mode
221-
var sdb *sqlite.Conn
222-
sdb, err = sqlite.Open(newDB, sqlite.OpenReadOnly)
223-
if err != nil {
224-
log.Printf("Couldn't open database in viewsHandler(): %s", err)
225-
jsonErr(w, err.Error(), http.StatusInternalServerError)
226-
return
227-
}
228-
if err = sdb.EnableExtendedResultCodes(true); err != nil {
229-
log.Printf("Couldn't enable extended result codes in viewsHandler(): %v\n", err.Error())
230-
jsonErr(w, err.Error(), http.StatusInternalServerError)
231-
return
232-
}
241+
defer sdb.Close()
233242

234243
// Retrieve the list of views
235-
var returnData struct {
236-
Views []string `json:"views"`
237-
}
238-
returnData.Views, err = com.Views(sdb)
244+
views, err := com.Views(sdb)
239245
if err != nil {
240246
jsonErr(w, err.Error(), http.StatusInternalServerError)
241247
return
242248
}
243249

244250
// Return the results
245-
jsonData, err := json.Marshal(returnData.Views)
251+
jsonData, err := json.Marshal(views)
246252
if err != nil {
247253
log.Printf("Error when JSON marshalling returned data in viewsHandler(): %v\n", err)
248254
jsonErr(w, err.Error(), http.StatusInternalServerError)

api/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ func main() {
7777

7878
// Our pages
7979
http.Handle("/", gz.GzipHandler(handleWrapper(rootHandler)))
80+
http.Handle("/v1/indexes", gz.GzipHandler(handleWrapper(indexesHandler)))
8081
http.Handle("/v1/query", gz.GzipHandler(handleWrapper(queryHandler)))
8182
http.Handle("/v1/tables", gz.GzipHandler(handleWrapper(tablesHandler)))
8283
http.Handle("/v1/views", gz.GzipHandler(handleWrapper(viewsHandler)))
@@ -135,12 +136,12 @@ func handleWrapper(fn http.HandlerFunc) http.HandlerFunc {
135136

136137
// jsonErr returns an error message wrapped in JSON, for (potentially) easier processing by an API caller
137138
func jsonErr(w http.ResponseWriter, msg string, statusCode int) {
138-
errmsg := com.JsonError{
139+
je := com.JsonError{
139140
Error: msg,
140141
}
141-
jsonData, err := json.Marshal(errmsg)
142+
jsonData, err := json.Marshal(je)
142143
if err != nil {
143-
errMsg := fmt.Sprintf("A futher error occurred when JSON marshalling an error structure: %v\n", err)
144+
errMsg := fmt.Sprintf("A 2nd error occurred when JSON marshalling an error structure: %v\n", err)
144145
log.Print(errMsg)
145146
http.Error(w, errMsg, http.StatusInternalServerError)
146147
fmt.Fprintf(w, `{"error":"An error occured when marshalling JSON inside jsonErr()"}`)

0 commit comments

Comments
 (0)