@@ -75,33 +75,41 @@ func (r *Ref) Child(path string) *Ref {
75
75
// therefore v has the same requirements as the json package. Specifically, it must be a pointer,
76
76
// and must not be nil.
77
77
func (r * Ref ) Get (ctx context.Context , v interface {}) error {
78
- resp , err := r .send (ctx , "GET" )
79
- if err != nil {
80
- return err
78
+ req := & internal.Request {
79
+ Method : http .MethodGet ,
81
80
}
82
- return resp .Unmarshal (http .StatusOK , v )
81
+ _ , err := r .sendAndUnmarshal (ctx , req , v )
82
+ return err
83
83
}
84
84
85
85
// GetWithETag retrieves the value at the current database location, along with its ETag.
86
86
func (r * Ref ) GetWithETag (ctx context.Context , v interface {}) (string , error ) {
87
- resp , err := r .send (ctx , "GET" , internal .WithHeader ("X-Firebase-ETag" , "true" ))
87
+ req := & internal.Request {
88
+ Method : http .MethodGet ,
89
+ Opts : []internal.HTTPOption {
90
+ internal .WithHeader ("X-Firebase-ETag" , "true" ),
91
+ },
92
+ }
93
+ resp , err := r .sendAndUnmarshal (ctx , req , v )
88
94
if err != nil {
89
95
return "" , err
90
- } else if err := resp .Unmarshal (http .StatusOK , v ); err != nil {
91
- return "" , err
92
96
}
97
+
93
98
return resp .Header .Get ("Etag" ), nil
94
99
}
95
100
96
101
// GetShallow performs a shallow read on the current database location.
97
102
//
98
103
// Shallow reads do not retrieve the child nodes of the current reference.
99
104
func (r * Ref ) GetShallow (ctx context.Context , v interface {}) error {
100
- resp , err := r .send (ctx , "GET" , internal .WithQueryParam ("shallow" , "true" ))
101
- if err != nil {
102
- return err
105
+ req := & internal.Request {
106
+ Method : http .MethodGet ,
107
+ Opts : []internal.HTTPOption {
108
+ internal .WithQueryParam ("shallow" , "true" ),
109
+ },
103
110
}
104
- return resp .Unmarshal (http .StatusOK , v )
111
+ _ , err := r .sendAndUnmarshal (ctx , req , v )
112
+ return err
105
113
}
106
114
107
115
// GetIfChanged retrieves the value and ETag of the current database location only if the specified
@@ -112,16 +120,26 @@ func (r *Ref) GetShallow(ctx context.Context, v interface{}) error {
112
120
// If the etag matches, returns false along with the same ETag passed into the function. No data
113
121
// will be stored in v in this case.
114
122
func (r * Ref ) GetIfChanged (ctx context.Context , etag string , v interface {}) (bool , string , error ) {
115
- resp , err := r .send (ctx , "GET" , internal .WithHeader ("If-None-Match" , etag ))
123
+ req := & internal.Request {
124
+ Method : http .MethodGet ,
125
+ Opts : []internal.HTTPOption {
126
+ internal .WithHeader ("If-None-Match" , etag ),
127
+ },
128
+ SuccessFn : successOrNotModified ,
129
+ }
130
+ resp , err := r .sendAndUnmarshal (ctx , req , nil )
116
131
if err != nil {
117
132
return false , "" , err
118
133
}
134
+
119
135
if resp .Status == http .StatusNotModified {
120
136
return false , etag , nil
121
137
}
122
- if err := resp .Unmarshal (http .StatusOK , v ); err != nil {
138
+
139
+ if err := json .Unmarshal (resp .Body , v ); err != nil {
123
140
return false , "" , err
124
141
}
142
+
125
143
return true , resp .Header .Get ("ETag" ), nil
126
144
}
127
145
@@ -131,28 +149,39 @@ func (r *Ref) GetIfChanged(ctx context.Context, etag string, v interface{}) (boo
131
149
// v has the same requirements as the json package. Values like functions and channels cannot be
132
150
// saved into Realtime Database.
133
151
func (r * Ref ) Set (ctx context.Context , v interface {}) error {
134
- resp , err := r .sendWithBody (ctx , "PUT" , v , internal .WithQueryParam ("print" , "silent" ))
135
- if err != nil {
136
- return err
152
+ req := & internal.Request {
153
+ Method : http .MethodPut ,
154
+ Body : internal .NewJSONEntity (v ),
155
+ Opts : []internal.HTTPOption {
156
+ internal .WithQueryParam ("print" , "silent" ),
157
+ },
137
158
}
138
- return resp .CheckStatus (http .StatusNoContent )
159
+ _ , err := r .sendAndUnmarshal (ctx , req , nil )
160
+ return err
139
161
}
140
162
141
163
// SetIfUnchanged conditionally sets the data at this location to the given value.
142
164
//
143
165
// Sets the data at this location to v only if the specified ETag matches. Returns true if the
144
166
// value is written. Returns false if no changes are made to the database.
145
167
func (r * Ref ) SetIfUnchanged (ctx context.Context , etag string , v interface {}) (bool , error ) {
146
- resp , err := r .sendWithBody (ctx , "PUT" , v , internal .WithHeader ("If-Match" , etag ))
168
+ req := & internal.Request {
169
+ Method : http .MethodPut ,
170
+ Body : internal .NewJSONEntity (v ),
171
+ Opts : []internal.HTTPOption {
172
+ internal .WithHeader ("If-Match" , etag ),
173
+ },
174
+ SuccessFn : successOrPreconditionFailed ,
175
+ }
176
+ resp , err := r .sendAndUnmarshal (ctx , req , nil )
147
177
if err != nil {
148
178
return false , err
149
179
}
180
+
150
181
if resp .Status == http .StatusPreconditionFailed {
151
182
return false , nil
152
183
}
153
- if err := resp .CheckStatus (http .StatusOK ); err != nil {
154
- return false , err
155
- }
184
+
156
185
return true , nil
157
186
}
158
187
@@ -164,16 +193,18 @@ func (r *Ref) Push(ctx context.Context, v interface{}) (*Ref, error) {
164
193
if v == nil {
165
194
v = ""
166
195
}
167
- resp , err := r .sendWithBody (ctx , "POST" , v )
168
- if err != nil {
169
- return nil , err
196
+
197
+ req := & internal.Request {
198
+ Method : http .MethodPost ,
199
+ Body : internal .NewJSONEntity (v ),
170
200
}
171
201
var d struct {
172
202
Name string `json:"name"`
173
203
}
174
- if err := resp . Unmarshal ( http . StatusOK , & d ); err != nil {
204
+ if _ , err := r . sendAndUnmarshal ( ctx , req , & d ); err != nil {
175
205
return nil , err
176
206
}
207
+
177
208
return r .Child (d .Name ), nil
178
209
}
179
210
@@ -182,11 +213,16 @@ func (r *Ref) Update(ctx context.Context, v map[string]interface{}) error {
182
213
if len (v ) == 0 {
183
214
return fmt .Errorf ("value argument must be a non-empty map" )
184
215
}
185
- resp , err := r .sendWithBody (ctx , "PATCH" , v , internal .WithQueryParam ("print" , "silent" ))
186
- if err != nil {
187
- return err
216
+
217
+ req := & internal.Request {
218
+ Method : http .MethodPatch ,
219
+ Body : internal .NewJSONEntity (v ),
220
+ Opts : []internal.HTTPOption {
221
+ internal .WithQueryParam ("print" , "silent" ),
222
+ },
188
223
}
189
- return resp .CheckStatus (http .StatusNoContent )
224
+ _ , err := r .sendAndUnmarshal (ctx , req , nil )
225
+ return err
190
226
}
191
227
192
228
// UpdateFn represents a function type that can be passed into Transaction().
@@ -207,55 +243,65 @@ type UpdateFn func(TransactionNode) (interface{}, error)
207
243
// The update function may also force an early abort by returning an error instead of returning a
208
244
// value.
209
245
func (r * Ref ) Transaction (ctx context.Context , fn UpdateFn ) error {
210
- resp , err := r .send (ctx , "GET" , internal .WithHeader ("X-Firebase-ETag" , "true" ))
246
+ req := & internal.Request {
247
+ Method : http .MethodGet ,
248
+ Opts : []internal.HTTPOption {
249
+ internal .WithHeader ("X-Firebase-ETag" , "true" ),
250
+ },
251
+ }
252
+ resp , err := r .sendAndUnmarshal (ctx , req , nil )
211
253
if err != nil {
212
254
return err
213
- } else if err := resp .CheckStatus (http .StatusOK ); err != nil {
214
- return err
215
255
}
216
- etag := resp .Header .Get ("Etag" )
217
256
257
+ etag := resp .Header .Get ("Etag" )
218
258
for i := 0 ; i < txnRetries ; i ++ {
219
259
new , err := fn (& transactionNodeImpl {resp .Body })
220
260
if err != nil {
221
261
return err
222
262
}
223
- resp , err = r .sendWithBody (ctx , "PUT" , new , internal .WithHeader ("If-Match" , etag ))
263
+
264
+ req := & internal.Request {
265
+ Method : http .MethodPut ,
266
+ Body : internal .NewJSONEntity (new ),
267
+ Opts : []internal.HTTPOption {
268
+ internal .WithHeader ("If-Match" , etag ),
269
+ },
270
+ SuccessFn : successOrPreconditionFailed ,
271
+ }
272
+ resp , err = r .sendAndUnmarshal (ctx , req , nil )
224
273
if err != nil {
225
274
return err
226
275
}
276
+
227
277
if resp .Status == http .StatusOK {
228
278
return nil
229
- } else if err := resp .CheckStatus (http .StatusPreconditionFailed ); err != nil {
230
- return err
231
279
}
280
+
232
281
etag = resp .Header .Get ("ETag" )
233
282
}
234
283
return fmt .Errorf ("transaction aborted after failed retries" )
235
284
}
236
285
237
286
// Delete removes this node from the database.
238
287
func (r * Ref ) Delete (ctx context.Context ) error {
239
- resp , err := r .send (ctx , "DELETE" )
240
- if err != nil {
241
- return err
288
+ req := & internal.Request {
289
+ Method : http .MethodDelete ,
242
290
}
243
- return resp .CheckStatus (http .StatusOK )
291
+ _ , err := r .sendAndUnmarshal (ctx , req , nil )
292
+ return err
244
293
}
245
294
246
- func (r * Ref ) send (
247
- ctx context.Context ,
248
- method string ,
249
- opts ... internal.HTTPOption ) (* internal.Response , error ) {
250
-
251
- return r .client .send (ctx , method , r .Path , nil , opts ... )
295
+ func (r * Ref ) sendAndUnmarshal (
296
+ ctx context.Context , req * internal.Request , v interface {}) (* internal.Response , error ) {
297
+ req .URL = r .Path
298
+ return r .client .sendAndUnmarshal (ctx , req , v )
252
299
}
253
300
254
- func (r * Ref ) sendWithBody (
255
- ctx context.Context ,
256
- method string ,
257
- body interface {},
258
- opts ... internal.HTTPOption ) (* internal.Response , error ) {
301
+ func successOrNotModified (resp * internal.Response ) bool {
302
+ return internal .HasSuccessStatus (resp ) || resp .Status == http .StatusNotModified
303
+ }
259
304
260
- return r .client .send (ctx , method , r .Path , internal .NewJSONEntity (body ), opts ... )
305
+ func successOrPreconditionFailed (resp * internal.Response ) bool {
306
+ return internal .HasSuccessStatus (resp ) || resp .Status == http .StatusPreconditionFailed
261
307
}
0 commit comments