gcloud 0.8.2 → 0.9.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 (55) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +26 -0
  3. data/OVERVIEW.md +10 -8
  4. data/lib/gcloud.rb +12 -13
  5. data/lib/gcloud/bigquery/dataset/list.rb +2 -4
  6. data/lib/gcloud/bigquery/job/list.rb +3 -5
  7. data/lib/gcloud/bigquery/table/list.rb +3 -5
  8. data/lib/gcloud/datastore.rb +326 -97
  9. data/lib/gcloud/datastore/commit.rb +73 -56
  10. data/lib/gcloud/datastore/credentials.rb +1 -12
  11. data/lib/gcloud/datastore/cursor.rb +76 -0
  12. data/lib/gcloud/datastore/dataset.rb +337 -134
  13. data/lib/gcloud/datastore/dataset/lookup_results.rb +12 -12
  14. data/lib/gcloud/datastore/dataset/query_results.rb +117 -27
  15. data/lib/gcloud/datastore/entity.rb +159 -93
  16. data/lib/gcloud/datastore/errors.rb +0 -21
  17. data/lib/gcloud/datastore/gql_query.rb +211 -0
  18. data/lib/gcloud/datastore/grpc_utils.rb +131 -0
  19. data/lib/gcloud/datastore/key.rb +74 -65
  20. data/lib/gcloud/datastore/properties.rb +14 -1
  21. data/lib/gcloud/datastore/query.rb +188 -52
  22. data/lib/gcloud/datastore/service.rb +161 -0
  23. data/lib/gcloud/datastore/transaction.rb +175 -60
  24. data/lib/gcloud/dns/change/list.rb +2 -4
  25. data/lib/gcloud/dns/record/list.rb +2 -4
  26. data/lib/gcloud/dns/zone/list.rb +2 -4
  27. data/lib/gcloud/grpc_utils.rb +11 -0
  28. data/lib/gcloud/logging/entry.rb +6 -17
  29. data/lib/gcloud/logging/entry/list.rb +8 -9
  30. data/lib/gcloud/logging/metric/list.rb +4 -5
  31. data/lib/gcloud/logging/resource.rb +1 -12
  32. data/lib/gcloud/logging/resource_descriptor.rb +9 -12
  33. data/lib/gcloud/logging/resource_descriptor/list.rb +4 -5
  34. data/lib/gcloud/logging/sink/list.rb +4 -5
  35. data/lib/gcloud/pubsub/message.rb +1 -3
  36. data/lib/gcloud/pubsub/subscription.rb +5 -7
  37. data/lib/gcloud/pubsub/topic.rb +1 -3
  38. data/lib/gcloud/resource_manager/project/list.rb +2 -4
  39. data/lib/gcloud/translate/api.rb +5 -3
  40. data/lib/gcloud/translate/connection.rb +4 -4
  41. data/lib/gcloud/version.rb +1 -1
  42. metadata +9 -20
  43. data/lib/gcloud/datastore/connection.rb +0 -203
  44. data/lib/gcloud/datastore/proto.rb +0 -266
  45. data/lib/gcloud/proto/datastore_v1.pb.rb +0 -377
  46. data/lib/google/protobuf/any.rb +0 -17
  47. data/lib/google/protobuf/api.rb +0 -31
  48. data/lib/google/protobuf/duration.rb +0 -17
  49. data/lib/google/protobuf/empty.rb +0 -15
  50. data/lib/google/protobuf/field_mask.rb +0 -16
  51. data/lib/google/protobuf/source_context.rb +0 -16
  52. data/lib/google/protobuf/struct.rb +0 -35
  53. data/lib/google/protobuf/timestamp.rb +0 -17
  54. data/lib/google/protobuf/type.rb +0 -79
  55. data/lib/google/protobuf/wrappers.rb +0 -48
@@ -71,6 +71,17 @@ module Gcloud
71
71
  end
72
72
  alias_method :to_hash, :to_h
73
73
 
74
+ def to_grpc
75
+ Hash[@hash.map { |(k, v)| [k.to_s, GRPCUtils.to_value(v)] }]
76
+ end
77
+
78
+ def self.from_grpc grpc_map
79
+ # For some reason Google::Protobuf::Map#map isn't returning the value.
80
+ # It returns nil every time. COnvert to Hash to get actual objects.
81
+ grpc_hash = GRPCUtils.map_to_hash grpc_map
82
+ new Hash[grpc_hash.map { |(k, v)| [k.to_s, GRPCUtils.from_value(v)] }]
83
+ end
84
+
74
85
  protected
75
86
 
76
87
  ##
@@ -82,7 +93,7 @@ module Gcloud
82
93
  end
83
94
 
84
95
  # rubocop:disable all
85
- # Disabled rubocop because this needs to match Proto.to_proto_value
96
+ # Disabled rubocop because this needs to match GRPCUtils.to_value
86
97
 
87
98
  ##
88
99
  # Ensures the value is a type that can be persisted,
@@ -100,6 +111,8 @@ module Gcloud
100
111
  return value
101
112
  elsif value.respond_to?(:to_time)
102
113
  return value
114
+ elsif value.respond_to?(:to_hash) && value.keys.sort == [:latitude, :longitude]
115
+ return value
103
116
  elsif value.respond_to?(:read) && value.respond_to?(:rewind)
104
117
  # Always convert an IO object to a StringIO when storing.
105
118
  value.rewind
@@ -15,7 +15,6 @@
15
15
 
16
16
  require "gcloud/datastore/entity"
17
17
  require "gcloud/datastore/key"
18
- require "gcloud/datastore/proto"
19
18
 
20
19
  module Gcloud
21
20
  module Datastore
@@ -24,12 +23,19 @@ module Gcloud
24
23
  #
25
24
  # Represents the search criteria against a Datastore.
26
25
  #
26
+ # @see https://cloud.google.com/datastore/docs/concepts/queries Datastore
27
+ # Queries
28
+ # @see https://cloud.google.com/datastore/docs/concepts/metadataqueries
29
+ # Datastore Metadata
30
+ #
27
31
  # @example
28
32
  # query = Gcloud::Datastore::Query.new
29
33
  # query.kind("Task").
30
- # where("completed", "=", true)
34
+ # where("done", "=", false).
35
+ # where("priority", ">=", 4).
36
+ # order("priority", :desc)
31
37
  #
32
- # entities = dataset.run query
38
+ # tasks = datastore.run query
33
39
  #
34
40
  class Query
35
41
  ##
@@ -39,22 +45,28 @@ module Gcloud
39
45
  # query = Gcloud::Datastore::Query.new
40
46
  #
41
47
  def initialize
42
- @_query = Proto::Query.new
48
+ @grpc = Google::Datastore::V1beta3::Query.new
43
49
  end
44
50
 
45
51
  ##
46
52
  # Add the kind of entities to query.
47
53
  #
54
+ # Special entity kinds such as `__namespace__`, `__kind__`, and
55
+ # `__property__` can be used for [metadata
56
+ # queries](https://cloud.google.com/datastore/docs/concepts/metadataqueries).
57
+ #
48
58
  # @example
49
59
  # query = Gcloud::Datastore::Query.new
50
60
  # query.kind "Task"
51
61
  #
52
- # all_tasks = dataset.run query
62
+ # tasks = datastore.run query
53
63
  #
54
64
  def kind *kinds
55
- @_query.kind ||= Proto::KindExpression.new
56
- @_query.kind.name ||= []
57
- @_query.kind.name |= kinds
65
+ kinds.each do |kind|
66
+ grpc_kind = Google::Datastore::V1beta3::KindExpression.new(name: kind)
67
+ @grpc.kind << grpc_kind
68
+ end
69
+
58
70
  self
59
71
  end
60
72
 
@@ -64,21 +76,72 @@ module Gcloud
64
76
  # @example
65
77
  # query = Gcloud::Datastore::Query.new
66
78
  # query.kind("Task").
67
- # where("completed", "=", true)
79
+ # where("done", "=", false)
80
+ #
81
+ # tasks = datastore.run query
82
+ #
83
+ # @example Add a composite property filter:
84
+ # query = Gcloud::Datastore::Query.new
85
+ # query.kind("Task").
86
+ # where("done", "=", false).
87
+ # where("priority", ">=", 4)
88
+ #
89
+ # tasks = datastore.run query
90
+ #
91
+ # @example Add an inequality filter on a **single** property only:
92
+ # query = Gcloud::Datastore::Query.new
93
+ # query.kind("Task").
94
+ # where("created", ">=", Time.utc(1990, 1, 1)).
95
+ # where("created", "<", Time.utc(2000, 1, 1))
96
+ #
97
+ # tasks = datastore.run query
98
+ #
99
+ # @example Add a composite filter on an array property:
100
+ # query = Gcloud::Datastore::Query.new
101
+ # query.kind("Task").
102
+ # where("tag", "=", "fun").
103
+ # where("tag", "=", "programming")
68
104
  #
69
- # completed_tasks = dataset.run query
105
+ # tasks = datastore.run query
106
+ #
107
+ # @example Add an inequality filter on an array property :
108
+ # query = Gcloud::Datastore::Query.new
109
+ # query.kind("Task").
110
+ # where("tag", ">", "learn").
111
+ # where("tag", "<", "math")
112
+ #
113
+ # tasks = datastore.run query
114
+ #
115
+ # @example Add a key filter using the special property `__key__`:
116
+ # query = Gcloud::Datastore::Query.new
117
+ # query.kind("Task").
118
+ # where("__key__", ">", datastore.key("Task", "someTask"))
119
+ #
120
+ # tasks = datastore.run query
121
+ #
122
+ # @example Add a key filter to a *kindless* query:
123
+ # last_seen_key = datastore.key "Task", "a"
124
+ # query = Gcloud::Datastore::Query.new
125
+ # query.where("__key__", ">", last_seen_key)
126
+ #
127
+ # tasks = datastore.run query
70
128
  #
71
129
  def where name, operator, value
72
- # Initialize filter
73
- @_query.filter ||= Proto.new_filter.tap do |f|
74
- f.composite_filter = Proto.new_composite_filter
75
- end
76
- # Create new property filter
77
- filter = Proto.new_filter.tap do |f|
78
- f.property_filter = Proto.new_property_filter name, operator, value
79
- end
80
- # Add new property filter to the list
81
- @_query.filter.composite_filter.filter << filter
130
+ @grpc.filter ||= Google::Datastore::V1beta3::Filter.new(
131
+ composite_filter: Google::Datastore::V1beta3::CompositeFilter.new(
132
+ op: :AND
133
+ )
134
+ )
135
+ @grpc.filter.composite_filter.filters << \
136
+ Google::Datastore::V1beta3::Filter.new(
137
+ property_filter: Google::Datastore::V1beta3::PropertyFilter.new(
138
+ property: Google::Datastore::V1beta3::PropertyReference.new(
139
+ name: name),
140
+ op: GRPCUtils.to_prop_filter_op(operator),
141
+ value: GRPCUtils.to_value(value)
142
+ )
143
+ )
144
+
82
145
  self
83
146
  end
84
147
  alias_method :filter, :where
@@ -87,11 +150,13 @@ module Gcloud
87
150
  # Add a filter for entities that inherit from a key.
88
151
  #
89
152
  # @example
153
+ # task_list_key = datastore.key "TaskList", "default"
154
+ #
90
155
  # query = Gcloud::Datastore::Query.new
91
156
  # query.kind("Task").
92
- # ancestor(parent.key)
157
+ # ancestor(task_list_key)
93
158
  #
94
- # completed_tasks = dataset.run query
159
+ # tasks = datastore.run query
95
160
  #
96
161
  def ancestor parent
97
162
  # Use key if given an entity
@@ -105,20 +170,44 @@ module Gcloud
105
170
  # To sort in descending order, provide a second argument
106
171
  # of a string or symbol that starts with "d".
107
172
  #
108
- # @example
173
+ # @example With ascending sort order:
174
+ # query = Gcloud::Datastore::Query.new
175
+ # query.kind("Task").
176
+ # order("created")
177
+ #
178
+ # tasks = datastore.run query
179
+ #
180
+ # @example With descending sort order:
181
+ # query = Gcloud::Datastore::Query.new
182
+ # query.kind("Task").
183
+ # order("created", :desc)
184
+ #
185
+ # tasks = datastore.run query
186
+ #
187
+ # @example With multiple sort orders:
188
+ # query = Gcloud::Datastore::Query.new
189
+ # query.kind("Task").
190
+ # order("priority", :desc).
191
+ # order("created")
192
+ #
193
+ # tasks = datastore.run query
194
+ #
195
+ # @example A property used in an inequality filter must be ordered first:
109
196
  # query = Gcloud::Datastore::Query.new
110
197
  # query.kind("Task").
111
- # order("due", :desc)
198
+ # where("priority", ">", 3).
199
+ # order("priority").
200
+ # order("created")
112
201
  #
113
- # sorted_tasks = dataset.run query
202
+ # tasks = datastore.run query
114
203
  #
115
204
  def order name, direction = :asc
116
- @_query.order ||= []
117
- po = Proto::PropertyOrder.new
118
- po.property = Proto::PropertyReference.new
119
- po.property.name = name
120
- po.direction = Proto.to_prop_order_direction direction
121
- @_query.order << po
205
+ @grpc.order << Google::Datastore::V1beta3::PropertyOrder.new(
206
+ property: Google::Datastore::V1beta3::PropertyReference.new(
207
+ name: name),
208
+ direction: prop_order_direction(direction)
209
+ )
210
+
122
211
  self
123
212
  end
124
213
 
@@ -128,12 +217,13 @@ module Gcloud
128
217
  # @example
129
218
  # query = Gcloud::Datastore::Query.new
130
219
  # query.kind("Task").
131
- # limit(10)
220
+ # limit(5)
132
221
  #
133
- # paginated_tasks = dataset.run query
222
+ # tasks = datastore.run query
134
223
  #
135
224
  def limit num
136
- @_query.limit = num
225
+ @grpc.limit = Google::Protobuf::Int32Value.new(value: num)
226
+
137
227
  self
138
228
  end
139
229
 
@@ -143,13 +233,14 @@ module Gcloud
143
233
  # @example
144
234
  # query = Gcloud::Datastore::Query.new
145
235
  # query.kind("Task").
146
- # limit(10).
147
- # offset(20)
236
+ # limit(5).
237
+ # offset(10)
148
238
  #
149
- # paginated_tasks = dataset.run query
239
+ # tasks = datastore.run query
150
240
  #
151
241
  def offset num
152
- @_query.offset = num
242
+ @grpc.offset = num
243
+
153
244
  self
154
245
  end
155
246
 
@@ -159,13 +250,20 @@ module Gcloud
159
250
  # @example
160
251
  # query = Gcloud::Datastore::Query.new
161
252
  # query.kind("Task").
162
- # limit(10).
163
- # cursor(task_cursor)
253
+ # limit(page_size).
254
+ # start(page_cursor)
164
255
  #
165
- # paginated_tasks = dataset.run query
256
+ # tasks = datastore.run query
166
257
  #
167
258
  def start cursor
168
- @_query.start_cursor = Proto.decode_cursor cursor
259
+ if cursor.is_a? Cursor
260
+ @grpc.start_cursor = cursor.to_grpc
261
+ elsif cursor.is_a? String
262
+ @grpc.start_cursor = GRPCUtils.decode_bytes cursor
263
+ else
264
+ fail ArgumentError, "Can't set a cursor using a #{cursor.class}."
265
+ end
266
+
169
267
  self
170
268
  end
171
269
  alias_method :cursor, :start
@@ -176,13 +274,30 @@ module Gcloud
176
274
  # @example
177
275
  # query = Gcloud::Datastore::Query.new
178
276
  # query.kind("Task").
179
- # select("completed", "due")
277
+ # select("priority", "percent_complete")
278
+ #
279
+ # priorities = []
280
+ # percent_completes = []
281
+ # datastore.run(query).each do |task|
282
+ # priorities << task["priority"]
283
+ # percent_completes << task["percent_complete"]
284
+ # end
285
+ #
286
+ # @example A keys-only query using the special property `__key__`:
287
+ # query = Gcloud::Datastore::Query.new
288
+ # query.kind("Task").
289
+ # select("__key__")
180
290
  #
181
- # partial_tasks = dataset.run query
291
+ # keys = datastore.run(query).map(&:key)
182
292
  #
183
293
  def select *names
184
- @_query.projection ||= []
185
- @_query.projection += Proto.new_property_expressions(*names)
294
+ names.each do |name|
295
+ grpc_projection = Google::Datastore::V1beta3::Projection.new(
296
+ property: Google::Datastore::V1beta3::PropertyReference.new(
297
+ name: name))
298
+ @grpc.projection << grpc_projection
299
+ end
300
+
186
301
  self
187
302
  end
188
303
  alias_method :projection, :select
@@ -193,19 +308,40 @@ module Gcloud
193
308
  # @example
194
309
  # query = Gcloud::Datastore::Query.new
195
310
  # query.kind("Task").
196
- # group_by("completed")
311
+ # distinct_on("type", "priority").
312
+ # order("type").
313
+ # order("priority")
197
314
  #
198
- # grouped_tasks = dataset.run query
315
+ # tasks = datastore.run query
199
316
  #
200
317
  def group_by *names
201
- @_query.group_by ||= []
202
- @_query.group_by += Proto.new_property_references(*names)
318
+ names.each do |name|
319
+ grpc_property = Google::Datastore::V1beta3::PropertyReference.new(
320
+ name: name)
321
+ @grpc.distinct_on << grpc_property
322
+ end
323
+
203
324
  self
204
325
  end
326
+ alias_method :distinct_on, :group_by
205
327
 
206
328
  # @private
207
- def to_proto
208
- @_query
329
+ def to_grpc
330
+ @grpc
331
+ end
332
+
333
+ protected
334
+
335
+ ##
336
+ # @private Get the property order direction for a string.
337
+ def prop_order_direction direction
338
+ if direction.to_s.downcase.start_with? "a"
339
+ :ASCENDING
340
+ elsif direction.to_s.downcase.start_with? "d"
341
+ :DESCENDING
342
+ else
343
+ :DIRECTION_UNSPECIFIED
344
+ end
209
345
  end
210
346
  end
211
347
  end
@@ -0,0 +1,161 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
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
+ # http://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 "gcloud/datastore/credentials"
17
+ require "google/datastore/v1beta3/datastore_services"
18
+ require "gcloud/backoff"
19
+
20
+ module Gcloud
21
+ module Datastore
22
+ ##
23
+ # @private Represents the gRPC Datastore service, including all the API
24
+ # methods.
25
+ class Service
26
+ attr_accessor :project, :credentials, :host
27
+
28
+ ##
29
+ # Creates a new Service instance.
30
+ def initialize project, credentials
31
+ @project = project
32
+ @credentials = credentials
33
+ @host = "datastore.googleapis.com"
34
+ end
35
+
36
+ def creds
37
+ return credentials if insecure?
38
+ GRPC::Core::ChannelCredentials.new.compose \
39
+ GRPC::Core::CallCredentials.new credentials.client.updater_proc
40
+ end
41
+
42
+ def datastore
43
+ return mocked_datastore if mocked_datastore
44
+ @datastore ||= Google::Datastore::V1beta3::Datastore::Stub.new(
45
+ host, creds)
46
+ end
47
+ attr_accessor :mocked_datastore
48
+
49
+ def insecure?
50
+ credentials == :this_channel_is_insecure
51
+ end
52
+
53
+ ##
54
+ # Allocate IDs for incomplete keys.
55
+ # (This is useful for referencing an entity before it is inserted.)
56
+ def allocate_ids *incomplete_keys
57
+ allocate_req = Google::Datastore::V1beta3::AllocateIdsRequest.new(
58
+ project_id: project,
59
+ keys: incomplete_keys
60
+ )
61
+
62
+ backoff { datastore.allocate_ids allocate_req }
63
+ end
64
+
65
+ ##
66
+ # Look up entities by keys.
67
+ def lookup *keys, consistency: nil, transaction: nil
68
+ lookup_req = Google::Datastore::V1beta3::LookupRequest.new(
69
+ project_id: project,
70
+ keys: keys
71
+ )
72
+ lookup_req.read_options = generate_read_options consistency, transaction
73
+
74
+ backoff { datastore.lookup lookup_req }
75
+ end
76
+
77
+ # Query for entities.
78
+ def run_query query, namespace = nil, consistency: nil, transaction: nil
79
+ run_req = Google::Datastore::V1beta3::RunQueryRequest.new(
80
+ project_id: project)
81
+ if query.is_a? Google::Datastore::V1beta3::Query
82
+ run_req["query"] = query
83
+ elsif query.is_a? Google::Datastore::V1beta3::GqlQuery
84
+ run_req["gql_query"] = query
85
+ else
86
+ fail ArgumentError, "Unable to query with a #{query.class} object."
87
+ end
88
+ run_req.read_options = generate_read_options consistency, transaction
89
+
90
+ run_req.partition_id = Google::Datastore::V1beta3::PartitionId.new(
91
+ namespace_id: namespace) if namespace
92
+
93
+ backoff { datastore.run_query run_req }
94
+ end
95
+
96
+ ##
97
+ # Begin a new transaction.
98
+ def begin_transaction
99
+ tx_req = Google::Datastore::V1beta3::BeginTransactionRequest.new(
100
+ project_id: project
101
+ )
102
+
103
+ backoff { datastore.begin_transaction tx_req }
104
+ end
105
+
106
+ ##
107
+ # Commit a transaction, optionally creating, deleting or modifying
108
+ # some entities.
109
+ def commit mutations, transaction: nil
110
+ commit_req = Google::Datastore::V1beta3::CommitRequest.new(
111
+ project_id: project,
112
+ mode: :NON_TRANSACTIONAL,
113
+ mutations: mutations
114
+ )
115
+ if transaction
116
+ commit_req.mode = :TRANSACTIONAL
117
+ commit_req.transaction = transaction
118
+ end
119
+
120
+ backoff { datastore.commit commit_req }
121
+ end
122
+
123
+ ##
124
+ # Roll back a transaction.
125
+ def rollback transaction
126
+ rb_req = Google::Datastore::V1beta3::RollbackRequest.new(
127
+ project_id: project,
128
+ transaction: transaction
129
+ )
130
+
131
+ backoff { datastore.rollback rb_req }
132
+ end
133
+
134
+ def inspect
135
+ "#{self.class}(#{@project})"
136
+ end
137
+
138
+ def backoff options = {}
139
+ Gcloud::Backoff.new(options).execute_grpc do
140
+ yield
141
+ end
142
+ end
143
+
144
+ protected
145
+
146
+ def generate_read_options consistency, transaction
147
+ if consistency == :eventual
148
+ return Google::Datastore::V1beta3::ReadOptions.new(
149
+ read_consistency: :EVENTUAL)
150
+ elsif consistency == :strong
151
+ return Google::Datastore::V1beta3::ReadOptions.new(
152
+ read_consistency: :STRONG)
153
+ elsif transaction
154
+ return Google::Datastore::V1beta3::ReadOptions.new(
155
+ transaction: transaction)
156
+ end
157
+ nil
158
+ end
159
+ end
160
+ end
161
+ end