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"; |
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 dynamic _convertApi2DatastoreProperty(api.Value value) { |
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) |
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.'); |
| 164 else if (value.geoPointValue != null) |
| 165 throw new UnsupportedError('GeoPoint values are not supported.'); |
188 return null; | 166 return null; |
189 } | 167 } |
190 | 168 |
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) { | 169 static datastore.Entity _convertApi2DatastoreEntity(api.Entity entity) { |
233 var unindexedProperties = new Set(); | 170 var unindexedProperties = new Set(); |
234 var properties = {}; | 171 var properties = {}; |
235 | 172 |
236 if (entity.properties != null) { | 173 if (entity.properties != null) { |
237 entity.properties.forEach((String name, api.Property property) { | 174 entity.properties.forEach((String name, api.Value value) { |
238 properties[name] = _convertApi2DatastoreProperty(property); | 175 properties[name] = _convertApi2DatastoreProperty(value); |
239 if (property.indexed == false) { | 176 if (value.excludeFromIndexes != null && |
240 // TODO(Issue #$4): Should we support mixed indexed/non-indexed list | 177 value.excludeFromIndexes) { |
241 // values? | 178 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 } | 179 } |
259 }); | 180 }); |
260 } | 181 } |
261 return new datastore.Entity(_convertApi2DatastoreKey(entity.key), | 182 return new datastore.Entity(_convertApi2DatastoreKey(entity.key), |
262 properties, | 183 properties, |
263 unIndexedProperties: unindexedProperties); | 184 unIndexedProperties: unindexedProperties); |
264 } | 185 } |
265 | 186 |
266 api.Entity _convertDatastore2ApiEntity(datastore.Entity entity, | 187 api.Entity _convertDatastore2ApiEntity(datastore.Entity entity, |
267 {bool enforceId: false}) { | 188 {bool enforceId: false}) { |
(...skipping 28 matching lines...) Expand all Loading... |
296 var pf = new api.PropertyFilter(); | 217 var pf = new api.PropertyFilter(); |
297 var operator = relationMapping[filter.relation]; | 218 var operator = relationMapping[filter.relation]; |
298 // FIXME(Issue #5): Is this OK? | 219 // FIXME(Issue #5): Is this OK? |
299 if (filter.relation == datastore.FilterRelation.In) { | 220 if (filter.relation == datastore.FilterRelation.In) { |
300 operator = 'EQUAL'; | 221 operator = 'EQUAL'; |
301 } | 222 } |
302 | 223 |
303 if (operator == null) { | 224 if (operator == null) { |
304 throw new ArgumentError('Unknown filter relation: ${filter.relation}.'); | 225 throw new ArgumentError('Unknown filter relation: ${filter.relation}.'); |
305 } | 226 } |
306 pf.operator = operator; | 227 pf.op = operator; |
307 pf.property = new api.PropertyReference()..name = filter.name; | 228 pf.property = new api.PropertyReference()..name = filter.name; |
308 | 229 |
309 // FIXME(Issue #5): Is this OK? | 230 // FIXME(Issue #5): Is this OK? |
310 var value = filter.value; | 231 var value = filter.value; |
311 if (filter.relation == datastore.FilterRelation.In) { | 232 if (filter.relation == datastore.FilterRelation.In) { |
312 if (value is List && value.length == 1) { | 233 if (value is List && value.length == 1) { |
313 value = value.first; | 234 value = value.first; |
314 } else { | 235 } else { |
315 throw new ArgumentError('List values not supported (was: $value).'); | 236 throw new ArgumentError('List values not supported (was: $value).'); |
316 } | 237 } |
317 } | 238 } |
318 | 239 |
319 pf.value = _convertDatastore2ApiPropertyValue(value, true, lists: false); | 240 pf.value = _convertDatastore2ApiPropertyValue(value, true, lists: false); |
320 return new api.Filter()..propertyFilter = pf; | 241 return new api.Filter()..propertyFilter = pf; |
321 } | 242 } |
322 | 243 |
323 api.Filter _convertDatastoreAncestorKey2ApiFilter(datastore.Key key) { | 244 api.Filter _convertDatastoreAncestorKey2ApiFilter(datastore.Key key) { |
324 var pf = new api.PropertyFilter(); | 245 var pf = new api.PropertyFilter(); |
325 pf.operator = 'HAS_ANCESTOR'; | 246 pf.op = 'HAS_ANCESTOR'; |
326 pf.property = new api.PropertyReference()..name = '__key__'; | 247 pf.property = new api.PropertyReference()..name = '__key__'; |
327 pf.value = new api.Value() | 248 pf.value = new api.Value() |
328 ..keyValue = _convertDatastore2ApiKey(key, enforceId: true); | 249 ..keyValue = _convertDatastore2ApiKey(key, enforceId: true); |
329 return new api.Filter()..propertyFilter = pf; | 250 return new api.Filter()..propertyFilter = pf; |
330 } | 251 } |
331 | 252 |
332 api.Filter _convertDatastore2ApiFilters(List<datastore.Filter> filters, | 253 api.Filter _convertDatastore2ApiFilters(List<datastore.Filter> filters, |
333 datastore.Key ancestorKey) { | 254 datastore.Key ancestorKey) { |
334 if ((filters == null || filters.length == 0) && ancestorKey == null) { | 255 if ((filters == null || filters.length == 0) && ancestorKey == null) { |
335 return null; | 256 return null; |
336 } | 257 } |
337 | 258 |
338 var compFilter = new api.CompositeFilter(); | 259 var compFilter = new api.CompositeFilter(); |
339 if (filters != null) { | 260 if (filters != null) { |
340 compFilter.filters = filters.map(_convertDatastore2ApiFilter).toList(); | 261 compFilter.filters = filters.map(_convertDatastore2ApiFilter).toList(); |
341 } | 262 } |
342 if (ancestorKey != null) { | 263 if (ancestorKey != null) { |
343 var filter = _convertDatastoreAncestorKey2ApiFilter(ancestorKey); | 264 var filter = _convertDatastoreAncestorKey2ApiFilter(ancestorKey); |
344 if (compFilter.filters == null) { | 265 if (compFilter.filters == null) { |
345 compFilter.filters = [filter]; | 266 compFilter.filters = [filter]; |
346 } else { | 267 } else { |
347 compFilter.filters.add(filter); | 268 compFilter.filters.add(filter); |
348 } | 269 } |
349 } | 270 } |
350 compFilter.operator = 'AND'; | 271 compFilter.op = 'AND'; |
351 return new api.Filter()..compositeFilter = compFilter; | 272 return new api.Filter()..compositeFilter = compFilter; |
352 } | 273 } |
353 | 274 |
354 api.PropertyOrder _convertDatastore2ApiOrder(datastore.Order order) { | 275 api.PropertyOrder _convertDatastore2ApiOrder(datastore.Order order) { |
355 var property = new api.PropertyReference()..name = order.propertyName; | 276 var property = new api.PropertyReference()..name = order.propertyName; |
356 var direction = order.direction == datastore.OrderDirection.Ascending | 277 var direction = order.direction == datastore.OrderDirection.Ascending |
357 ? 'ASCENDING' : 'DESCENDING'; | 278 ? 'ASCENDING' : 'DESCENDING'; |
358 return new api.PropertyOrder() | 279 return new api.PropertyOrder() |
359 ..direction = direction | 280 ..direction = direction |
360 ..property = property; | 281 ..property = property; |
(...skipping 21 matching lines...) Expand all Loading... |
382 } | 303 } |
383 } | 304 } |
384 return new Future.error(error, stack); | 305 return new Future.error(error, stack); |
385 } | 306 } |
386 | 307 |
387 Future<List<datastore.Key>> allocateIds(List<datastore.Key> keys) { | 308 Future<List<datastore.Key>> allocateIds(List<datastore.Key> keys) { |
388 var request = new api.AllocateIdsRequest(); | 309 var request = new api.AllocateIdsRequest(); |
389 request..keys = keys.map((key) { | 310 request..keys = keys.map((key) { |
390 return _convertDatastore2ApiKey(key, enforceId: false); | 311 return _convertDatastore2ApiKey(key, enforceId: false); |
391 }).toList(); | 312 }).toList(); |
392 return _api.datasets.allocateIds(request, _project).then((response) { | 313 return _api.projects.allocateIds(request, _project).then((response) { |
393 return response.keys.map(_convertApi2DatastoreKey).toList(); | 314 return response.keys.map(_convertApi2DatastoreKey).toList(); |
394 }, onError: _handleError); | 315 }, onError: _handleError); |
395 } | 316 } |
396 | 317 |
397 Future<datastore.Transaction> beginTransaction( | 318 Future<datastore.Transaction> beginTransaction( |
398 {bool crossEntityGroup: false}) { | 319 {bool crossEntityGroup: false}) { |
399 var request = new api.BeginTransactionRequest(); | 320 var request = new api.BeginTransactionRequest(); |
400 // TODO: Should this be made configurable? | 321 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); | 322 return new TransactionImpl(result.transaction); |
404 }, onError: _handleError); | 323 }, onError: _handleError); |
405 } | 324 } |
406 | 325 |
407 Future<datastore.CommitResult> commit({List<datastore.Entity> inserts, | 326 Future<datastore.CommitResult> commit({List<datastore.Entity> inserts, |
408 List<datastore.Entity> autoIdInserts, | 327 List<datastore.Entity> autoIdInserts, |
409 List<datastore.Key> deletes, | 328 List<datastore.Key> deletes, |
410 datastore.Transaction transaction}) { | 329 datastore.Transaction transaction}) { |
411 var request = new api.CommitRequest(); | 330 var request = new api.CommitRequest(); |
412 | 331 |
413 if (transaction != null) { | 332 if (transaction != null) { |
414 request.mode = 'TRANSACTIONAL'; | 333 request.mode = 'TRANSACTIONAL'; |
415 request.transaction = (transaction as TransactionImpl).data; | 334 request.transaction = (transaction as TransactionImpl).data; |
416 } else { | 335 } else { |
417 request.mode = 'NON_TRANSACTIONAL'; | 336 request.mode = 'NON_TRANSACTIONAL'; |
418 } | 337 } |
419 | 338 |
420 request.mutation = new api.Mutation(); | 339 var mutations = request.mutations = []; |
421 if (inserts != null) { | 340 if (inserts != null) { |
422 request.mutation.upsert = new List(inserts.length); | |
423 for (int i = 0; i < inserts.length; i++) { | 341 for (int i = 0; i < inserts.length; i++) { |
424 request.mutation.upsert[i] = _convertDatastore2ApiEntity(inserts[i]); | 342 mutations.add( |
| 343 new api.Mutation()..upsert = |
| 344 _convertDatastore2ApiEntity(inserts[i], enforceId: true)); |
425 } | 345 } |
426 } | 346 } |
| 347 int autoIdStartIndex = -1; |
427 if (autoIdInserts != null) { | 348 if (autoIdInserts != null) { |
428 request.mutation.insertAutoId = new List(autoIdInserts.length); | 349 autoIdStartIndex = mutations.length; |
429 for (int i = 0; i < autoIdInserts.length; i++) { | 350 for (int i = 0; i < autoIdInserts.length; i++) { |
430 request.mutation.insertAutoId[i] = | 351 mutations.add( |
431 _convertDatastore2ApiEntity(autoIdInserts[i], enforceId: false); | 352 new api.Mutation()..insert = |
| 353 _convertDatastore2ApiEntity(autoIdInserts[i], enforceId: false)); |
432 } | 354 } |
433 } | 355 } |
434 if (deletes != null) { | 356 if (deletes != null) { |
435 request.mutation.delete = new List(deletes.length); | |
436 for (int i = 0; i < deletes.length; i++) { | 357 for (int i = 0; i < deletes.length; i++) { |
437 request.mutation.delete[i] = | 358 mutations.add( |
438 _convertDatastore2ApiKey(deletes[i], enforceId: true); | 359 new api.Mutation()..delete = |
| 360 _convertDatastore2ApiKey(deletes[i], enforceId: true)); |
439 } | 361 } |
440 } | 362 } |
441 return _api.datasets.commit(request, _project).then((result) { | 363 return _api.projects.commit(request, _project).then((result) { |
442 var keys; | 364 var keys; |
443 if (autoIdInserts != null && autoIdInserts.length > 0) { | 365 if (autoIdInserts != null && autoIdInserts.length > 0) { |
444 keys = result | 366 List<api.MutationResult> mutationResults = result.mutationResults; |
445 .mutationResult | 367 assert(autoIdStartIndex != -1); |
446 .insertAutoIdKeys | 368 assert(mutationResults.length >= |
447 .map(_convertApi2DatastoreKey).toList(); | 369 (autoIdStartIndex + autoIdInserts.length)); |
| 370 keys = mutationResults |
| 371 .skip(autoIdStartIndex) |
| 372 .take(autoIdInserts.length) |
| 373 .map((api.MutationResult r) => _convertApi2DatastoreKey(r.key)) |
| 374 .toList(); |
448 } | 375 } |
449 return new datastore.CommitResult(keys); | 376 return new datastore.CommitResult(keys); |
450 }, onError: _handleError); | 377 }, onError: _handleError); |
451 } | 378 } |
452 | 379 |
453 Future<List<datastore.Entity>> lookup(List<datastore.Key> keys, | 380 Future<List<datastore.Entity>> lookup(List<datastore.Key> keys, |
454 {datastore.Transaction transaction}) { | 381 {datastore.Transaction transaction}) { |
455 var apiKeys = keys.map((key) { | 382 var apiKeys = keys.map((key) { |
456 return _convertDatastore2ApiKey(key, enforceId: true); | 383 return _convertDatastore2ApiKey(key, enforceId: true); |
457 }).toList(); | 384 }).toList(); |
458 var request = new api.LookupRequest(); | 385 var request = new api.LookupRequest(); |
459 request.keys = apiKeys; | 386 request.keys = apiKeys; |
460 if (transaction != null) { | 387 if (transaction != null) { |
461 // TODO: Make readOptions more configurable. | 388 // TODO: Make readOptions more configurable. |
462 request.readOptions = new api.ReadOptions(); | 389 request.readOptions = new api.ReadOptions(); |
463 request.readOptions.transaction = (transaction as TransactionImpl).data; | 390 request.readOptions.transaction = (transaction as TransactionImpl).data; |
464 } | 391 } |
465 return _api.datasets.lookup(request, _project).then((response) { | 392 return _api.projects.lookup(request, _project).then((response) { |
466 if (response.deferred != null && response.deferred.length > 0) { | 393 if (response.deferred != null && response.deferred.length > 0) { |
467 throw new datastore.DatastoreError( | 394 throw new datastore.DatastoreError( |
468 'Could not successfully look up all keys due to resource ' | 395 'Could not successfully look up all keys due to resource ' |
469 'constraints.'); | 396 'constraints.'); |
470 } | 397 } |
471 | 398 |
472 // NOTE: This is worst-case O(n^2)! | 399 // NOTE: This is worst-case O(n^2)! |
473 // Maybe we can optimize this somehow. But the API says: | 400 // Maybe we can optimize this somehow. But the API says: |
474 // message LookupResponse { | 401 // message LookupResponse { |
475 // // The order of results in these fields is undefined and has no rela
tion to | 402 // // 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}) { | 454 datastore.Transaction transaction}) { |
528 // NOTE: We explicitly do not set 'limit' here, since this is handled by | 455 // NOTE: We explicitly do not set 'limit' here, since this is handled by |
529 // QueryPageImpl.runQuery. | 456 // QueryPageImpl.runQuery. |
530 var apiQuery = new api.Query() | 457 var apiQuery = new api.Query() |
531 ..filter = _convertDatastore2ApiFilters(query.filters, | 458 ..filter = _convertDatastore2ApiFilters(query.filters, |
532 query.ancestorKey) | 459 query.ancestorKey) |
533 ..order = _convertDatastore2ApiOrders(query.orders) | 460 ..order = _convertDatastore2ApiOrders(query.orders) |
534 ..offset = query.offset; | 461 ..offset = query.offset; |
535 | 462 |
536 if (query.kind != null) { | 463 if (query.kind != null) { |
537 apiQuery.kinds = [new api.KindExpression()..name = query.kind]; | 464 apiQuery.kind = [new api.KindExpression()..name = query.kind]; |
538 } | 465 } |
539 | 466 |
540 var request = new api.RunQueryRequest(); | 467 var request = new api.RunQueryRequest(); |
541 request.query = apiQuery; | 468 request.query = apiQuery; |
542 if (transaction != null) { | 469 if (transaction != null) { |
543 // TODO: Make readOptions more configurable. | 470 // TODO: Make readOptions more configurable. |
544 request.readOptions = new api.ReadOptions(); | 471 request.readOptions = new api.ReadOptions(); |
545 request.readOptions.transaction = (transaction as TransactionImpl).data; | 472 request.readOptions.transaction = (transaction as TransactionImpl).data; |
546 } | 473 } |
547 if (partition != null) { | 474 if (partition != null) { |
548 request.partitionId = new api.PartitionId() | 475 request.partitionId = new api.PartitionId() |
549 ..namespace = partition.namespace; | 476 ..namespaceId = partition.namespace; |
550 } | 477 } |
551 | 478 |
552 return QueryPageImpl.runQuery(_api, _project, request, query.limit) | 479 return QueryPageImpl.runQuery(_api, _project, request, query.limit) |
553 .catchError(_handleError); | 480 .catchError(_handleError); |
554 } | 481 } |
555 | 482 |
556 Future rollback(datastore.Transaction transaction) { | 483 Future rollback(datastore.Transaction transaction) { |
557 // TODO: Handle [transaction] | 484 // TODO: Handle [transaction] |
558 var request = new api.RollbackRequest() | 485 var request = new api.RollbackRequest() |
559 ..transaction = (transaction as TransactionImpl).data; | 486 ..transaction = (transaction as TransactionImpl).data; |
560 return _api.datasets.rollback(request, _project).catchError(_handleError); | 487 return _api.projects.rollback(request, _project).catchError(_handleError); |
561 } | 488 } |
562 } | 489 } |
563 | 490 |
564 class QueryPageImpl implements Page<datastore.Entity> { | 491 class QueryPageImpl implements Page<datastore.Entity> { |
565 static const int MAX_ENTITIES_PER_RESPONSE = 2000; | 492 static const int MAX_ENTITIES_PER_RESPONSE = 2000; |
566 | 493 |
567 final api.DatastoreApi _api; | 494 final api.DatastoreApi _api; |
568 final String _project; | 495 final String _project; |
569 final api.RunQueryRequest _nextRequest; | 496 final api.RunQueryRequest _nextRequest; |
570 final List<datastore.Entity> _entities; | 497 final List<datastore.Entity> _entities; |
(...skipping 14 matching lines...) Expand all Loading... |
585 int batchLimit = batchSize; | 512 int batchLimit = batchSize; |
586 if (batchLimit == null) { | 513 if (batchLimit == null) { |
587 batchLimit = MAX_ENTITIES_PER_RESPONSE; | 514 batchLimit = MAX_ENTITIES_PER_RESPONSE; |
588 } | 515 } |
589 if (limit != null && limit < batchLimit) { | 516 if (limit != null && limit < batchLimit) { |
590 batchLimit = limit; | 517 batchLimit = limit; |
591 } | 518 } |
592 | 519 |
593 request.query.limit = batchLimit; | 520 request.query.limit = batchLimit; |
594 | 521 |
595 return api.datasets.runQuery(request, project).then((response) { | 522 return api.projects.runQuery(request, project).then((response) { |
596 var returnedEntities = const []; | 523 var returnedEntities = const []; |
597 | 524 |
598 var batch = response.batch; | 525 var batch = response.batch; |
599 if (batch.entityResults != null) { | 526 if (batch.entityResults != null) { |
600 returnedEntities = batch.entityResults | 527 returnedEntities = batch.entityResults |
601 .map((result) => result.entity) | 528 .map((result) => result.entity) |
602 .map(DatastoreImpl._convertApi2DatastoreEntity) | 529 .map(DatastoreImpl._convertApi2DatastoreEntity) |
603 .toList(); | 530 .toList(); |
604 } | 531 } |
605 | 532 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
701 return new Future.sync(() { | 628 return new Future.sync(() { |
702 throw new ArgumentError('Cannot call next() on last page.'); | 629 throw new ArgumentError('Cannot call next() on last page.'); |
703 }); | 630 }); |
704 } | 631 } |
705 | 632 |
706 return QueryPageImpl.runQuery( | 633 return QueryPageImpl.runQuery( |
707 _api, _project, _nextRequest, _remainingNumberOfEntities) | 634 _api, _project, _nextRequest, _remainingNumberOfEntities) |
708 .catchError(DatastoreImpl._handleError); | 635 .catchError(DatastoreImpl._handleError); |
709 } | 636 } |
710 } | 637 } |
OLD | NEW |