google-cloud-firestore 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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