Chromium Code Reviews| 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 |