google-cloud-datastore 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.
@@ -0,0 +1,43 @@
1
+ # Copyright 2014 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 "google/cloud/errors"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Datastore
21
+ ##
22
+ # # KeyError
23
+ #
24
+ # Raised when a key is not correct.
25
+ class KeyError < Google::Cloud::Error
26
+ end
27
+
28
+ ##
29
+ # # PropertyError
30
+ #
31
+ # Raised when a property is not correct.
32
+ class PropertyError < Google::Cloud::Error
33
+ end
34
+
35
+ ##
36
+ # # TransactionError
37
+ #
38
+ # General error for Transaction problems.
39
+ class TransactionError < Google::Cloud::Error
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,216 @@
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 "google/cloud/datastore/entity"
17
+ require "google/cloud/datastore/key"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Datastore
22
+ ##
23
+ # # GqlQuery
24
+ #
25
+ # Represents a GQL query.
26
+ #
27
+ # GQL is a SQL-like language for retrieving entities or keys from
28
+ # Datastore.
29
+ #
30
+ # @see https://cloud.google.com/datastore/docs/apis/gql/gql_reference GQL
31
+ # Reference
32
+ #
33
+ # @example
34
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
35
+ # gql_query.query_string = "SELECT * FROM Task ORDER BY created ASC"
36
+ # tasks = datastore.run gql_query
37
+ #
38
+ class GqlQuery
39
+ ##
40
+ # Returns a new GqlQuery instance.
41
+ #
42
+ # @example
43
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
44
+ #
45
+ def initialize
46
+ @grpc = Google::Datastore::V1::GqlQuery.new
47
+ end
48
+
49
+ ##
50
+ # The GQL query string for the query. The string may contain named or
51
+ # positional argument binding sites that start with `@`. Corresponding
52
+ # binding values should be set with {#named_bindings=} or
53
+ # {#positional_bindings=}.
54
+ #
55
+ # @return [String] a GQL statement
56
+ #
57
+ def query_string
58
+ gql = @grpc.query_string.dup
59
+ gql.freeze
60
+ gql
61
+ end
62
+
63
+ ##
64
+ # Sets the GQL query string for the query. The string may contain named
65
+ # or positional argument binding sites that start with `@`.
66
+ # Corresponding binding values should be set with {#named_bindings=} or
67
+ # {#positional_bindings=}.
68
+ #
69
+ # See the [GQL
70
+ # Reference](https://cloud.google.com/datastore/docs/apis/gql/gql_reference).
71
+ #
72
+ # @param [String] new_query_string a valid GQL statement
73
+ #
74
+ # @example
75
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
76
+ # gql_query.query_string = "SELECT * FROM Task " \
77
+ # "WHERE done = @done " \
78
+ # "AND priority = @priority"
79
+ # gql_query.named_bindings = {done: false, priority: 4}
80
+ #
81
+ def query_string= new_query_string
82
+ @grpc.query_string = new_query_string.to_s
83
+ end
84
+
85
+ ##
86
+ # Whether the query may contain literal values. When false, the query
87
+ # string must not contain any literals and instead must bind all values
88
+ # using {#named_bindings=} or {#positional_bindings=}.
89
+ #
90
+ # @return [Boolean] `true` if the query may contain literal values
91
+ #
92
+ def allow_literals
93
+ @grpc.allow_literals
94
+ end
95
+
96
+ ##
97
+ # Sets whether the query may contain literal values. When false, the
98
+ # query string must not contain any literals and instead must bind all
99
+ # values using {#named_bindings=} or {#positional_bindings=}.
100
+ #
101
+ # @param [Boolean] new_allow_literals `true` if the query may contain
102
+ # literal values
103
+ #
104
+ # @example
105
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
106
+ # gql_query.query_string = "SELECT * FROM Task " \
107
+ # "WHERE completed = false AND priority = 4"
108
+ # gql_query.allow_literals = true
109
+ #
110
+ def allow_literals= new_allow_literals
111
+ @grpc.allow_literals = new_allow_literals
112
+ end
113
+
114
+ ##
115
+ # The named binding values for a query that contains named argument
116
+ # binding sites that start with `@`.
117
+ #
118
+ # @return [Hash] a frozen hash that maps the binding site names in the
119
+ # query string to valid GQL arguments
120
+ #
121
+ def named_bindings
122
+ bindings = Hash[@grpc.named_bindings.map do |name, gql_query_param|
123
+ if gql_query_param.parameter_type == :cursor
124
+ [name, Cursor.from_grpc(gql_query_param.cursor)]
125
+ else
126
+ [name, Core::GRPCUtils.from_value(gql_query_param.value)]
127
+ end
128
+ end]
129
+ bindings.freeze
130
+ bindings
131
+ end
132
+
133
+ ##
134
+ # Sets named binding values for a query that contains named argument
135
+ # binding sites that start with `@`.
136
+ #
137
+ # @param [Hash] new_named_bindings a hash that maps the binding site
138
+ # names in the query string to valid GQL arguments
139
+ #
140
+ # @example
141
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
142
+ # gql_query.query_string = "SELECT * FROM Task " \
143
+ # "WHERE done = @done " \
144
+ # "AND priority = @priority"
145
+ # gql_query.named_bindings = {done: false, priority: 4}
146
+ #
147
+ def named_bindings= new_named_bindings
148
+ @grpc.named_bindings.clear
149
+ new_named_bindings.map do |name, value|
150
+ if value.is_a? Google::Cloud::Datastore::Cursor
151
+ @grpc.named_bindings[name.to_s] = \
152
+ Google::Datastore::V1::GqlQueryParameter.new(
153
+ cursor: value.to_grpc)
154
+ else
155
+ @grpc.named_bindings[name.to_s] = \
156
+ Google::Datastore::V1::GqlQueryParameter.new(
157
+ value: Core::GRPCUtils.to_value(value))
158
+ end
159
+ end
160
+ end
161
+
162
+ ##
163
+ # The binding values for a query that contains numbered argument binding
164
+ # sites that start with `@`.
165
+ #
166
+ # @return [Array] a frozen array containing the query arguments in the
167
+ # order of the numbered binding sites in the query string
168
+ #
169
+ def positional_bindings
170
+ bindings = @grpc.positional_bindings.map do |gql_query_param|
171
+ if gql_query_param.parameter_type == :cursor
172
+ Cursor.from_grpc gql_query_param.cursor
173
+ else
174
+ Core::GRPCUtils.from_value gql_query_param.value
175
+ end
176
+ end
177
+ bindings.freeze
178
+ bindings
179
+ end
180
+
181
+ ##
182
+ # Sets the binding values for a query that contains numbered argument
183
+ # binding sites that start with `@`.
184
+ #
185
+ # @param [Array] new_positional_bindings query arguments in the order
186
+ # of the numbered binding sites in the query string
187
+ #
188
+ # @example
189
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
190
+ # gql_query.query_string = "SELECT * FROM Task" \
191
+ # "WHERE completed = @1 AND priority = @2"
192
+ # gql_query.positional_bindings = [false, 4]
193
+ #
194
+ def positional_bindings= new_positional_bindings
195
+ @grpc.positional_bindings.clear
196
+ new_positional_bindings.map do |value|
197
+ if value.is_a? Google::Cloud::Datastore::Cursor
198
+ @grpc.positional_bindings << \
199
+ Google::Datastore::V1::GqlQueryParameter.new(
200
+ cursor: value.to_grpc)
201
+ else
202
+ @grpc.positional_bindings << \
203
+ Google::Datastore::V1::GqlQueryParameter.new(
204
+ value: Core::GRPCUtils.to_value(value))
205
+ end
206
+ end
207
+ end
208
+
209
+ # @private
210
+ def to_grpc
211
+ @grpc
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,140 @@
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 "google/cloud/core/grpc_utils"
17
+ require "google/cloud/datastore/errors"
18
+ require "stringio"
19
+ require "base64"
20
+
21
+ module Google
22
+ module Cloud
23
+ module Core
24
+ ##
25
+ # @private Conversion to/from Datastore GRPC objects.
26
+ # This file adds Datastore methods to Core::GRPCUtils.
27
+ module GRPCUtils
28
+ # rubocop:disable all
29
+
30
+ PROP_FILTER_OPS = { "<" => :LESS_THAN,
31
+ "lt" => :LESS_THAN,
32
+ "<=" => :LESS_THAN_OR_EQUAL,
33
+ "lte" => :LESS_THAN_OR_EQUAL,
34
+ ">" => :GREATER_THAN,
35
+ "gt" => :GREATER_THAN,
36
+ ">=" => :GREATER_THAN_OR_EQUAL,
37
+ "gte" => :GREATER_THAN_OR_EQUAL,
38
+ "=" => :EQUAL,
39
+ "eq" => :EQUAL,
40
+ "eql" => :EQUAL,
41
+ "~" => :HAS_ANCESTOR,
42
+ "~>" => :HAS_ANCESTOR,
43
+ "ancestor" => :HAS_ANCESTOR,
44
+ "has_ancestor" => :HAS_ANCESTOR,
45
+ "has ancestor" => :HAS_ANCESTOR }
46
+
47
+ ##
48
+ # Get a property filter operator from op
49
+ def self.to_prop_filter_op op
50
+ PROP_FILTER_OPS[op.to_s.downcase] || :EQUAL
51
+ end
52
+
53
+ ##
54
+ # Gets an object from a Google::Datastore::V1::Value.
55
+ def self.from_value grpc_value
56
+ if grpc_value.value_type == :null_value
57
+ return nil
58
+ elsif grpc_value.value_type == :key_value
59
+ return Google::Cloud::Datastore::Key.from_grpc(grpc_value.key_value)
60
+ elsif grpc_value.value_type == :entity_value
61
+ return Google::Cloud::Datastore::Entity.from_grpc(
62
+ grpc_value.entity_value)
63
+ elsif grpc_value.value_type == :boolean_value
64
+ return grpc_value.boolean_value
65
+ elsif grpc_value.value_type == :double_value
66
+ return grpc_value.double_value
67
+ elsif grpc_value.value_type == :integer_value
68
+ return grpc_value.integer_value
69
+ elsif grpc_value.value_type == :string_value
70
+ return grpc_value.string_value
71
+ elsif grpc_value.value_type == :array_value
72
+ return Array(grpc_value.array_value.values).map { |v| from_value v }
73
+ elsif grpc_value.value_type == :timestamp_value
74
+ return Time.at grpc_value.timestamp_value.seconds,
75
+ grpc_value.timestamp_value.nanos/1000.0
76
+ elsif grpc_value.value_type == :geo_point_value
77
+ return grpc_value.geo_point_value.to_hash
78
+ elsif grpc_value.value_type == :blob_value
79
+ return StringIO.new(
80
+ grpc_value.blob_value.dup.force_encoding("ASCII-8BIT"))
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ ##
87
+ # Stores an object into a Google::Datastore::V1::Value.
88
+ def self.to_value value
89
+ v = Google::Datastore::V1::Value.new
90
+ if NilClass === value
91
+ v.null_value = :NULL_VALUE
92
+ elsif TrueClass === value
93
+ v.boolean_value = true
94
+ elsif FalseClass === value
95
+ v.boolean_value = false
96
+ elsif Integer === value
97
+ v.integer_value = value
98
+ elsif Float === value
99
+ v.double_value = value
100
+ elsif defined?(BigDecimal) && BigDecimal === value
101
+ v.double_value = value
102
+ elsif Google::Cloud::Datastore::Key === value
103
+ v.key_value = value.to_grpc
104
+ elsif Google::Cloud::Datastore::Entity === value
105
+ value.key = nil # Embedded entities can't have keys
106
+ v.entity_value = value.to_grpc
107
+ elsif String === value
108
+ v.string_value = value
109
+ elsif Array === value
110
+ v.array_value = Google::Datastore::V1::ArrayValue.new(
111
+ values: value.map { |val| to_value val }
112
+ )
113
+ elsif value.respond_to? :to_time
114
+ v.timestamp_value = Google::Protobuf::Timestamp.new(
115
+ seconds: value.to_time.to_i, nanos: value.to_time.nsec)
116
+ elsif value.respond_to?(:to_hash) &&
117
+ value.keys.sort == [:latitude, :longitude]
118
+ v.geo_point_value = Google::Type::LatLng.new(value)
119
+ elsif value.respond_to?(:read) && value.respond_to?(:rewind)
120
+ value.rewind
121
+ v.blob_value = value.read.force_encoding("ASCII-8BIT")
122
+ else
123
+ fail Google::Cloud::Datastore::PropertyError,
124
+ "A property of type #{value.class} is not supported."
125
+ end
126
+ v
127
+ end
128
+
129
+ def self.encode_bytes bytes
130
+ Base64.strict_encode64(bytes.to_s).encode("ASCII-8BIT")
131
+ end
132
+
133
+ def self.decode_bytes bytes
134
+ Base64.decode64(bytes.to_s).force_encoding Encoding::ASCII_8BIT
135
+ end
136
+ # rubocop:enable all
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,289 @@
1
+ # Copyright 2014 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
+ module Google
17
+ module Cloud
18
+ module Datastore
19
+ ##
20
+ # # Key
21
+ #
22
+ # Every Datastore record has an identifying key, which includes the
23
+ # record's entity kind and a unique identifier. The identifier may be
24
+ # either a key name string, assigned explicitly by the application, or an
25
+ # integer numeric ID, assigned automatically by Datastore.
26
+ #
27
+ # @example
28
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
29
+ #
30
+ class Key
31
+ ##
32
+ # The kind of the Key.
33
+ #
34
+ # @return [String]
35
+ #
36
+ # @example
37
+ # key = Google::Cloud::Datastore::Key.new "TaskList"
38
+ # key.kind #=> "TaskList"
39
+ # key.kind = "Task"
40
+ #
41
+ attr_accessor :kind
42
+
43
+ ##
44
+ # The project of the Key.
45
+ #
46
+ # @return [String]
47
+ #
48
+ # @example
49
+ # require "google/cloud"
50
+ #
51
+ # gcloud = Google::Cloud.new "my-todo-project",
52
+ # "/path/to/keyfile.json"
53
+ #
54
+ # datastore = gcloud.datastore
55
+ # task = datastore.find "Task", "sampleTask"
56
+ # task.key.project #=> "my-todo-project"
57
+ #
58
+ attr_accessor :project
59
+ alias_method :dataset_id, :project
60
+ alias_method :dataset_id=, :project=
61
+
62
+ ##
63
+ # The namespace of the Key.
64
+ #
65
+ # @return [String, nil]
66
+ #
67
+ # @example
68
+ # require "google/cloud"
69
+ #
70
+ # gcloud = Google::Cloud.new "my-todo-project",
71
+ # "/path/to/keyfile.json"
72
+ #
73
+ # datastore = gcloud.datastore
74
+ # task = datastore.find "Task", "sampleTask"
75
+ # task.key.namespace #=> "ns~todo-project"
76
+ #
77
+ attr_accessor :namespace
78
+
79
+ ##
80
+ # Create a new Key instance.
81
+ #
82
+ # @param [String] kind The kind of the Key. This is optional.
83
+ # @param [Integer, String] id_or_name The id or name of the Key. This is
84
+ # optional.
85
+ #
86
+ # @return [Google::Cloud::Datastore::Dataset::Key]
87
+ #
88
+ # @example
89
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
90
+ #
91
+ def initialize kind = nil, id_or_name = nil
92
+ @kind = kind
93
+ if id_or_name.is_a? Integer
94
+ @id = id_or_name
95
+ else
96
+ @name = id_or_name
97
+ end
98
+ end
99
+
100
+ ##
101
+ # @private Set the id of the Key.
102
+ # If a name is already present it will be removed.
103
+ #
104
+ # @return [Integer, nil]
105
+ #
106
+ # @example
107
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
108
+ # task_key.id #=> nil
109
+ # task_key.name #=> "sampleTask"
110
+ # task_key.id = 654321
111
+ # task_key.id #=> 654321
112
+ # task_key.name #=> nil
113
+ #
114
+ def id= new_id
115
+ @name = nil if new_id
116
+ @id = new_id
117
+ end
118
+
119
+ ##
120
+ # The id of the Key.
121
+ #
122
+ # @return [Integer, nil]
123
+ #
124
+ # @example
125
+ # task_key = Google::Cloud::Datastore::Key.new "Task", 123456
126
+ # task_key.id #=> 123456
127
+ #
128
+ attr_reader :id
129
+
130
+ ##
131
+ # @private Set the name of the Key.
132
+ # If an id is already present it will be removed.
133
+ #
134
+ # @return [String, nil]
135
+ #
136
+ # @example
137
+ # task_key = Google::Cloud::Datastore::Key.new "Task", 123456
138
+ # task_key.id #=> 123456
139
+ # task_key.name #=> nil
140
+ # task_key.name = "sampleTask"
141
+ # task_key.id #=> nil
142
+ # task_key.name #=> "sampleTask"
143
+ #
144
+ def name= new_name
145
+ @id = nil if new_name
146
+ @name = new_name
147
+ end
148
+
149
+ ##
150
+ # The name of the Key.
151
+ #
152
+ # @return [String, nil]
153
+ #
154
+ # @example
155
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
156
+ # task_key.name #=> "sampleTask"
157
+ #
158
+ attr_reader :name
159
+
160
+ ##
161
+ # Set the parent of the Key.
162
+ #
163
+ # @return [Key, nil]
164
+ #
165
+ # @example
166
+ # parent_key = Google::Cloud::Datastore::Key.new "TaskList", "default"
167
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
168
+ # task_key.parent = parent_key
169
+ #
170
+ # @example With multiple levels:
171
+ # user_key = Google::Cloud::Datastore::Key.new "User", "alice"
172
+ # list_key = Google::Cloud::Datastore::Key.new "TaskList", "default"
173
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
174
+ # list_key.parent = user_key
175
+ # task_key.parent = list_key
176
+ #
177
+ def parent= new_parent
178
+ # store key if given an entity
179
+ new_parent = new_parent.key if new_parent.respond_to? :key
180
+ @parent = new_parent
181
+ end
182
+
183
+ ##
184
+ # The parent of the Key.
185
+ #
186
+ # @return [Key, nil]
187
+ #
188
+ # @example
189
+ # require "google/cloud"
190
+ #
191
+ # gcloud = Google::Cloud.new
192
+ # datastore = gcloud.datastore
193
+ #
194
+ # task_list = datastore.find "TaskList", "default"
195
+ # query = datastore.query("Task").
196
+ # ancestor(task_list)
197
+ # lists = datastore.run query
198
+ # lists.first.key.parent #=> Key("TaskList", "default")
199
+ #
200
+ attr_reader :parent
201
+
202
+ ##
203
+ # Represent the Key's path (including parent) as an array of arrays.
204
+ # Each inner array contains two values, the kind and the id or name.
205
+ # If neither an id or name exist then nil will be returned.
206
+ #
207
+ # @return [Array<Array<(String, String)>>]
208
+ #
209
+ # @example
210
+ # parent_key = Google::Cloud::Datastore::Key.new "TaskList", "default"
211
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
212
+ # task_key.parent = parent_key
213
+ # task_key.path #=> [["TaskList", "default"], ["Task", "sampleTask"]]
214
+ #
215
+ def path
216
+ new_path = parent ? parent.path : []
217
+ new_path << [kind, (id || name)]
218
+ end
219
+
220
+ ##
221
+ # Determine if the key is complete.
222
+ # A complete key has either an id or a name.
223
+ #
224
+ # Inverse of {#incomplete?}
225
+ def complete?
226
+ !incomplete?
227
+ end
228
+
229
+ ##
230
+ # Determine if the key is incomplete.
231
+ # An incomplete key has neither an id nor a name.
232
+ #
233
+ # Inverse of {#complete?}
234
+ def incomplete?
235
+ kind.nil? || (id.nil? && (name.nil? || name.empty?))
236
+ end
237
+
238
+ ##
239
+ # The number of bytes the Key will take to serialize during API calls.
240
+ def serialized_size
241
+ to_grpc.to_proto.length
242
+ end
243
+
244
+ ##
245
+ # @private Convert the Key to a Google::Datastore::V1::Key object.
246
+ def to_grpc
247
+ grpc_path = path.map do |pe_kind, pe_id_or_name|
248
+ path_args = { kind: pe_kind }
249
+ if pe_id_or_name.is_a? Integer
250
+ path_args[:id] = pe_id_or_name
251
+ elsif pe_id_or_name.is_a? String
252
+ path_args[:name] = pe_id_or_name unless pe_id_or_name.empty?
253
+ end
254
+ Google::Datastore::V1::Key::PathElement.new(path_args)
255
+ end
256
+ grpc = Google::Datastore::V1::Key.new(path: grpc_path)
257
+ if project || namespace
258
+ grpc.partition_id = Google::Datastore::V1::PartitionId.new(
259
+ project_id: project.to_s, namespace_id: namespace.to_s)
260
+ end
261
+ grpc
262
+ end
263
+
264
+ ##
265
+ # @private Create a new Key from a Google::Datastore::V1::Key
266
+ # object.
267
+ def self.from_grpc grpc
268
+ return nil if grpc.nil?
269
+ key_grpc = grpc.dup
270
+ key = Key.new
271
+ path_grpc = key_grpc.path.pop
272
+ if path_grpc
273
+ id_or_name =
274
+ (path_grpc.id_type == :id ? path_grpc.id : path_grpc.name)
275
+ key = Key.new path_grpc.kind, id_or_name
276
+ end
277
+ if key_grpc.partition_id
278
+ key.project = key_grpc.partition_id.project_id
279
+ key.namespace = key_grpc.partition_id.namespace_id
280
+ end
281
+ key.parent = Key.from_grpc(key_grpc) if key_grpc.path.count > 0
282
+ # Freeze the key to make it immutable.
283
+ key.freeze
284
+ key
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end