-
Notifications
You must be signed in to change notification settings - Fork 0
/
firebaseQueryHandler.go
277 lines (222 loc) · 7.34 KB
/
firebaseQueryHandler.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
package rest
import (
"cloud.google.com/go/firestore"
"context"
"firebase.google.com/go"
"github.com/GlidingTracks/gt-backend"
"github.com/GlidingTracks/gt-backend/constant"
"github.com/GlidingTracks/gt-backend/models"
"github.com/pkg/errors"
"google.golang.org/api/iterator"
"io/ioutil"
"net/http"
)
// fileNameFQH filenameDBH
const fileNameFQH = "firebaseQueryHandler.go"
// GetTracks gets a list of IgcMetadata from Firebase based on query
func GetTracks(app *firebase.App, query models.FirebaseQuery) (data []models.IgcMetadata, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "GetTracks")
ctx := context.Background()
if app == nil {
err = errors.New("Could not contact DB")
return
}
client, err := app.Firestore(ctx)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
// Start query to Firebase based on the Query Type
if query.Qt == "Private" {
iter := client.Collection(constant.IgcMetadata).
Where("UID", "==", query.UID).
OrderBy(constant.FirebaseQueryOrder, query.OrdDir).
StartAfter(query.TmSk).Documents(ctx)
return processIterGetTracks(iter, "")
} else {
iter := client.Collection(constant.IgcMetadata).
Where("Privacy", "==", false).
OrderBy(constant.FirebaseQueryOrder, query.OrdDir).
StartAfter(query.TmSk).Documents(ctx)
return processIterGetTracks(iter, query.UID)
}
return data, err
}
// GetTrack gets a track file from the Firebase Storage based on TrackID in metadata
func GetTrack(app *firebase.App, trackID string, uid string) (data []byte, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "GetTrack")
// Verify that the user can download the file and abort if not
_, d, err := getTrackMetadata(app, trackID, uid, false)
if d.Privacy == true && d.UID != uid {
err = errors.New(constant.ErrorForbidden)
gtbackend.DebugLogErrNoMsg(log, err)
return
}
client, err := app.Storage(context.Background())
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
bucket, err := client.DefaultBucket()
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
// Read the entire file to data
rc, err := bucket.Object(trackID).NewReader(context.Background())
data, err = ioutil.ReadAll(rc)
defer rc.Close()
return
}
// DeleteTrack deletes the track from storage and firestore
func DeleteTrack(app *firebase.App, trackID string, uid string) (httpCode int, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "DeleteTrack")
ctx := context.Background()
httpCode = http.StatusBadRequest // Return before OK means failure
// Verify that the user actually can delete this track
client, _, err := getTrackMetadata(app, trackID, uid, true)
if err != nil {
httpCode = http.StatusForbidden
gtbackend.DebugLogErrNoMsg(log, err)
return
}
// Delete file from storage
storageClient, err := app.Storage(ctx)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
bucket, err := storageClient.DefaultBucket()
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
err = bucket.Object(trackID).Delete(ctx)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
_, err = client.Collection(constant.IgcMetadata).Doc(trackID).Delete(ctx)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
httpCode = http.StatusOK
return
}
// UpdatePrivacy Updates privacy setting to new variable
func UpdatePrivacy(app *firebase.App, trackID string, uid string, newSetting bool) (updated models.IgcMetadata, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "UpdatePrivacy")
// Can only update privacy on owned tracks
client, updated, err := getTrackMetadata(app, trackID, uid, true)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
// Set privacy and update it on firestore
updated.Privacy = newSetting
_, err = client.Collection(constant.IgcMetadata).Doc(trackID).Set(context.Background(), updated)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
}
return
}
// TakeOwnership Takes ownership of a track owned by the ScraperUID
func TakeOwnership(app *firebase.App, trackID string, uid string) (own models.IgcMetadata, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "TakeOwnership")
// Verify that the Scraper owns the track
client, own, err := getTrackMetadata(app, trackID, constant.ScraperUID, true)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
// Take ownership and update status on firestore
own.UID = uid
_, err = client.Collection(constant.IgcMetadata).Doc(trackID).Set(context.Background(), own)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
}
return
}
// InsertTrackPoint Inserts TrackPoint data for caching in the database
func InsertTrackPoint(app *firebase.App, trackID string, uid string, data []models.TrackPoint) (updated models.IgcMetadata, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "InsertTrackPoint")
client, updated, err := getTrackMetadata(app, trackID, uid, false)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
updated.TrackPoints = data
_, err = client.Collection(constant.IgcMetadata).Doc(trackID).Set(context.Background(), updated)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
}
return
}
// Gets a single track metadata from firestore, can optionally verify that UID matches for security
func getTrackMetadata(app *firebase.App, trackID string, uid string, verifyUID bool) (client *firestore.Client, data models.IgcMetadata, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "getTrackMetadata")
ctx := context.Background()
client, err = app.Firestore(ctx)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
}
doc, err := client.Collection(constant.IgcMetadata).Doc(trackID).Get(ctx)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
d, err := documentToModel(doc)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return
}
if verifyUID && d.UID != uid {
err = errors.New(constant.ErrorForbidden)
gtbackend.DebugLogErrNoMsg(log, err)
return
}
data = d
return
}
// documentToModel Pulls an IgcMetadata object out of a DocumentSnapshot
func documentToModel(doc *firestore.DocumentSnapshot) (data models.IgcMetadata, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "documentToModel")
// Convert doc to our model
err = doc.DataTo(&data)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return data, err
}
return
}
/** processIterGetTracks
Processes the request made to Firebase based
iter *firestore.DocumentIterator Iterator with the results from firestore
filterUID string Filter UID to remove from the results
*/
func processIterGetTracks(iter *firestore.DocumentIterator, filterUID string) (data []models.IgcMetadata, err error) {
log := gtbackend.DebugLogPrepareHeader(fileNameFQH, "processIterGetTracks")
// Process track query until length of data is the size of a page
for len(data) < constant.PageSize {
doc, err := iter.Next()
// Early break if there is no more data (last page)
if err == iterator.Done {
break
}
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return data, err
}
d, err := documentToModel(doc)
if err != nil {
gtbackend.DebugLogErrNoMsg(log, err)
return data, err
}
// Filter out matching UID and add to data
if d.UID != filterUID && d.UID != "" {
data = append(data, d)
}
}
return data, err
}