Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: pkg/gcloud/lib/datastore.dart

Issue 804973002: Add appengine/gcloud/mustache dependencies. (Closed) Base URL: git@github.com:dart-lang/pub-dartlang-dart.git@master
Patch Set: Added AUTHORS/LICENSE/PATENTS files Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/gcloud/lib/common.dart ('k') | pkg/gcloud/lib/db.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// This library provides a low-level API for accessing Google's Cloud
6 /// Datastore.
7 ///
8 /// For more information on Cloud Datastore, please refer to the following
9 /// developers page: https://cloud.google.com/datastore/docs
10 library gcloud.datastore;
11
12 import 'dart:async';
13
14 import 'common.dart' show Page;
15 import 'service_scope.dart' as ss;
16
17 const Symbol _datastoreKey = #_gcloud.datastore;
18
19 /// Access the [Datastore] object available in the current service scope.
20 ///
21 /// The returned object will be the one which was previously registered with
22 /// [registerDatastoreService] within the current (or a parent) service scope.
23 ///
24 /// Accessing this getter outside of a service scope will result in an error.
25 /// See the `package:gcloud/service_scope.dart` library for more information.
26 Datastore get datastoreService => ss.lookup(_datastoreKey);
27
28 /// Registers the [Datastore] object within the current service scope.
29 ///
30 /// The provided `datastore` object will be avilable via the top-level
31 /// `datastore` getter.
32 ///
33 /// Calling this function outside of a service scope will result in an error.
34 /// Calling this function more than once inside the same service scope is not
35 /// allowed.
36 void registerDatastoreService(Datastore datastore) {
37 ss.register(_datastoreKey, datastore);
38 }
39
40 class ApplicationError implements Exception {
41 final String message;
42 ApplicationError(this.message);
43
44 String toString() => "ApplicationError: $message";
45 }
46
47
48 class DatastoreError implements Exception {
49 final String message;
50
51 DatastoreError([String message]) : message =
52 (message != null ?message : 'DatastoreError: An unknown error occured');
53
54 String toString() => '$message';
55 }
56
57 class UnknownDatastoreError extends DatastoreError {
58 UnknownDatastoreError(error) : super("An unknown error occured ($error).");
59 }
60
61 class TransactionAbortedError extends DatastoreError {
62 TransactionAbortedError() : super("The transaction was aborted.");
63 }
64
65 class TimeoutError extends DatastoreError {
66 TimeoutError() : super("The operation timed out.");
67 }
68
69 /// Thrown when a query would require an index which was not set.
70 ///
71 /// An application needs to specify indices in a `index.yaml` file and needs to
72 /// create indices using the `gcloud preview datastore create-indexes` command.
73 class NeedIndexError extends DatastoreError {
74 NeedIndexError()
75 : super("An index is needed for the query to succeed.");
76 }
77
78 class PermissionDeniedError extends DatastoreError {
79 PermissionDeniedError() : super("Permission denied.");
80 }
81
82 class InternalError extends DatastoreError {
83 InternalError() : super("Internal service error.");
84 }
85
86 class QuotaExceededError extends DatastoreError {
87 QuotaExceededError(error) : super("Quota was exceeded ($error).");
88 }
89
90 /// A datastore Entity
91 ///
92 /// An entity is identified by a unique `key` and consists of a number of
93 /// `properties`. If a property should not be indexed, it needs to be included
94 /// in the `unIndexedProperties` set.
95 ///
96 /// The `properties` field maps names to values. Values can be of a primitive
97 /// type or of a composed type.
98 ///
99 /// The following primitive types are supported:
100 /// bool, int, double, String, DateTime, BlobValue, Key
101 ///
102 /// It is possible to have a `List` of values. The values must be primitive.
103 /// Lists inside lists are not supported.
104 ///
105 /// Whether a property is indexed or not applies to all values (this is only
106 /// relevant if the value is a list of primitive values).
107 class Entity {
108 final Key key;
109 final Map<String, Object> properties;
110 final Set<String> unIndexedProperties;
111
112 Entity(this.key, this.properties, {this.unIndexedProperties});
113 }
114
115 /// A complete or partial key.
116 ///
117 /// A key can uniquely identifiy a datastore `Entity`s. It consists of a
118 /// partition and path. The path consists of one or more `KeyElement`s.
119 ///
120 /// A key may be incomplete. This is usesfull when inserting `Entity`s which IDs
121 /// should be automatically allocated.
122 ///
123 /// Example of a fully populated [Key]:
124 ///
125 /// var fullKey = new Key([new KeyElement('Person', 1),
126 /// new KeyElement('Address', 2)]);
127 ///
128 /// Example of a partially populated [Key] / an imcomplete [Key]:
129 ///
130 /// var partialKey = new Key([new KeyElement('Person', 1),
131 /// new KeyElement('Address', null)]);
132 class Key {
133 /// The partition of this `Key`.
134 final Partition partition;
135
136 /// The path of `KeyElement`s.
137 final List<KeyElement> elements;
138
139 Key(this.elements, {Partition partition})
140 : this.partition = (partition == null) ? Partition.DEFAULT : partition;
141
142 factory Key.fromParent(String kind, int id, {Key parent}) {
143 var partition;
144 var elements = [];
145 if (parent != null) {
146 partition = parent.partition;
147 elements.addAll(parent.elements);
148 }
149 elements.add(new KeyElement(kind, id));
150 return new Key(elements, partition: partition);
151 }
152
153 int get hashCode =>
154 elements.fold(partition.hashCode, (a, b) => a ^ b.hashCode);
155
156 bool operator==(Object other) {
157 if (identical(this, other)) return true;
158
159 if (other is Key &&
160 partition == other.partition &&
161 elements.length == other.elements.length) {
162 for (int i = 0; i < elements.length; i++) {
163 if (elements[i] != other.elements[i]) return false;
164 }
165 return true;
166 }
167 return false;
168 }
169
170 String toString() {
171 var namespaceString =
172 partition.namespace == null ? 'null' : "'${partition.namespace}'";
173 return "Key(namespace=$namespaceString, path=[${elements.join(', ')}])";
174 }
175 }
176
177 /// A datastore partition.
178 ///
179 /// A partition is used for partitioning a dataset into multiple namespaces.
180 /// The default namespace is `null`. Using empty Strings as namespaces is
181 /// invalid.
182 ///
183 // TODO(Issue #6): Add dataset-id here.
184 class Partition {
185 static const Partition DEFAULT = const Partition._default();
186
187 /// The namespace of this partition.
188 final String namespace;
189
190 Partition(this.namespace) {
191 if (namespace == '') {
192 throw new ArgumentError("'namespace' must not be empty");
193 }
194 }
195
196 const Partition._default() : this.namespace = null;
197
198 int get hashCode => namespace.hashCode;
199
200 bool operator==(Object other) =>
201 other is Partition && namespace == other.namespace;
202 }
203
204 /// An element in a `Key`s path.
205 class KeyElement {
206 /// The kind of this element.
207 final String kind;
208
209 /// The ID of this element. It must be either an `int` or a `String.
210 ///
211 /// This may be `null`, in which case it does not identify an Entity. It is
212 /// possible to insert [Entity]s with incomplete keys and let Datastore
213 /// automatically select a unused integer ID.
214 final id;
215
216 KeyElement(this.kind, this.id) {
217 if (kind == null) {
218 throw new ArgumentError("'kind' must not be null");
219 }
220 if (id != null) {
221 if (id is! int && id is! String) {
222 throw new ArgumentError("'id' must be either null, a String or an int");
223 }
224 }
225 }
226
227 int get hashCode => kind.hashCode ^ id.hashCode;
228
229 bool operator==(Object other) =>
230 other is KeyElement && kind == other.kind && id == other.id;
231
232 String toString() => "$kind.$id";
233 }
234
235 /// A relation used in query filters.
236 class FilterRelation {
237 static const FilterRelation LessThan = const FilterRelation._('<');
238 static const FilterRelation LessThanOrEqual = const FilterRelation._('<=');
239 static const FilterRelation GreatherThan = const FilterRelation._('>');
240 static const FilterRelation GreatherThanOrEqual =
241 const FilterRelation._('>=');
242 static const FilterRelation Equal = const FilterRelation._('==');
243 static const FilterRelation In = const FilterRelation._('IN');
244
245 final String name;
246
247 const FilterRelation._(this.name);
248
249 String toString() => name;
250 }
251
252 /// A filter used in queries.
253 class Filter {
254 /// The relation used for comparing `name` with `value`.
255 final FilterRelation relation;
256
257 /// The name of the datastore property used in the comparision.
258 final String name;
259
260 /// The value used for comparing against the property named by `name`.
261 final Object value;
262
263 Filter(this.relation, this.name, this.value);
264 }
265
266 /// The direction of a order.
267 ///
268 // TODO(Issue #6): Make this class Private and add the two statics to the
269 /// 'Order' class.
270 /// [i.e. so one can write Order.Ascending, Order.Descending].
271 class OrderDirection {
272 static const OrderDirection Ascending = const OrderDirection._('Ascending');
273 static const OrderDirection Decending = const OrderDirection._('Decending');
274
275 final String name;
276
277 const OrderDirection._(this.name);
278 }
279
280 /// A order used in queries.
281 class Order {
282 /// The direction of the order.
283 final OrderDirection direction;
284
285 /// The name of the property used for the order.
286 final String propertyName;
287
288 // TODO(Issue #6): Make [direction] the second argument and make it optional.
289 Order(this.direction, this.propertyName);
290 }
291
292 /// A datastore query.
293 ///
294 /// A query consists of filters (kind, ancestor and property filters), one or
295 /// more orders and a offset/limit pair.
296 ///
297 /// All fields may be optional.
298 ///
299 /// Example of building a [Query]:
300 /// var person = ....;
301 /// var query = new Query(ancestorKey: personKey, kind: 'Address')
302 class Query {
303 /// Restrict the result set to entities of this kind.
304 final String kind;
305
306 /// Restrict the result set to entities which have this ancestorKey / parent.
307 final Key ancestorKey;
308
309 /// Restrict the result set by a list of property [Filter]s.
310 final List<Filter> filters;
311
312 /// Order the matching entities following the given property [Order]s.
313 final List<Order> orders;
314
315 /// Skip the first [offset] entities in the result set.
316 final int offset;
317
318 /// Limit the number of entities returned to [limit].
319 final int limit;
320
321 Query({this.ancestorKey, this.kind, this.filters, this.orders,
322 this.offset, this.limit});
323 }
324
325 /// The result of a commit.
326 class CommitResult {
327 /// If the commit included `autoIdInserts`, this list will be the fully
328 /// populated Keys, including the automatically allocated integer IDs.
329 final List<Key> autoIdInsertKeys;
330
331 CommitResult(this.autoIdInsertKeys);
332 }
333
334 /// A blob value which can be used as a property value in `Entity`s.
335 class BlobValue {
336 /// The binary data of this blob.
337 final List<int> bytes;
338
339 BlobValue(this.bytes);
340 }
341
342 /// An opaque token returned by the `beginTransaction` method of a [Datastore].
343 ///
344 /// This token can be passed to the `commit` and `lookup` calls if they should
345 /// operate within this transaction.
346 abstract class Transaction { }
347
348 /// Interface used to talk to the Google Cloud Datastore service.
349 ///
350 /// It can be used to insert/update/delete [Entity]s, lookup/query [Entity]s
351 /// and allocate IDs from the auto ID allocation policy.
352 abstract class Datastore {
353 /// Allocate integer IDs for the partially populated [keys] given as argument.
354 ///
355 /// The returned [Key]s will be fully populated with the allocated IDs.
356 Future<List<Key>> allocateIds(List<Key> keys);
357
358 /// Starts a new transaction and returns an opaque value representing it.
359 ///
360 /// If [crossEntityGroup] is `true`, the transaction can work on up to 5
361 /// entity groups. Otherwise the transaction will be limited to only operate
362 /// on a single entity group.
363 Future<Transaction> beginTransaction({bool crossEntityGroup: false});
364
365 /// Make modifications to the datastore.
366 ///
367 /// - `inserts` are [Entity]s which have a fully populated [Key] and should
368 /// be either added to the datastore or updated.
369 ///
370 /// - `autoIdInserts` are [Entity]s which do not have a fully populated [Key]
371 /// and should be added to the dataset, automatically assiging integer IDs.
372 /// The returned [CommitResult] will contain the fuly populated keys.
373 ///
374 /// - `deletes` are a list of fully populated [Key]s which uniquely identify
375 /// the [Entity]s which should be deleted.
376 ///
377 /// If a [transaction] is given, all modifications will be done within that
378 /// transaction.
379 ///
380 /// This method might complete with a [TransactionAbortedError] error.
381 /// Users must take care of retrying transactions.
382 // TODO(Issue #6): Consider splitting `inserts` into insert/update/upsert.
383 Future<CommitResult> commit({List<Entity> inserts,
384 List<Entity> autoIdInserts,
385 List<Key> deletes,
386 Transaction transaction});
387
388 /// Roll a started transaction back.
389 Future rollback(Transaction transaction);
390
391 /// Looks up the fully populated [keys] in the datastore and returns either
392 /// the [Entity] corresponding to the [Key] or `null`. The order in the
393 /// returned [Entity]s is the same as in [keys].
394 ///
395 /// If a [transaction] is given, the lookup will be within this transaction.
396 Future<List<Entity>> lookup(List<Key> keys, {Transaction transaction});
397
398 /// Runs a query on the dataset and returns a [Page] of matching [Entity]s.
399 ///
400 /// The [Page] instance returned might not contain all matching [Entity]s -
401 /// in which case `isLast` is set to `false`. The page's `next` method can
402 /// be used to page through the whole result set.
403 /// The maximum number of [Entity]s returned within a single page is
404 /// implementation specific.
405 ///
406 /// - `query` is used to restrict the number of returned [Entity]s and may
407 /// may specify an order.
408 ///
409 /// - `partition` can be used to specify the namespace used for the lookup.
410 ///
411 /// If a [transaction] is given, the query will be within this transaction.
412 /// But note that arbitrary queries within a transaction are not possible.
413 /// A transaction is limited to a very small number of entity groups. Usually
414 /// queries with transactions are restricted by providing an ancestor filter.
415 ///
416 /// Outside of transactions, the result set might be stale. Queries are by
417 /// default eventually consistent.
418 Future<Page<Entity>> query(
419 Query query, {Partition partition, Transaction transaction});
420 }
OLDNEW
« no previous file with comments | « pkg/gcloud/lib/common.dart ('k') | pkg/gcloud/lib/db.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698