| 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);
|
| +}
|
|
|