Index: pkg/gcloud/lib/src/db/annotations.dart |
diff --git a/pkg/gcloud/lib/src/db/annotations.dart b/pkg/gcloud/lib/src/db/annotations.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e158ad531947da7135c55a163d03e5824a2ce22f |
--- /dev/null |
+++ b/pkg/gcloud/lib/src/db/annotations.dart |
@@ -0,0 +1,280 @@ |
+// 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. |
+ |
+part of gcloud.db; |
+ |
+/// Annotation used to mark dart classes which can be stored into datastore. |
+/// |
+/// The `Kind` annotation on a class as well as other `Property` annotations on |
+/// fields or getters of the class itself (and any of it's superclasses) up to |
+/// the [Model] class describe the *mapping* of *dart objects* to datastore |
+/// *entities*. |
+/// |
+/// An "entity" is an object which can be stored into Google Cloud Datastore. |
+/// It contains a number of named "properties", some of them might get indexed, |
+/// others are not. A "property" value can be of a limited set of supported |
+/// types (such as `int` and `String`). |
+/// |
+/// Here is an example of a dart model class which can be stored into datastore: |
+/// @Kind() |
+/// class Person extends db.Model { |
+/// @StringProperty() |
+/// String name; |
+/// |
+/// @IntProperty() |
+/// int age; |
+/// |
+/// @DateTimeProperty() |
+/// DateTime dateOfBirth; |
+/// } |
+class Kind { |
+ /// The kind name used when saving objects to datastore. |
+ /// |
+ /// If `null` the name will be the same as the class name at which the |
+ /// annotation is placed. |
+ final String name; |
+ |
+ /// The type, either [ID_TYPE_INTEGER] or [ID_TYPE_STRING]. |
+ final IdType idType; |
+ |
+ /// Annotation specifying the name of this kind and whether to use integer or |
+ /// string `id`s. |
+ /// |
+ /// If `name` is omitted, it will default to the name of class to which this |
+ /// annotation is attached to. |
+ const Kind({this.name, this.idType: IdType.Integer}); |
+} |
+ |
+/// The type used for id's of an entity. |
+class IdType { |
+ /// Use integer ids for identifying entities. |
+ static const IdType Integer = const IdType(1); |
+ |
+ /// Use string ids for identifying entities. |
+ static const IdType String = const IdType(2); |
+ |
+ final int _type; |
+ |
+ const IdType(this._type); |
+} |
+ |
+/// Describes a property of an Entity. |
+/// |
+/// Please see [Kind] for an example on how to use them. |
+abstract class Property { |
+ /// The name of the property. |
+ /// |
+ /// If it is `null`, the name will be the same as used in the |
+ /// model class. |
+ final String propertyName; |
+ |
+ /// Specifies whether this property is required or not. |
+ /// |
+ /// If required is `true`, it will be enforced when saving model objects to |
+ /// the datastore and when retrieving them. |
+ final bool required; |
+ |
+ /// Specifies whether this property should be indexed or not. |
+ /// |
+ /// When running queries no this property, it is necessary to set [indexed] to |
+ /// `true`. |
+ final bool indexed; |
+ |
+ const Property({this.propertyName, this.required: false, this.indexed: true}); |
+ |
+ bool validate(ModelDB db, Object value) { |
+ if (required && value == null) return false; |
+ return true; |
+ } |
+ |
+ Object encodeValue(ModelDB db, Object value); |
+ |
+ Object decodePrimitiveValue(ModelDB db, Object value); |
+} |
+ |
+/// An abstract base class for primitive properties which can e.g. be used |
+/// within a composed `ListProperty`. |
+abstract class PrimitiveProperty extends Property { |
+ const PrimitiveProperty( |
+ {String propertyName, bool required: false, bool indexed: true}) |
+ : super(propertyName: propertyName, required: required, indexed: indexed); |
+ |
+ Object encodeValue(ModelDB db, Object value) => value; |
+ |
+ Object decodePrimitiveValue(ModelDB db, Object value) => value; |
+} |
+ |
+/// A boolean [Property]. |
+/// |
+/// It will validate that values are booleans before writing them to the |
+/// datastore and when reading them back. |
+class BoolProperty extends PrimitiveProperty { |
+ const BoolProperty( |
+ {String propertyName, bool required: false, bool indexed: true}) |
+ : super(propertyName: propertyName, required: required, indexed: indexed); |
+ |
+ bool validate(ModelDB db, Object value) |
+ => super.validate(db, value) && (value == null || value is bool); |
+} |
+ |
+/// A integer [Property]. |
+/// |
+/// It will validate that values are integers before writing them to the |
+/// datastore and when reading them back. |
+class IntProperty extends PrimitiveProperty { |
+ const IntProperty( |
+ {String propertyName, bool required: false, bool indexed: true}) |
+ : super(propertyName: propertyName, required: required, indexed: indexed); |
+ |
+ bool validate(ModelDB db, Object value) |
+ => super.validate(db, value) && (value == null || value is int); |
+} |
+ |
+/// A double [Property]. |
+/// |
+/// It will validate that values are doubles before writing them to the |
+/// datastore and when reading them back. |
+class DoubleProperty extends PrimitiveProperty { |
+ const DoubleProperty( |
+ {String propertyName, bool required: false, bool indexed: true}) |
+ : super(propertyName: propertyName, required: required, indexed: indexed); |
+ |
+ bool validate(ModelDB db, Object value) |
+ => super.validate(db, value) && (value == null || value is double); |
+} |
+ |
+/// A string [Property]. |
+/// |
+/// It will validate that values are strings before writing them to the |
+/// datastore and when reading them back. |
+class StringProperty extends PrimitiveProperty { |
+ const StringProperty( |
+ {String propertyName, bool required: false, bool indexed: true}) |
+ : super(propertyName: propertyName, required: required, indexed: indexed); |
+ |
+ bool validate(ModelDB db, Object value) |
+ => super.validate(db, value) && (value == null || value is String); |
+} |
+ |
+/// A key [Property]. |
+/// |
+/// It will validate that values are keys before writing them to the |
+/// datastore and when reading them back. |
+class ModelKeyProperty extends PrimitiveProperty { |
+ const ModelKeyProperty( |
+ {String propertyName, bool required: false, bool indexed: true}) |
+ : super(propertyName: propertyName, required: required, indexed: indexed); |
+ |
+ bool validate(ModelDB db, Object value) |
+ => super.validate(db, value) && (value == null || value is Key); |
+ |
+ Object encodeValue(ModelDB db, Object value) { |
+ if (value == null) return null; |
+ return db.toDatastoreKey(value); |
+ } |
+ |
+ Object decodePrimitiveValue(ModelDB db, Object value) { |
+ if (value == null) return null; |
+ return db.fromDatastoreKey(value as datastore.Key); |
+ } |
+} |
+ |
+/// A binary blob [Property]. |
+/// |
+/// It will validate that values are blobs before writing them to the |
+/// datastore and when reading them back. Blob values will be represented by |
+/// List<int>. |
+class BlobProperty extends PrimitiveProperty { |
+ const BlobProperty({String propertyName, bool required: false}) |
+ : super(propertyName: propertyName, required: required, indexed: false); |
+ |
+ // NOTE: We don't validate that the entries of the list are really integers |
+ // of the range 0..255! |
+ // If an untyped list was created the type check will always succeed. i.e. |
+ // "[1, true, 'bar'] is List<int>" evaluates to `true` |
+ bool validate(ModelDB db, Object value) |
+ => super.validate(db, value) && (value == null || value is List<int>); |
+ |
+ Object encodeValue(ModelDB db, Object value) { |
+ if (value == null) return null; |
+ return new datastore.BlobValue(value); |
+ } |
+ |
+ Object decodePrimitiveValue(ModelDB db, Object value) { |
+ if (value == null) return null; |
+ |
+ datastore.BlobValue blobValue = value; |
+ return blobValue.bytes; |
+ } |
+} |
+ |
+/// A datetime [Property]. |
+/// |
+/// It will validate that values are DateTime objects before writing them to the |
+/// datastore and when reading them back. |
+class DateTimeProperty extends PrimitiveProperty { |
+ const DateTimeProperty( |
+ {String propertyName, bool required: false, bool indexed: true}) |
+ : super(propertyName: propertyName, required: required, indexed: indexed); |
+ |
+ bool validate(ModelDB db, Object value) |
+ => super.validate(db, value) && (value == null || value is DateTime); |
+ |
+ Object decodePrimitiveValue(ModelDB db, Object value) { |
+ if (value is int) { |
+ return |
+ new DateTime.fromMillisecondsSinceEpoch(value ~/ 1000, isUtc: true); |
+ } |
+ return value; |
+ } |
+} |
+ |
+ |
+/// A composed list [Property], with a `subProperty` for the list elements. |
+/// |
+/// It will validate that values are List objects before writing them to the |
+/// datastore and when reading them back. It will also validate the elements |
+/// of the list itself. |
+class ListProperty extends Property { |
+ final PrimitiveProperty subProperty; |
+ |
+ // TODO: We want to support optional list properties as well. |
+ // Get rid of "required: true" here. |
+ const ListProperty(this.subProperty, |
+ {String propertyName, bool indexed: true}) |
+ : super(propertyName: propertyName, required: true, indexed: indexed); |
+ |
+ bool validate(ModelDB db, Object value) { |
+ if (!super.validate(db, value) || value is! List) return false; |
+ |
+ for (var entry in value) { |
+ if (!subProperty.validate(db, entry)) return false; |
+ } |
+ return true; |
+ } |
+ |
+ Object encodeValue(ModelDB db, Object value) { |
+ if (value == null) return null; |
+ List list = value; |
+ if (list.length == 0) return null; |
+ if (list.length == 1) return subProperty.encodeValue(db, list[0]); |
+ return list.map( |
+ (value) => subProperty.encodeValue(db, value)).toList(); |
+ } |
+ |
+ Object decodePrimitiveValue(ModelDB db, Object value) { |
+ if (value == null) return []; |
+ if (value is! List) return [subProperty.decodePrimitiveValue(db, value)]; |
+ return (value as List) |
+ .map((entry) => subProperty.decodePrimitiveValue(db, entry)) |
+ .toList(); |
+ } |
+} |
+ |
+/// A convenience [Property] for list of strings. |
+class StringListProperty extends ListProperty { |
+ const StringListProperty({String propertyName, bool indexed: true}) |
+ : super(const StringProperty(), |
+ propertyName: propertyName, indexed: indexed); |
+} |