OLD | NEW |
---|---|
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library gcloud.datastore_impl; | 5 library gcloud.datastore_impl; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import 'package:http/http.dart' as http; | 9 import 'package:http/http.dart' as http; |
10 | 10 |
11 import '../datastore.dart' as datastore; | 11 import '../datastore.dart' as datastore; |
12 import '../common.dart' show Page; | 12 import '../common.dart' show Page; |
13 import 'package:googleapis_beta/datastore/v1beta2.dart' as api; | 13 import 'package:googleapis/datastore/v1.dart' as api; |
14 | 14 |
15 class TransactionImpl implements datastore.Transaction { | 15 class TransactionImpl implements datastore.Transaction { |
16 final String data; | 16 final String data; |
17 TransactionImpl(this.data); | 17 TransactionImpl(this.data); |
18 } | 18 } |
19 | 19 |
20 class DatastoreImpl implements datastore.Datastore { | 20 class DatastoreImpl implements datastore.Datastore { |
21 static const List<String> SCOPES = const <String>[ | 21 static const List<String> SCOPES = const <String>[ |
22 api.DatastoreApi.DatastoreScope, | 22 api.DatastoreApi.DatastoreScope, |
23 api.DatastoreApi.UserinfoEmailScope, | 23 api.DatastoreApi.CloudPlatformScope, |
24 ]; | 24 ]; |
25 | 25 |
26 final api.DatastoreApi _api; | 26 final api.DatastoreApi _api; |
27 final String _project; | 27 final String _project; |
28 | 28 |
29 DatastoreImpl(http.Client client, this._project) | 29 /// The [project] parameter is the name of the cloud project (it should not |
30 : _api = new api.DatastoreApi(client); | 30 /// start with a `s~`). |
31 DatastoreImpl(http.Client client, String project) | |
32 : _api = new api.DatastoreApi(client), _project = project; | |
31 | 33 |
32 api.Key _convertDatastore2ApiKey(datastore.Key key, {bool enforceId: true}) { | 34 api.Key _convertDatastore2ApiKey(datastore.Key key, {bool enforceId: true}) { |
33 var apiKey = new api.Key(); | 35 var apiKey = new api.Key(); |
34 | 36 |
35 apiKey.partitionId = new api.PartitionId() | 37 apiKey.partitionId = new api.PartitionId() |
36 ..datasetId = _project | 38 ..projectId = _project |
37 ..namespace = key.partition.namespace; | 39 ..namespaceId = key.partition.namespace; |
38 | 40 |
39 apiKey.path = key.elements.map((datastore.KeyElement element) { | 41 apiKey.path = key.elements.map((datastore.KeyElement element) { |
40 var part = new api.KeyPathElement(); | 42 var part = new api.PathElement(); |
41 part.kind = element.kind; | 43 part.kind = element.kind; |
42 if (element.id is int) { | 44 if (element.id is int) { |
43 part.id = '${element.id}'; | 45 part.id = '${element.id}'; |
44 } else if (element.id is String) { | 46 } else if (element.id is String) { |
45 part.name = element.id; | 47 part.name = element.id; |
46 } else if (enforceId) { | 48 } else if (enforceId) { |
47 throw new datastore.ApplicationError( | 49 throw new datastore.ApplicationError( |
48 'Error while encoding entity key: Using `null` as the id is not ' | 50 'Error while encoding entity key: Using `null` as the id is not ' |
49 'allowed.'); | 51 'allowed.'); |
50 } | 52 } |
51 return part; | 53 return part; |
52 }).toList(); | 54 }).toList(); |
53 | 55 |
54 return apiKey; | 56 return apiKey; |
55 } | 57 } |
56 | 58 |
57 static datastore.Key _convertApi2DatastoreKey(api.Key key) { | 59 static datastore.Key _convertApi2DatastoreKey(api.Key key) { |
58 var elements = key.path.map((api.KeyPathElement element) { | 60 var elements = key.path.map((api.PathElement element) { |
59 if (element.id != null) { | 61 if (element.id != null) { |
60 return new datastore.KeyElement(element.kind, int.parse(element.id)); | 62 return new datastore.KeyElement(element.kind, int.parse(element.id)); |
61 } else if (element.name != null) { | 63 } else if (element.name != null) { |
62 return new datastore.KeyElement(element.kind, element.name); | 64 return new datastore.KeyElement(element.kind, element.name); |
63 } else { | 65 } else { |
64 throw new datastore.DatastoreError( | 66 throw new datastore.DatastoreError( |
65 'Invalid server response: Expected allocated name/id.'); | 67 'Invalid server response: Expected allocated name/id.'); |
66 } | 68 } |
67 }).toList(); | 69 }).toList(); |
68 | 70 |
69 var partition; | 71 var partition; |
70 if (key.partitionId != null) { | 72 if (key.partitionId != null) { |
71 partition = new datastore.Partition(key.partitionId.namespace); | 73 partition = new datastore.Partition(key.partitionId.namespaceId); |
72 // TODO: assert projectId. | 74 // TODO: assert projectId. |
73 } | 75 } |
74 return new datastore.Key(elements, partition: partition); | 76 return new datastore.Key(elements, partition: partition); |
75 } | 77 } |
76 | 78 |
77 bool _compareApiKey(api.Key a, api.Key b) { | 79 bool _compareApiKey(api.Key a, api.Key b) { |
78 if (a.path.length != b.path.length) return false; | 80 if (a.path.length != b.path.length) return false; |
79 | 81 |
80 // FIXME(Issue #2): Is this comparison working correctly? | 82 // FIXME(Issue #2): Is this comparison working correctly? |
81 if (a.partitionId != null) { | 83 if (a.partitionId != null) { |
82 if (b.partitionId == null) return false; | 84 if (b.partitionId == null) return false; |
83 if (a.partitionId.datasetId != b.partitionId.datasetId) return false; | 85 if (a.partitionId.projectId != b.partitionId.projectId) return false; |
84 if (a.partitionId.namespace != b.partitionId.namespace) return false; | 86 if (a.partitionId.namespaceId != b.partitionId.namespaceId) return false; |
85 } else { | 87 } else { |
86 if (b.partitionId != null) return false; | 88 if (b.partitionId != null) return false; |
87 } | 89 } |
88 | 90 |
89 for (int i = 0; i < a.path.length; i++) { | 91 for (int i = 0; i < a.path.length; i++) { |
90 if (a.path[i].id != b.path[i].id || | 92 if (a.path[i].id != b.path[i].id || |
91 a.path[i].name != b.path[i].name || | 93 a.path[i].name != b.path[i].name || |
92 a.path[i].kind != b.path[i].kind) return false; | 94 a.path[i].kind != b.path[i].kind) return false; |
93 } | 95 } |
94 return true; | 96 return true; |
95 } | 97 } |
96 | 98 |
97 static _convertApi2DatastorePropertyValue(api.Value value) { | |
98 if (value.booleanValue != null) | |
99 return value.booleanValue; | |
100 else if (value.integerValue != null) | |
101 return int.parse(value.integerValue); | |
102 else if (value.doubleValue != null) | |
103 return value.doubleValue; | |
104 else if (value.stringValue != null) | |
105 return value.stringValue; | |
106 else if (value.dateTimeValue != null) | |
107 return value.dateTimeValue; | |
108 else if (value.blobValue != null) | |
109 return new datastore.BlobValue(value.blobValueAsBytes); | |
110 else if (value.keyValue != null) | |
111 return _convertApi2DatastoreKey(value.keyValue); | |
112 else if (value.listValue != null) | |
113 // FIXME(Issue #3): Consistently handle exceptions. | |
114 throw new Exception('Cannot have lists inside lists.'); | |
115 else if (value.blobKeyValue != null) | |
116 throw new UnsupportedError('Blob keys are not supported.'); | |
117 else if (value.entityValue != null) | |
118 throw new UnsupportedError('Entity values are not supported.'); | |
119 return null; | |
120 } | |
121 | |
122 api.Value _convertDatastore2ApiPropertyValue( | 99 api.Value _convertDatastore2ApiPropertyValue( |
123 value, bool indexed, {bool lists: true}) { | 100 value, bool indexed, {bool lists: true}) { |
124 var apiValue = new api.Value() | 101 var apiValue = new api.Value() |
125 ..indexed = indexed; | 102 ..excludeFromIndexes = !indexed; |
126 if (value == null) { | 103 if (value == null) { |
127 return apiValue; | 104 return apiValue |
105 ..nullValue = "NULL_VALUE"; | |
Søren Gjesse
2016/08/29 10:29:09
How does this nullValue thing work? Is this requir
kustermann
2016/08/29 16:24:04
It is an enum and the enum values are ["NULL_VALUE
| |
128 } else if (value is bool) { | 106 } else if (value is bool) { |
129 return apiValue | 107 return apiValue |
130 ..booleanValue = value; | 108 ..booleanValue = value; |
131 } else if (value is int) { | 109 } else if (value is int) { |
132 return apiValue | 110 return apiValue |
133 ..integerValue = '$value'; | 111 ..integerValue = '$value'; |
134 } else if (value is double) { | 112 } else if (value is double) { |
135 return apiValue | 113 return apiValue |
136 ..doubleValue = value; | 114 ..doubleValue = value; |
137 } else if (value is String) { | 115 } else if (value is String) { |
138 return apiValue | 116 return apiValue |
139 ..stringValue = value; | 117 ..stringValue = value; |
140 } else if (value is DateTime) { | 118 } else if (value is DateTime) { |
141 return apiValue | 119 return apiValue |
142 ..dateTimeValue = value; | 120 ..timestampValue = value.toIso8601String(); |
143 } else if (value is datastore.BlobValue) { | 121 } else if (value is datastore.BlobValue) { |
144 return apiValue | 122 return apiValue |
145 ..blobValueAsBytes = value.bytes; | 123 ..blobValueAsBytes = value.bytes; |
146 } else if (value is datastore.Key) { | 124 } else if (value is datastore.Key) { |
147 return apiValue | 125 return apiValue |
148 ..keyValue = _convertDatastore2ApiKey(value, enforceId: false); | 126 ..keyValue = _convertDatastore2ApiKey(value, enforceId: false); |
149 } else if (value is List) { | 127 } else if (value is List) { |
150 if (!lists) { | 128 if (!lists) { |
151 // FIXME(Issue #3): Consistently handle exceptions. | 129 // FIXME(Issue #3): Consistently handle exceptions. |
152 throw new Exception('List values are not allowed.'); | 130 throw new Exception('List values are not allowed.'); |
153 } | 131 } |
154 | 132 |
155 convertItem(i) | 133 convertItem(i) |
156 => _convertDatastore2ApiPropertyValue(i, indexed, lists: false); | 134 => _convertDatastore2ApiPropertyValue(i, indexed, lists: false); |
157 | 135 |
158 return new api.Value() | 136 return new api.Value()..arrayValue = ( |
159 ..listValue = value.map(convertItem).toList(); | 137 new api.ArrayValue()..values = value.map(convertItem).toList()); |
160 } else { | 138 } else { |
161 throw new UnsupportedError( | 139 throw new UnsupportedError( |
162 'Types ${value.runtimeType} cannot be used for serializing.'); | 140 'Types ${value.runtimeType} cannot be used for serializing.'); |
163 } | 141 } |
164 } | 142 } |
165 | 143 |
166 static _convertApi2DatastoreProperty(api.Property property) { | 144 static _convertApi2DatastoreProperty(api.Value value) { |
Søren Gjesse
2016/08/29 10:29:09
Add explicit dynamic here?
kustermann
2016/08/29 16:24:04
done
| |
167 if (property.booleanValue != null) | 145 if (value.booleanValue != null) |
168 return property.booleanValue; | 146 return value.booleanValue; |
169 else if (property.integerValue != null) | 147 else if (value.integerValue != null) |
170 return int.parse(property.integerValue); | 148 return int.parse(value.integerValue); |
171 else if (property.doubleValue != null) | 149 else if (value.doubleValue != null) |
172 return property.doubleValue; | 150 return value.doubleValue; |
173 else if (property.stringValue != null) | 151 else if (value.stringValue != null) |
174 return property.stringValue; | 152 return value.stringValue; |
175 else if (property.dateTimeValue != null) | 153 else if (value.timestampValue != null) |
176 return property.dateTimeValue; | 154 return DateTime.parse(value.timestampValue); |
177 else if (property.blobValue != null) | 155 else if (value.blobValue != null) |
178 return new datastore.BlobValue(property.blobValueAsBytes); | 156 return new datastore.BlobValue(value.blobValueAsBytes); |
179 else if (property.keyValue != null) | 157 else if (value.keyValue != null) |
180 return _convertApi2DatastoreKey(property.keyValue); | 158 return _convertApi2DatastoreKey(value.keyValue); |
181 else if (property.listValue != null) | 159 else if (value.arrayValue != null && value.arrayValue.values != null) |
182 return | 160 return value |
183 property.listValue.map(_convertApi2DatastorePropertyValue).toList(); | 161 .arrayValue.values.map(_convertApi2DatastoreProperty).toList(); |
184 else if (property.blobKeyValue != null) | 162 else if (value.entityValue != null) |
Søren Gjesse
2016/08/29 10:29:09
Why is value.nullValue not handled here?
kustermann
2016/08/29 16:24:04
It was handled via the default case below, but I'v
| |
185 throw new UnsupportedError('Blob keys are not supported.'); | |
186 else if (property.entityValue != null) | |
187 throw new UnsupportedError('Entity values are not supported.'); | 163 throw new UnsupportedError('Entity values are not supported.'); |
188 return null; | 164 return null; |
189 } | 165 } |
190 | 166 |
191 api.Property _convertDatastore2ApiProperty( | |
192 value, bool indexed, {bool lists: true}) { | |
193 var apiProperty = new api.Property() | |
194 ..indexed = indexed; | |
195 if (value == null) { | |
196 return null; | |
197 } else if (value is bool) { | |
198 return apiProperty | |
199 ..booleanValue = value; | |
200 } else if (value is int) { | |
201 return apiProperty | |
202 ..integerValue = '$value'; | |
203 } else if (value is double) { | |
204 return apiProperty | |
205 ..doubleValue = value; | |
206 } else if (value is String) { | |
207 return apiProperty | |
208 ..stringValue = value; | |
209 } else if (value is DateTime) { | |
210 return apiProperty | |
211 ..dateTimeValue = value; | |
212 } else if (value is datastore.BlobValue) { | |
213 return apiProperty | |
214 ..blobValueAsBytes = value.bytes; | |
215 } else if (value is datastore.Key) { | |
216 return apiProperty | |
217 ..keyValue = _convertDatastore2ApiKey(value, enforceId: false); | |
218 } else if (value is List) { | |
219 if (!lists) { | |
220 // FIXME(Issue #3): Consistently handle exceptions. | |
221 throw new Exception('List values are not allowed.'); | |
222 } | |
223 convertItem(i) | |
224 => _convertDatastore2ApiPropertyValue(i, indexed, lists: false); | |
225 return new api.Property()..listValue = value.map(convertItem).toList(); | |
226 } else { | |
227 throw new UnsupportedError( | |
228 'Types ${value.runtimeType} cannot be used for serializing.'); | |
229 } | |
230 } | |
231 | |
232 static datastore.Entity _convertApi2DatastoreEntity(api.Entity entity) { | 167 static datastore.Entity _convertApi2DatastoreEntity(api.Entity entity) { |
233 var unindexedProperties = new Set(); | 168 var unindexedProperties = new Set(); |
234 var properties = {}; | 169 var properties = {}; |
235 | 170 |
236 if (entity.properties != null) { | 171 if (entity.properties != null) { |
237 entity.properties.forEach((String name, api.Property property) { | 172 entity.properties.forEach((String name, api.Value value) { |
238 properties[name] = _convertApi2DatastoreProperty(property); | 173 properties[name] = _convertApi2DatastoreProperty(value); |
239 if (property.indexed == false) { | 174 if (value.excludeFromIndexes != null && |
240 // TODO(Issue #$4): Should we support mixed indexed/non-indexed list | 175 value.excludeFromIndexes) { |
241 // values? | 176 unindexedProperties.add(name); |
242 if (property.listValue != null) { | |
243 if (property.listValue.length > 0) { | |
244 var firstIndexed = property.listValue.first.indexed; | |
245 for (int i = 1; i < property.listValue.length; i++) { | |
246 if (property.listValue[i].indexed != firstIndexed) { | |
247 throw new Exception('Some list entries are indexed and some ' | |
248 'are not. This is currently not supported.'); | |
249 } | |
250 } | |
251 if (firstIndexed == false) { | |
252 unindexedProperties.add(name); | |
253 } | |
254 } | |
255 } else { | |
256 unindexedProperties.add(name); | |
257 } | |
258 } | 177 } |
259 }); | 178 }); |
260 } | 179 } |
261 return new datastore.Entity(_convertApi2DatastoreKey(entity.key), | 180 return new datastore.Entity(_convertApi2DatastoreKey(entity.key), |
262 properties, | 181 properties, |
263 unIndexedProperties: unindexedProperties); | 182 unIndexedProperties: unindexedProperties); |
264 } | 183 } |
265 | 184 |
266 api.Entity _convertDatastore2ApiEntity(datastore.Entity entity, | 185 api.Entity _convertDatastore2ApiEntity(datastore.Entity entity, |
267 {bool enforceId: false}) { | 186 {bool enforceId: false}) { |
(...skipping 28 matching lines...) Expand all Loading... | |
296 var pf = new api.PropertyFilter(); | 215 var pf = new api.PropertyFilter(); |
297 var operator = relationMapping[filter.relation]; | 216 var operator = relationMapping[filter.relation]; |
298 // FIXME(Issue #5): Is this OK? | 217 // FIXME(Issue #5): Is this OK? |
299 if (filter.relation == datastore.FilterRelation.In) { | 218 if (filter.relation == datastore.FilterRelation.In) { |
300 operator = 'EQUAL'; | 219 operator = 'EQUAL'; |
301 } | 220 } |
302 | 221 |
303 if (operator == null) { | 222 if (operator == null) { |
304 throw new ArgumentError('Unknown filter relation: ${filter.relation}.'); | 223 throw new ArgumentError('Unknown filter relation: ${filter.relation}.'); |
305 } | 224 } |
306 pf.operator = operator; | 225 pf.op = operator; |
307 pf.property = new api.PropertyReference()..name = filter.name; | 226 pf.property = new api.PropertyReference()..name = filter.name; |
308 | 227 |
309 // FIXME(Issue #5): Is this OK? | 228 // FIXME(Issue #5): Is this OK? |
310 var value = filter.value; | 229 var value = filter.value; |
311 if (filter.relation == datastore.FilterRelation.In) { | 230 if (filter.relation == datastore.FilterRelation.In) { |
312 if (value is List && value.length == 1) { | 231 if (value is List && value.length == 1) { |
313 value = value.first; | 232 value = value.first; |
314 } else { | 233 } else { |
315 throw new ArgumentError('List values not supported (was: $value).'); | 234 throw new ArgumentError('List values not supported (was: $value).'); |
316 } | 235 } |
317 } | 236 } |
318 | 237 |
319 pf.value = _convertDatastore2ApiPropertyValue(value, true, lists: false); | 238 pf.value = _convertDatastore2ApiPropertyValue(value, true, lists: false); |
320 return new api.Filter()..propertyFilter = pf; | 239 return new api.Filter()..propertyFilter = pf; |
321 } | 240 } |
322 | 241 |
323 api.Filter _convertDatastoreAncestorKey2ApiFilter(datastore.Key key) { | 242 api.Filter _convertDatastoreAncestorKey2ApiFilter(datastore.Key key) { |
324 var pf = new api.PropertyFilter(); | 243 var pf = new api.PropertyFilter(); |
325 pf.operator = 'HAS_ANCESTOR'; | 244 pf.op = 'HAS_ANCESTOR'; |
326 pf.property = new api.PropertyReference()..name = '__key__'; | 245 pf.property = new api.PropertyReference()..name = '__key__'; |
327 pf.value = new api.Value() | 246 pf.value = new api.Value() |
328 ..keyValue = _convertDatastore2ApiKey(key, enforceId: true); | 247 ..keyValue = _convertDatastore2ApiKey(key, enforceId: true); |
329 return new api.Filter()..propertyFilter = pf; | 248 return new api.Filter()..propertyFilter = pf; |
330 } | 249 } |
331 | 250 |
332 api.Filter _convertDatastore2ApiFilters(List<datastore.Filter> filters, | 251 api.Filter _convertDatastore2ApiFilters(List<datastore.Filter> filters, |
333 datastore.Key ancestorKey) { | 252 datastore.Key ancestorKey) { |
334 if ((filters == null || filters.length == 0) && ancestorKey == null) { | 253 if ((filters == null || filters.length == 0) && ancestorKey == null) { |
335 return null; | 254 return null; |
336 } | 255 } |
337 | 256 |
338 var compFilter = new api.CompositeFilter(); | 257 var compFilter = new api.CompositeFilter(); |
339 if (filters != null) { | 258 if (filters != null) { |
340 compFilter.filters = filters.map(_convertDatastore2ApiFilter).toList(); | 259 compFilter.filters = filters.map(_convertDatastore2ApiFilter).toList(); |
341 } | 260 } |
342 if (ancestorKey != null) { | 261 if (ancestorKey != null) { |
343 var filter = _convertDatastoreAncestorKey2ApiFilter(ancestorKey); | 262 var filter = _convertDatastoreAncestorKey2ApiFilter(ancestorKey); |
344 if (compFilter.filters == null) { | 263 if (compFilter.filters == null) { |
345 compFilter.filters = [filter]; | 264 compFilter.filters = [filter]; |
346 } else { | 265 } else { |
347 compFilter.filters.add(filter); | 266 compFilter.filters.add(filter); |
348 } | 267 } |
349 } | 268 } |
350 compFilter.operator = 'AND'; | 269 compFilter.op = 'AND'; |
351 return new api.Filter()..compositeFilter = compFilter; | 270 return new api.Filter()..compositeFilter = compFilter; |
352 } | 271 } |
353 | 272 |
354 api.PropertyOrder _convertDatastore2ApiOrder(datastore.Order order) { | 273 api.PropertyOrder _convertDatastore2ApiOrder(datastore.Order order) { |
355 var property = new api.PropertyReference()..name = order.propertyName; | 274 var property = new api.PropertyReference()..name = order.propertyName; |
356 var direction = order.direction == datastore.OrderDirection.Ascending | 275 var direction = order.direction == datastore.OrderDirection.Ascending |
357 ? 'ASCENDING' : 'DESCENDING'; | 276 ? 'ASCENDING' : 'DESCENDING'; |
358 return new api.PropertyOrder() | 277 return new api.PropertyOrder() |
359 ..direction = direction | 278 ..direction = direction |
360 ..property = property; | 279 ..property = property; |
(...skipping 21 matching lines...) Expand all Loading... | |
382 } | 301 } |
383 } | 302 } |
384 return new Future.error(error, stack); | 303 return new Future.error(error, stack); |
385 } | 304 } |
386 | 305 |
387 Future<List<datastore.Key>> allocateIds(List<datastore.Key> keys) { | 306 Future<List<datastore.Key>> allocateIds(List<datastore.Key> keys) { |
388 var request = new api.AllocateIdsRequest(); | 307 var request = new api.AllocateIdsRequest(); |
389 request..keys = keys.map((key) { | 308 request..keys = keys.map((key) { |
390 return _convertDatastore2ApiKey(key, enforceId: false); | 309 return _convertDatastore2ApiKey(key, enforceId: false); |
391 }).toList(); | 310 }).toList(); |
392 return _api.datasets.allocateIds(request, _project).then((response) { | 311 return _api.projects.allocateIds(request, _project).then((response) { |
393 return response.keys.map(_convertApi2DatastoreKey).toList(); | 312 return response.keys.map(_convertApi2DatastoreKey).toList(); |
394 }, onError: _handleError); | 313 }, onError: _handleError); |
395 } | 314 } |
396 | 315 |
397 Future<datastore.Transaction> beginTransaction( | 316 Future<datastore.Transaction> beginTransaction( |
398 {bool crossEntityGroup: false}) { | 317 {bool crossEntityGroup: false}) { |
399 var request = new api.BeginTransactionRequest(); | 318 var request = new api.BeginTransactionRequest(); |
400 // TODO: Should this be made configurable? | 319 return _api.projects.beginTransaction(request, _project).then((result) { |
401 request.isolationLevel = 'SERIALIZABLE'; | |
402 return _api.datasets.beginTransaction(request, _project).then((result) { | |
403 return new TransactionImpl(result.transaction); | 320 return new TransactionImpl(result.transaction); |
404 }, onError: _handleError); | 321 }, onError: _handleError); |
405 } | 322 } |
406 | 323 |
407 Future<datastore.CommitResult> commit({List<datastore.Entity> inserts, | 324 Future<datastore.CommitResult> commit({List<datastore.Entity> inserts, |
408 List<datastore.Entity> autoIdInserts, | 325 List<datastore.Entity> autoIdInserts, |
409 List<datastore.Key> deletes, | 326 List<datastore.Key> deletes, |
410 datastore.Transaction transaction}) { | 327 datastore.Transaction transaction}) { |
411 var request = new api.CommitRequest(); | 328 var request = new api.CommitRequest(); |
412 | 329 |
413 if (transaction != null) { | 330 if (transaction != null) { |
414 request.mode = 'TRANSACTIONAL'; | 331 request.mode = 'TRANSACTIONAL'; |
415 request.transaction = (transaction as TransactionImpl).data; | 332 request.transaction = (transaction as TransactionImpl).data; |
416 } else { | 333 } else { |
417 request.mode = 'NON_TRANSACTIONAL'; | 334 request.mode = 'NON_TRANSACTIONAL'; |
418 } | 335 } |
419 | 336 |
420 request.mutation = new api.Mutation(); | 337 var mutations = request.mutations = []; |
421 if (inserts != null) { | 338 if (inserts != null) { |
422 request.mutation.upsert = new List(inserts.length); | |
423 for (int i = 0; i < inserts.length; i++) { | 339 for (int i = 0; i < inserts.length; i++) { |
424 request.mutation.upsert[i] = _convertDatastore2ApiEntity(inserts[i]); | 340 mutations.add( |
341 new api.Mutation()..upsert = | |
342 _convertDatastore2ApiEntity(inserts[i], enforceId: true)); | |
425 } | 343 } |
426 } | 344 } |
345 int autoIdStartIndex = -1; | |
427 if (autoIdInserts != null) { | 346 if (autoIdInserts != null) { |
428 request.mutation.insertAutoId = new List(autoIdInserts.length); | 347 autoIdStartIndex = mutations.length; |
429 for (int i = 0; i < autoIdInserts.length; i++) { | 348 for (int i = 0; i < autoIdInserts.length; i++) { |
430 request.mutation.insertAutoId[i] = | 349 mutations.add( |
431 _convertDatastore2ApiEntity(autoIdInserts[i], enforceId: false); | 350 new api.Mutation()..insert = |
351 _convertDatastore2ApiEntity(autoIdInserts[i], enforceId: false)); | |
432 } | 352 } |
433 } | 353 } |
434 if (deletes != null) { | 354 if (deletes != null) { |
435 request.mutation.delete = new List(deletes.length); | |
436 for (int i = 0; i < deletes.length; i++) { | 355 for (int i = 0; i < deletes.length; i++) { |
437 request.mutation.delete[i] = | 356 mutations.add( |
438 _convertDatastore2ApiKey(deletes[i], enforceId: true); | 357 new api.Mutation()..delete = |
358 _convertDatastore2ApiKey(deletes[i], enforceId: true)); | |
439 } | 359 } |
440 } | 360 } |
441 return _api.datasets.commit(request, _project).then((result) { | 361 return _api.projects.commit(request, _project).then((result) { |
442 var keys; | 362 var keys; |
443 if (autoIdInserts != null && autoIdInserts.length > 0) { | 363 if (autoIdInserts != null && autoIdInserts.length > 0) { |
444 keys = result | 364 List<api.MutationResult> mutationResults = result.mutationResults; |
445 .mutationResult | 365 assert(autoIdStartIndex != -1); |
446 .insertAutoIdKeys | 366 assert(mutationResults.length >= |
447 .map(_convertApi2DatastoreKey).toList(); | 367 (autoIdStartIndex + autoIdInserts.length)); |
368 keys = mutationResults | |
369 .skip(autoIdStartIndex) | |
370 .take(autoIdInserts.length) | |
371 .map((api.MutationResult r) => _convertApi2DatastoreKey(r.key)) | |
372 .toList(); | |
448 } | 373 } |
449 return new datastore.CommitResult(keys); | 374 return new datastore.CommitResult(keys); |
450 }, onError: _handleError); | 375 }, onError: _handleError); |
451 } | 376 } |
452 | 377 |
453 Future<List<datastore.Entity>> lookup(List<datastore.Key> keys, | 378 Future<List<datastore.Entity>> lookup(List<datastore.Key> keys, |
454 {datastore.Transaction transaction}) { | 379 {datastore.Transaction transaction}) { |
455 var apiKeys = keys.map((key) { | 380 var apiKeys = keys.map((key) { |
456 return _convertDatastore2ApiKey(key, enforceId: true); | 381 return _convertDatastore2ApiKey(key, enforceId: true); |
457 }).toList(); | 382 }).toList(); |
458 var request = new api.LookupRequest(); | 383 var request = new api.LookupRequest(); |
459 request.keys = apiKeys; | 384 request.keys = apiKeys; |
460 if (transaction != null) { | 385 if (transaction != null) { |
461 // TODO: Make readOptions more configurable. | 386 // TODO: Make readOptions more configurable. |
462 request.readOptions = new api.ReadOptions(); | 387 request.readOptions = new api.ReadOptions(); |
463 request.readOptions.transaction = (transaction as TransactionImpl).data; | 388 request.readOptions.transaction = (transaction as TransactionImpl).data; |
464 } | 389 } |
465 return _api.datasets.lookup(request, _project).then((response) { | 390 return _api.projects.lookup(request, _project).then((response) { |
466 if (response.deferred != null && response.deferred.length > 0) { | 391 if (response.deferred != null && response.deferred.length > 0) { |
467 throw new datastore.DatastoreError( | 392 throw new datastore.DatastoreError( |
468 'Could not successfully look up all keys due to resource ' | 393 'Could not successfully look up all keys due to resource ' |
469 'constraints.'); | 394 'constraints.'); |
470 } | 395 } |
471 | 396 |
472 // NOTE: This is worst-case O(n^2)! | 397 // NOTE: This is worst-case O(n^2)! |
473 // Maybe we can optimize this somehow. But the API says: | 398 // Maybe we can optimize this somehow. But the API says: |
474 // message LookupResponse { | 399 // message LookupResponse { |
475 // // The order of results in these fields is undefined and has no rela tion to | 400 // // The order of results in these fields is undefined and has no rela tion to |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
527 datastore.Transaction transaction}) { | 452 datastore.Transaction transaction}) { |
528 // NOTE: We explicitly do not set 'limit' here, since this is handled by | 453 // NOTE: We explicitly do not set 'limit' here, since this is handled by |
529 // QueryPageImpl.runQuery. | 454 // QueryPageImpl.runQuery. |
530 var apiQuery = new api.Query() | 455 var apiQuery = new api.Query() |
531 ..filter = _convertDatastore2ApiFilters(query.filters, | 456 ..filter = _convertDatastore2ApiFilters(query.filters, |
532 query.ancestorKey) | 457 query.ancestorKey) |
533 ..order = _convertDatastore2ApiOrders(query.orders) | 458 ..order = _convertDatastore2ApiOrders(query.orders) |
534 ..offset = query.offset; | 459 ..offset = query.offset; |
535 | 460 |
536 if (query.kind != null) { | 461 if (query.kind != null) { |
537 apiQuery.kinds = [new api.KindExpression()..name = query.kind]; | 462 apiQuery.kind = [new api.KindExpression()..name = query.kind]; |
538 } | 463 } |
539 | 464 |
540 var request = new api.RunQueryRequest(); | 465 var request = new api.RunQueryRequest(); |
541 request.query = apiQuery; | 466 request.query = apiQuery; |
542 if (transaction != null) { | 467 if (transaction != null) { |
543 // TODO: Make readOptions more configurable. | 468 // TODO: Make readOptions more configurable. |
544 request.readOptions = new api.ReadOptions(); | 469 request.readOptions = new api.ReadOptions(); |
545 request.readOptions.transaction = (transaction as TransactionImpl).data; | 470 request.readOptions.transaction = (transaction as TransactionImpl).data; |
546 } | 471 } |
547 if (partition != null) { | 472 if (partition != null) { |
548 request.partitionId = new api.PartitionId() | 473 request.partitionId = new api.PartitionId() |
549 ..namespace = partition.namespace; | 474 ..namespaceId = partition.namespace; |
550 } | 475 } |
551 | 476 |
552 return QueryPageImpl.runQuery(_api, _project, request, query.limit) | 477 return QueryPageImpl.runQuery(_api, _project, request, query.limit) |
553 .catchError(_handleError); | 478 .catchError(_handleError); |
554 } | 479 } |
555 | 480 |
556 Future rollback(datastore.Transaction transaction) { | 481 Future rollback(datastore.Transaction transaction) { |
557 // TODO: Handle [transaction] | 482 // TODO: Handle [transaction] |
558 var request = new api.RollbackRequest() | 483 var request = new api.RollbackRequest() |
559 ..transaction = (transaction as TransactionImpl).data; | 484 ..transaction = (transaction as TransactionImpl).data; |
560 return _api.datasets.rollback(request, _project).catchError(_handleError); | 485 return _api.projects.rollback(request, _project).catchError(_handleError); |
561 } | 486 } |
562 } | 487 } |
563 | 488 |
564 class QueryPageImpl implements Page<datastore.Entity> { | 489 class QueryPageImpl implements Page<datastore.Entity> { |
565 static const int MAX_ENTITIES_PER_RESPONSE = 2000; | 490 static const int MAX_ENTITIES_PER_RESPONSE = 2000; |
566 | 491 |
567 final api.DatastoreApi _api; | 492 final api.DatastoreApi _api; |
568 final String _project; | 493 final String _project; |
569 final api.RunQueryRequest _nextRequest; | 494 final api.RunQueryRequest _nextRequest; |
570 final List<datastore.Entity> _entities; | 495 final List<datastore.Entity> _entities; |
(...skipping 14 matching lines...) Expand all Loading... | |
585 int batchLimit = batchSize; | 510 int batchLimit = batchSize; |
586 if (batchLimit == null) { | 511 if (batchLimit == null) { |
587 batchLimit = MAX_ENTITIES_PER_RESPONSE; | 512 batchLimit = MAX_ENTITIES_PER_RESPONSE; |
588 } | 513 } |
589 if (limit != null && limit < batchLimit) { | 514 if (limit != null && limit < batchLimit) { |
590 batchLimit = limit; | 515 batchLimit = limit; |
591 } | 516 } |
592 | 517 |
593 request.query.limit = batchLimit; | 518 request.query.limit = batchLimit; |
594 | 519 |
595 return api.datasets.runQuery(request, project).then((response) { | 520 return api.projects.runQuery(request, project).then((response) { |
596 var returnedEntities = const []; | 521 var returnedEntities = const []; |
597 | 522 |
598 var batch = response.batch; | 523 var batch = response.batch; |
599 if (batch.entityResults != null) { | 524 if (batch.entityResults != null) { |
600 returnedEntities = batch.entityResults | 525 returnedEntities = batch.entityResults |
601 .map((result) => result.entity) | 526 .map((result) => result.entity) |
602 .map(DatastoreImpl._convertApi2DatastoreEntity) | 527 .map(DatastoreImpl._convertApi2DatastoreEntity) |
603 .toList(); | 528 .toList(); |
604 } | 529 } |
605 | 530 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
701 return new Future.sync(() { | 626 return new Future.sync(() { |
702 throw new ArgumentError('Cannot call next() on last page.'); | 627 throw new ArgumentError('Cannot call next() on last page.'); |
703 }); | 628 }); |
704 } | 629 } |
705 | 630 |
706 return QueryPageImpl.runQuery( | 631 return QueryPageImpl.runQuery( |
707 _api, _project, _nextRequest, _remainingNumberOfEntities) | 632 _api, _project, _nextRequest, _remainingNumberOfEntities) |
708 .catchError(DatastoreImpl._handleError); | 633 .catchError(DatastoreImpl._handleError); |
709 } | 634 } |
710 } | 635 } |
OLD | NEW |