| 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 |