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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/gcloud/lib/common.dart ('k') | pkg/gcloud/lib/db.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/gcloud/lib/datastore.dart
diff --git a/pkg/gcloud/lib/datastore.dart b/pkg/gcloud/lib/datastore.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3f0dcf9c4e9ff1d2987e1ae32811df45294827bb
--- /dev/null
+++ b/pkg/gcloud/lib/datastore.dart
@@ -0,0 +1,420 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// This library provides a low-level API for accessing Google's Cloud
+/// Datastore.
+///
+/// For more information on Cloud Datastore, please refer to the following
+/// developers page: https://cloud.google.com/datastore/docs
+library gcloud.datastore;
+
+import 'dart:async';
+
+import 'common.dart' show Page;
+import 'service_scope.dart' as ss;
+
+const Symbol _datastoreKey = #_gcloud.datastore;
+
+/// Access the [Datastore] object available in the current service scope.
+///
+/// The returned object will be the one which was previously registered with
+/// [registerDatastoreService] within the current (or a parent) service scope.
+///
+/// Accessing this getter outside of a service scope will result in an error.
+/// See the `package:gcloud/service_scope.dart` library for more information.
+Datastore get datastoreService => ss.lookup(_datastoreKey);
+
+/// Registers the [Datastore] object within the current service scope.
+///
+/// The provided `datastore` object will be avilable via the top-level
+/// `datastore` getter.
+///
+/// Calling this function outside of a service scope will result in an error.
+/// Calling this function more than once inside the same service scope is not
+/// allowed.
+void registerDatastoreService(Datastore datastore) {
+ ss.register(_datastoreKey, datastore);
+}
+
+class ApplicationError implements Exception {
+ final String message;
+ ApplicationError(this.message);
+
+ String toString() => "ApplicationError: $message";
+}
+
+
+class DatastoreError implements Exception {
+ final String message;
+
+ DatastoreError([String message]) : message =
+ (message != null ?message : 'DatastoreError: An unknown error occured');
+
+ String toString() => '$message';
+}
+
+class UnknownDatastoreError extends DatastoreError {
+ UnknownDatastoreError(error) : super("An unknown error occured ($error).");
+}
+
+class TransactionAbortedError extends DatastoreError {
+ TransactionAbortedError() : super("The transaction was aborted.");
+}
+
+class TimeoutError extends DatastoreError {
+ TimeoutError() : super("The operation timed out.");
+}
+
+/// Thrown when a query would require an index which was not set.
+///
+/// An application needs to specify indices in a `index.yaml` file and needs to
+/// create indices using the `gcloud preview datastore create-indexes` command.
+class NeedIndexError extends DatastoreError {
+ NeedIndexError()
+ : super("An index is needed for the query to succeed.");
+}
+
+class PermissionDeniedError extends DatastoreError {
+ PermissionDeniedError() : super("Permission denied.");
+}
+
+class InternalError extends DatastoreError {
+ InternalError() : super("Internal service error.");
+}
+
+class QuotaExceededError extends DatastoreError {
+ QuotaExceededError(error) : super("Quota was exceeded ($error).");
+}
+
+/// A datastore Entity
+///
+/// An entity is identified by a unique `key` and consists of a number of
+/// `properties`. If a property should not be indexed, it needs to be included
+/// in the `unIndexedProperties` set.
+///
+/// The `properties` field maps names to values. Values can be of a primitive
+/// type or of a composed type.
+///
+/// The following primitive types are supported:
+/// bool, int, double, String, DateTime, BlobValue, Key
+///
+/// It is possible to have a `List` of values. The values must be primitive.
+/// Lists inside lists are not supported.
+///
+/// Whether a property is indexed or not applies to all values (this is only
+/// relevant if the value is a list of primitive values).
+class Entity {
+ final Key key;
+ final Map<String, Object> properties;
+ final Set<String> unIndexedProperties;
+
+ Entity(this.key, this.properties, {this.unIndexedProperties});
+}
+
+/// A complete or partial key.
+///
+/// A key can uniquely identifiy a datastore `Entity`s. It consists of a
+/// partition and path. The path consists of one or more `KeyElement`s.
+///
+/// A key may be incomplete. This is usesfull when inserting `Entity`s which IDs
+/// should be automatically allocated.
+///
+/// Example of a fully populated [Key]:
+///
+/// var fullKey = new Key([new KeyElement('Person', 1),
+/// new KeyElement('Address', 2)]);
+///
+/// Example of a partially populated [Key] / an imcomplete [Key]:
+///
+/// var partialKey = new Key([new KeyElement('Person', 1),
+/// new KeyElement('Address', null)]);
+class Key {
+ /// The partition of this `Key`.
+ final Partition partition;
+
+ /// The path of `KeyElement`s.
+ final List<KeyElement> elements;
+
+ Key(this.elements, {Partition partition})
+ : this.partition = (partition == null) ? Partition.DEFAULT : partition;
+
+ factory Key.fromParent(String kind, int id, {Key parent}) {
+ var partition;
+ var elements = [];
+ if (parent != null) {
+ partition = parent.partition;
+ elements.addAll(parent.elements);
+ }
+ elements.add(new KeyElement(kind, id));
+ return new Key(elements, partition: partition);
+ }
+
+ int get hashCode =>
+ elements.fold(partition.hashCode, (a, b) => a ^ b.hashCode);
+
+ bool operator==(Object other) {
+ if (identical(this, other)) return true;
+
+ if (other is Key &&
+ partition == other.partition &&
+ elements.length == other.elements.length) {
+ for (int i = 0; i < elements.length; i++) {
+ if (elements[i] != other.elements[i]) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ String toString() {
+ var namespaceString =
+ partition.namespace == null ? 'null' : "'${partition.namespace}'";
+ return "Key(namespace=$namespaceString, path=[${elements.join(', ')}])";
+ }
+}
+
+/// A datastore partition.
+///
+/// A partition is used for partitioning a dataset into multiple namespaces.
+/// The default namespace is `null`. Using empty Strings as namespaces is
+/// invalid.
+///
+// TODO(Issue #6): Add dataset-id here.
+class Partition {
+ static const Partition DEFAULT = const Partition._default();
+
+ /// The namespace of this partition.
+ final String namespace;
+
+ Partition(this.namespace) {
+ if (namespace == '') {
+ throw new ArgumentError("'namespace' must not be empty");
+ }
+ }
+
+ const Partition._default() : this.namespace = null;
+
+ int get hashCode => namespace.hashCode;
+
+ bool operator==(Object other) =>
+ other is Partition && namespace == other.namespace;
+}
+
+/// An element in a `Key`s path.
+class KeyElement {
+ /// The kind of this element.
+ final String kind;
+
+ /// The ID of this element. It must be either an `int` or a `String.
+ ///
+ /// This may be `null`, in which case it does not identify an Entity. It is
+ /// possible to insert [Entity]s with incomplete keys and let Datastore
+ /// automatically select a unused integer ID.
+ final id;
+
+ KeyElement(this.kind, this.id) {
+ if (kind == null) {
+ throw new ArgumentError("'kind' must not be null");
+ }
+ if (id != null) {
+ if (id is! int && id is! String) {
+ throw new ArgumentError("'id' must be either null, a String or an int");
+ }
+ }
+ }
+
+ int get hashCode => kind.hashCode ^ id.hashCode;
+
+ bool operator==(Object other) =>
+ other is KeyElement && kind == other.kind && id == other.id;
+
+ String toString() => "$kind.$id";
+}
+
+/// A relation used in query filters.
+class FilterRelation {
+ static const FilterRelation LessThan = const FilterRelation._('<');
+ static const FilterRelation LessThanOrEqual = const FilterRelation._('<=');
+ static const FilterRelation GreatherThan = const FilterRelation._('>');
+ static const FilterRelation GreatherThanOrEqual =
+ const FilterRelation._('>=');
+ static const FilterRelation Equal = const FilterRelation._('==');
+ static const FilterRelation In = const FilterRelation._('IN');
+
+ final String name;
+
+ const FilterRelation._(this.name);
+
+ String toString() => name;
+}
+
+/// A filter used in queries.
+class Filter {
+ /// The relation used for comparing `name` with `value`.
+ final FilterRelation relation;
+
+ /// The name of the datastore property used in the comparision.
+ final String name;
+
+ /// The value used for comparing against the property named by `name`.
+ final Object value;
+
+ Filter(this.relation, this.name, this.value);
+}
+
+/// The direction of a order.
+///
+// TODO(Issue #6): Make this class Private and add the two statics to the
+/// 'Order' class.
+/// [i.e. so one can write Order.Ascending, Order.Descending].
+class OrderDirection {
+ static const OrderDirection Ascending = const OrderDirection._('Ascending');
+ static const OrderDirection Decending = const OrderDirection._('Decending');
+
+ final String name;
+
+ const OrderDirection._(this.name);
+}
+
+/// A order used in queries.
+class Order {
+ /// The direction of the order.
+ final OrderDirection direction;
+
+ /// The name of the property used for the order.
+ final String propertyName;
+
+ // TODO(Issue #6): Make [direction] the second argument and make it optional.
+ Order(this.direction, this.propertyName);
+}
+
+/// A datastore query.
+///
+/// A query consists of filters (kind, ancestor and property filters), one or
+/// more orders and a offset/limit pair.
+///
+/// All fields may be optional.
+///
+/// Example of building a [Query]:
+/// var person = ....;
+/// var query = new Query(ancestorKey: personKey, kind: 'Address')
+class Query {
+ /// Restrict the result set to entities of this kind.
+ final String kind;
+
+ /// Restrict the result set to entities which have this ancestorKey / parent.
+ final Key ancestorKey;
+
+ /// Restrict the result set by a list of property [Filter]s.
+ final List<Filter> filters;
+
+ /// Order the matching entities following the given property [Order]s.
+ final List<Order> orders;
+
+ /// Skip the first [offset] entities in the result set.
+ final int offset;
+
+ /// Limit the number of entities returned to [limit].
+ final int limit;
+
+ Query({this.ancestorKey, this.kind, this.filters, this.orders,
+ this.offset, this.limit});
+}
+
+/// The result of a commit.
+class CommitResult {
+ /// If the commit included `autoIdInserts`, this list will be the fully
+ /// populated Keys, including the automatically allocated integer IDs.
+ final List<Key> autoIdInsertKeys;
+
+ CommitResult(this.autoIdInsertKeys);
+}
+
+/// A blob value which can be used as a property value in `Entity`s.
+class BlobValue {
+ /// The binary data of this blob.
+ final List<int> bytes;
+
+ BlobValue(this.bytes);
+}
+
+/// An opaque token returned by the `beginTransaction` method of a [Datastore].
+///
+/// This token can be passed to the `commit` and `lookup` calls if they should
+/// operate within this transaction.
+abstract class Transaction { }
+
+/// Interface used to talk to the Google Cloud Datastore service.
+///
+/// It can be used to insert/update/delete [Entity]s, lookup/query [Entity]s
+/// and allocate IDs from the auto ID allocation policy.
+abstract class Datastore {
+ /// Allocate integer IDs for the partially populated [keys] given as argument.
+ ///
+ /// The returned [Key]s will be fully populated with the allocated IDs.
+ Future<List<Key>> allocateIds(List<Key> keys);
+
+ /// Starts a new transaction and returns an opaque value representing it.
+ ///
+ /// If [crossEntityGroup] is `true`, the transaction can work on up to 5
+ /// entity groups. Otherwise the transaction will be limited to only operate
+ /// on a single entity group.
+ Future<Transaction> beginTransaction({bool crossEntityGroup: false});
+
+ /// Make modifications to the datastore.
+ ///
+ /// - `inserts` are [Entity]s which have a fully populated [Key] and should
+ /// be either added to the datastore or updated.
+ ///
+ /// - `autoIdInserts` are [Entity]s which do not have a fully populated [Key]
+ /// and should be added to the dataset, automatically assiging integer IDs.
+ /// The returned [CommitResult] will contain the fuly populated keys.
+ ///
+ /// - `deletes` are a list of fully populated [Key]s which uniquely identify
+ /// the [Entity]s which should be deleted.
+ ///
+ /// If a [transaction] is given, all modifications will be done within that
+ /// transaction.
+ ///
+ /// This method might complete with a [TransactionAbortedError] error.
+ /// Users must take care of retrying transactions.
+ // TODO(Issue #6): Consider splitting `inserts` into insert/update/upsert.
+ Future<CommitResult> commit({List<Entity> inserts,
+ List<Entity> autoIdInserts,
+ List<Key> deletes,
+ Transaction transaction});
+
+ /// Roll a started transaction back.
+ Future rollback(Transaction transaction);
+
+ /// Looks up the fully populated [keys] in the datastore and returns either
+ /// the [Entity] corresponding to the [Key] or `null`. The order in the
+ /// returned [Entity]s is the same as in [keys].
+ ///
+ /// If a [transaction] is given, the lookup will be within this transaction.
+ Future<List<Entity>> lookup(List<Key> keys, {Transaction transaction});
+
+ /// Runs a query on the dataset and returns a [Page] of matching [Entity]s.
+ ///
+ /// The [Page] instance returned might not contain all matching [Entity]s -
+ /// in which case `isLast` is set to `false`. The page's `next` method can
+ /// be used to page through the whole result set.
+ /// The maximum number of [Entity]s returned within a single page is
+ /// implementation specific.
+ ///
+ /// - `query` is used to restrict the number of returned [Entity]s and may
+ /// may specify an order.
+ ///
+ /// - `partition` can be used to specify the namespace used for the lookup.
+ ///
+ /// If a [transaction] is given, the query will be within this transaction.
+ /// But note that arbitrary queries within a transaction are not possible.
+ /// A transaction is limited to a very small number of entity groups. Usually
+ /// queries with transactions are restricted by providing an ancestor filter.
+ ///
+ /// Outside of transactions, the result set might be stale. Queries are by
+ /// default eventually consistent.
+ Future<Page<Entity>> query(
+ Query query, {Partition partition, Transaction transaction});
+}
« 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