google-cloud-firestore 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +8 -0
  3. data/LICENSE +201 -0
  4. data/README.md +30 -0
  5. data/lib/google-cloud-firestore.rb +106 -0
  6. data/lib/google/cloud/firestore.rb +514 -0
  7. data/lib/google/cloud/firestore/batch.rb +462 -0
  8. data/lib/google/cloud/firestore/client.rb +449 -0
  9. data/lib/google/cloud/firestore/collection_reference.rb +249 -0
  10. data/lib/google/cloud/firestore/commit_response.rb +145 -0
  11. data/lib/google/cloud/firestore/convert.rb +561 -0
  12. data/lib/google/cloud/firestore/credentials.rb +35 -0
  13. data/lib/google/cloud/firestore/document_reference.rb +468 -0
  14. data/lib/google/cloud/firestore/document_snapshot.rb +324 -0
  15. data/lib/google/cloud/firestore/field_path.rb +216 -0
  16. data/lib/google/cloud/firestore/field_value.rb +113 -0
  17. data/lib/google/cloud/firestore/generate.rb +35 -0
  18. data/lib/google/cloud/firestore/query.rb +651 -0
  19. data/lib/google/cloud/firestore/service.rb +176 -0
  20. data/lib/google/cloud/firestore/transaction.rb +726 -0
  21. data/lib/google/cloud/firestore/v1beta1.rb +121 -0
  22. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/common.rb +63 -0
  23. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/document.rb +134 -0
  24. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/firestore.rb +584 -0
  25. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/query.rb +215 -0
  26. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/write.rb +167 -0
  27. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/any.rb +124 -0
  28. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/timestamp.rb +106 -0
  29. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/wrappers.rb +89 -0
  30. data/lib/google/cloud/firestore/v1beta1/doc/google/rpc/status.rb +83 -0
  31. data/lib/google/cloud/firestore/v1beta1/doc/overview.rb +53 -0
  32. data/lib/google/cloud/firestore/v1beta1/firestore_client.rb +974 -0
  33. data/lib/google/cloud/firestore/v1beta1/firestore_client_config.json +100 -0
  34. data/lib/google/cloud/firestore/version.rb +22 -0
  35. data/lib/google/firestore/v1beta1/common_pb.rb +44 -0
  36. data/lib/google/firestore/v1beta1/document_pb.rb +49 -0
  37. data/lib/google/firestore/v1beta1/firestore_pb.rb +219 -0
  38. data/lib/google/firestore/v1beta1/firestore_services_pb.rb +87 -0
  39. data/lib/google/firestore/v1beta1/query_pb.rb +103 -0
  40. data/lib/google/firestore/v1beta1/write_pb.rb +73 -0
  41. metadata +251 -0
@@ -0,0 +1,324 @@
1
+ # Copyright 2017 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/firestore/v1beta1"
17
+ require "google/cloud/firestore/document_reference"
18
+ require "google/cloud/firestore/collection_reference"
19
+ require "google/cloud/firestore/convert"
20
+
21
+ module Google
22
+ module Cloud
23
+ module Firestore
24
+ ##
25
+ # # DocumentSnapshot
26
+ #
27
+ # A document snapshot object is an immutable representation for a
28
+ # document in a Cloud Firestore database.
29
+ #
30
+ # The snapshot can reference a non-existing document.
31
+ #
32
+ # @example
33
+ # require "google/cloud/firestore"
34
+ #
35
+ # firestore = Google::Cloud::Firestore.new
36
+ #
37
+ # # Get a document snapshot
38
+ # nyc_snap = firestore.doc("cities/NYC").get
39
+ #
40
+ # # Get the document data
41
+ # nyc_snap[:population] #=> 1000000
42
+ #
43
+ class DocumentSnapshot
44
+ ##
45
+ # @private The Google::Firestore::V1beta1::Document object.
46
+ attr_accessor :grpc
47
+
48
+ ##
49
+ # The document identifier for the document snapshot.
50
+ #
51
+ # @return [String] document identifier.
52
+ def document_id
53
+ ref.document_id
54
+ end
55
+
56
+ ##
57
+ # A string representing the path of the document, relative to the
58
+ # document root of the database.
59
+ #
60
+ # @return [String] document path.
61
+ def document_path
62
+ ref.document_path
63
+ end
64
+
65
+ ##
66
+ # @private A string representing the full path of the document resource.
67
+ #
68
+ # @return [String] document resource path.
69
+ def path
70
+ ref.path
71
+ end
72
+
73
+ # @!group Access
74
+
75
+ ##
76
+ # The document reference object for the data.
77
+ #
78
+ # @return [DocumentReference] document reference.
79
+ #
80
+ # @example
81
+ # require "google/cloud/firestore"
82
+ #
83
+ # firestore = Google::Cloud::Firestore.new
84
+ #
85
+ # # Get a document snapshot
86
+ # nyc_snap = firestore.doc("cities/NYC").get
87
+ #
88
+ # # Get the document reference
89
+ # nyc_ref = nyc_snap.ref
90
+ #
91
+ def ref
92
+ @ref
93
+ end
94
+ alias_method :reference, :ref
95
+
96
+ ##
97
+ # The collection the document snapshot belongs to.
98
+ #
99
+ # @return [CollectionReference] parent collection.
100
+ #
101
+ # @example
102
+ # require "google/cloud/firestore"
103
+ #
104
+ # firestore = Google::Cloud::Firestore.new
105
+ #
106
+ # # Get a document snapshot
107
+ # nyc_snap = firestore.doc("cities/NYC").get
108
+ #
109
+ # # Get the document's parent collection
110
+ # cities_col = nyc_snap.parent
111
+ #
112
+ def parent
113
+ ref.parent
114
+ end
115
+
116
+ # @!endgroup
117
+
118
+ # @!group Data
119
+
120
+ ##
121
+ # Retrieves the document data.
122
+ #
123
+ # @return [Hash] The document data.
124
+ #
125
+ # @example
126
+ # require "google/cloud/firestore"
127
+ #
128
+ # firestore = Google::Cloud::Firestore.new
129
+ #
130
+ # nyc_snap = firestore.doc("cities/NYC").get
131
+ #
132
+ # # Get the document data
133
+ # nyc_snap.data[:population] #=> 1000000
134
+ #
135
+ def data
136
+ return nil if missing?
137
+ Convert.fields_to_hash grpc.fields, ref.client
138
+ end
139
+ alias_method :fields, :data
140
+
141
+ ##
142
+ # Retrieves the document data.
143
+ #
144
+ # @param [FieldPath, String, Symbol] field_path A field path
145
+ # representing the path of the data to select. A field path can
146
+ # represent as a string of individual fields joined by ".". Fields
147
+ # containing `~`, `*`, `/`, `[`, `]`, and `.` cannot be in a dotted
148
+ # string, and should provided using a {FieldPath} object instead.
149
+ #
150
+ # @return [Object] The data at the field path.
151
+ #
152
+ # @example
153
+ # require "google/cloud/firestore"
154
+ #
155
+ # firestore = Google::Cloud::Firestore.new
156
+ #
157
+ # nyc_snap = firestore.doc("cities/NYC").get
158
+ #
159
+ # nyc_snap.get(:population) #=> 1000000
160
+ #
161
+ # @example Accessing data using []:
162
+ # require "google/cloud/firestore"
163
+ #
164
+ # firestore = Google::Cloud::Firestore.new
165
+ #
166
+ # nyc_snap = firestore.doc("cities/NYC").get
167
+ #
168
+ # nyc_snap[:population] #=> 1000000
169
+ #
170
+ # @example Nested data can be accessing with field path:
171
+ # require "google/cloud/firestore"
172
+ #
173
+ # firestore = Google::Cloud::Firestore.new
174
+ #
175
+ # frank_snap = firestore.doc("users/frank").get
176
+ #
177
+ # frank_snap.get("favorites.food") #=> "Pizza"
178
+ #
179
+ # @example Nested data can be accessing with FieldPath object:
180
+ # require "google/cloud/firestore"
181
+ #
182
+ # firestore = Google::Cloud::Firestore.new
183
+ #
184
+ # user_snap = firestore.doc("users/frank").get
185
+ #
186
+ # nested_field_path = firestore.field_path :favorites, :food
187
+ # user_snap.get(nested_field_path) #=> "Pizza"
188
+ #
189
+ def get field_path
190
+ unless field_path.is_a? FieldPath
191
+ field_path = FieldPath.parse field_path
192
+ end
193
+
194
+ nodes = field_path.fields.map(&:to_sym)
195
+ selected_data = data
196
+
197
+ nodes.each do |node|
198
+ unless selected_data.is_a? Hash
199
+ fail ArgumentError,
200
+ "#{field_path.formatted_string} is not contained in the data"
201
+ end
202
+ selected_data = selected_data[node]
203
+ end
204
+ selected_data
205
+ end
206
+ alias_method :[], :get
207
+
208
+ # @!endgroup
209
+
210
+ ##
211
+ # The time at which the document was created.
212
+ #
213
+ # This value increases when a document is deleted then recreated.
214
+ #
215
+ # @return [Time] The time the document was was created
216
+ #
217
+ def created_at
218
+ return nil if missing?
219
+ Convert.timestamp_to_time grpc.create_time
220
+ end
221
+ alias_method :create_time, :created_at
222
+
223
+ ##
224
+ # The time at which the document was last changed.
225
+ #
226
+ # This value is initally set to the `created_at` on document creation,
227
+ # and increases each time the document is updated.
228
+ #
229
+ # @return [Time] The time the document was was last changed
230
+ #
231
+ def updated_at
232
+ return nil if missing?
233
+ Convert.timestamp_to_time grpc.update_time
234
+ end
235
+ alias_method :update_time, :updated_at
236
+
237
+ ##
238
+ # The time at which the document was read.
239
+ #
240
+ # This value is set even if the document does not exist.
241
+ #
242
+ # @return [Time] The time the document was read
243
+ #
244
+ def read_at
245
+ @read_at
246
+ end
247
+ alias_method :read_time, :read_at
248
+
249
+ ##
250
+ # Determines whether the document exists.
251
+ #
252
+ # @return [Boolean] Whether the document exists.
253
+ #
254
+ # @example
255
+ # require "google/cloud/firestore"
256
+ #
257
+ # firestore = Google::Cloud::Firestore.new
258
+ #
259
+ # nyc_snap = firestore.doc("cities/NYC").get
260
+ #
261
+ # # Does NYC exist?
262
+ # nyc_snap.exists? #=> true
263
+ #
264
+ def exists?
265
+ !missing?
266
+ end
267
+
268
+ ##
269
+ # Determines whether the document is missing.
270
+ #
271
+ # @return [Boolean] Whether the document is missing.
272
+ #
273
+ # @example
274
+ # require "google/cloud/firestore"
275
+ #
276
+ # firestore = Google::Cloud::Firestore.new
277
+ #
278
+ # atlantis_snap = firestore.doc("cities/Atlantis").get
279
+ #
280
+ # # Does Atlantis exist?
281
+ # atlantis_snap.missing? #=> true
282
+ #
283
+ def missing?
284
+ grpc.nil?
285
+ end
286
+
287
+ ##
288
+ # @private New DocumentSnapshot from a
289
+ # Google::Firestore::V1beta1::RunQueryResponse object.
290
+ def self.from_query_result result, context
291
+ ref = DocumentReference.from_path result.document.name, context
292
+ read_at = Convert.timestamp_to_time result.read_time
293
+
294
+ new.tap do |s|
295
+ s.grpc = result.document
296
+ s.instance_variable_set :@ref, ref
297
+ s.instance_variable_set :@read_at, read_at
298
+ end
299
+ end
300
+
301
+ ##
302
+ # @private New DocumentSnapshot from a
303
+ # Google::Firestore::V1beta1::BatchGetDocumentsResponse object.
304
+ def self.from_batch_result result, context
305
+ ref = nil
306
+ grpc = nil
307
+ if result.result == :found
308
+ grpc = result.found
309
+ ref = DocumentReference.from_path grpc.name, context
310
+ else
311
+ ref = DocumentReference.from_path result.missing, context
312
+ end
313
+ read_at = Convert.timestamp_to_time result.read_time
314
+
315
+ new.tap do |s|
316
+ s.grpc = grpc
317
+ s.instance_variable_set :@ref, ref
318
+ s.instance_variable_set :@read_at, read_at
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,216 @@
1
+ # Copyright 2017 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/firestore/convert"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Firestore
21
+ ##
22
+ # # FieldPath
23
+ #
24
+ # Represents a field path to the Firestore API. See {Client#field_path}.
25
+ #
26
+ # @example
27
+ # require "google/cloud/firestore"
28
+ #
29
+ # firestore = Google::Cloud::Firestore.new
30
+ #
31
+ # user_snap = firestore.doc("users/frank").get
32
+ #
33
+ # nested_field_path = Google::Cloud::Firestore::FieldPath.new(
34
+ # :favorites, :food
35
+ # )
36
+ # user_snap.get(nested_field_path) #=> "Pizza"
37
+ #
38
+ class FieldPath
39
+ include Comparable
40
+
41
+ ##
42
+ # Creates a field path object representing a nested field for
43
+ # document data.
44
+ #
45
+ # @return [FieldPath] The field path object.
46
+ #
47
+ # @example
48
+ # require "google/cloud/firestore"
49
+ #
50
+ # firestore = Google::Cloud::Firestore.new
51
+ #
52
+ # user_snap = firestore.doc("users/frank").get
53
+ #
54
+ # nested_field_path = Google::Cloud::Firestore::FieldPath.new(
55
+ # :favorites, :food
56
+ # )
57
+ # user_snap.get(nested_field_path) #=> "Pizza"
58
+ #
59
+ def initialize *fields
60
+ @fields = fields.flatten.map(&:to_s)
61
+ @fields.each do |field|
62
+ fail ArgumentError, "empty paths not allowed" if field.empty?
63
+ end
64
+ end
65
+
66
+ ##
67
+ # @private The individual fields representing the nested field path for
68
+ # document data.
69
+ #
70
+ # @return [Array<String>] The fields.
71
+ #
72
+ # @example
73
+ # require "google/cloud/firestore"
74
+ #
75
+ # firestore = Google::Cloud::Firestore.new
76
+ #
77
+ # user_snap = firestore.doc("users/frank").get
78
+ #
79
+ # nested_field_path = Google::Cloud::Firestore::FieldPath.new(
80
+ # :favorites, :food
81
+ # )
82
+ # nested_field_path.fields #=> ["favorites", "food"]
83
+ #
84
+ def fields
85
+ @fields
86
+ end
87
+
88
+ ##
89
+ # @private A string representing the nested fields for document data as
90
+ # a string of individual fields joined by ".". Fields containing `~`,
91
+ # `*`, `/`, `[`, `]`, and `.` are escaped.
92
+ #
93
+ # @return [String] The formatted string.
94
+ #
95
+ # @example
96
+ # require "google/cloud/firestore"
97
+ #
98
+ # firestore = Google::Cloud::Firestore.new
99
+ #
100
+ # user_snap = firestore.doc("users/frank").get
101
+ #
102
+ # nested_field_path = Google::Cloud::Firestore::FieldPath.new(
103
+ # :favorites, :food
104
+ # )
105
+ # nested_field_path.formatted_string #=> "favorites.food"
106
+ #
107
+ def formatted_string
108
+ escaped_fields = @fields.map { |field| escape_field_for_path field }
109
+ escaped_fields.join(".")
110
+ end
111
+
112
+ ##
113
+ # Creates a field path object representing the sentinel ID of a
114
+ # document. It can be used in queries to sort or filter by the document
115
+ # ID. See {Client#document_id}.
116
+ #
117
+ # @return [FieldPath] The field path object.
118
+ #
119
+ # @example
120
+ # require "google/cloud/firestore"
121
+ #
122
+ # firestore = Google::Cloud::Firestore.new
123
+ #
124
+ # # Get a collection reference
125
+ # cities_col = firestore.col "cities"
126
+ #
127
+ # # Create a query
128
+ # query = cities_col.start_at("NYC").order(
129
+ # Google::Cloud::Firestore::FieldPath.document_id
130
+ # )
131
+ #
132
+ # query.get do |city|
133
+ # puts "#{city.document_id} has #{city[:population]} residents."
134
+ # end
135
+ #
136
+ def self.document_id
137
+ new :__name__
138
+ end
139
+
140
+ ##
141
+ # @private Creates a field path object representing the nested fields
142
+ # for document data.
143
+ #
144
+ # @param [String] dotted_string A string representing the path of the
145
+ # document data. The string can represent as a string of individual
146
+ # fields joined by ".". Fields containing `~`, `*`, `/`, `[`, `]`, and
147
+ # `.` cannot be in a dotted string, and should be created by passing
148
+ # individual field strings to {FieldPath.new} instead.
149
+ #
150
+ # @return [FieldPath] The field path object.
151
+ #
152
+ # @example
153
+ # require "google/cloud/firestore"
154
+ #
155
+ # firestore = Google::Cloud::Firestore.new
156
+ #
157
+ # user_snap = firestore.doc("users/frank").get
158
+ #
159
+ # field_path = Google::Cloud::Firestore::FieldPath.parse(
160
+ # "favorites.food"
161
+ # )
162
+ # field_path.fields #=> ["favorites", "food"]
163
+ #
164
+ def self.parse dotted_string
165
+ return new dotted_string if dotted_string.is_a? Array
166
+
167
+ fields = String(dotted_string).split(".")
168
+ fields.each do |field|
169
+ if INVALID_FIELD_PATH_CHARS.match field
170
+ fail ArgumentError, "invalid character, use FieldPath instead"
171
+ end
172
+ end
173
+ new fields
174
+ end
175
+
176
+ ##
177
+ # @private
178
+ def <=> other
179
+ return nil unless other.is_a? FieldPath
180
+ formatted_string <=> other.formatted_string
181
+ end
182
+
183
+ ##
184
+ # @private
185
+ def eql? other
186
+ formatted_string.eql? other.formatted_string
187
+ end
188
+
189
+ ##
190
+ # @private
191
+ def hash
192
+ formatted_string.hash
193
+ end
194
+
195
+ protected
196
+
197
+ START_FIELD_PATH_CHARS = /\A[a-zA-Z_]/
198
+ INVALID_FIELD_PATH_CHARS = %r{[\~\*\/\[\]]}
199
+
200
+ def escape_field_for_path field
201
+ field = String field
202
+
203
+ if INVALID_FIELD_PATH_CHARS.match(field) ||
204
+ field["."] || field["`"] || field["\\"]
205
+ escaped_field = field.gsub(/[\`\\]/, "`" => "\\\`", "\\" => "\\\\")
206
+ return "`#{escaped_field}`"
207
+ end
208
+
209
+ return field if START_FIELD_PATH_CHARS.match field
210
+
211
+ "`#{field}`"
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end