Add support for accessing linking objects / backlinks (#1101)
* Add support for linkingObjects * Test linkingObjects * Borrow names helper from list tests * include computed properties when serializing the schema for the RN debugger * add API docs * review comments * Expose admin users to JS (#1100) The JS binding used to conflate `SyncUser::is_admin()` with the user being created by calling `Realm.Sync.User.adminToken()`, but now that we expose a user’s role on the server under `is_admin()` this supposition is no longer correct. #1097 attempted to fix one such case, but fixing it only uncovered another: in `UserClass<T>::all_users()`. I’ve gone through all the callsites of `SyncUser::is_admin()` to make sure they don’t assume an admin token user. * [1.8.3] Bump version * add linkingObjects method to Realm.Object * changelog
This commit is contained in:
parent
f1695f33db
commit
9e0a9a3bd3
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,3 +1,14 @@
|
||||||
|
vNext Release notes (TBD)
|
||||||
|
=============================================================
|
||||||
|
### Breaking changes
|
||||||
|
* None
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* Add support for Linking Objects (AKA Backlinks).
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* Node
|
||||||
|
|
||||||
1.8.3 Release notes (2017-6-27)
|
1.8.3 Release notes (2017-6-27)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
|
@ -36,4 +36,14 @@ class Object {
|
||||||
* @since 1.8.1
|
* @since 1.8.1
|
||||||
*/
|
*/
|
||||||
objectSchema() {}
|
objectSchema() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the objects that link to this object in the specified relationship.
|
||||||
|
* @param {string} objectType - The type of the objects that link to this object's type.
|
||||||
|
* @param {string} property - The name of the property that references objects of this object's type.
|
||||||
|
* @throws {Error} If the relationship is not valid.
|
||||||
|
* @returns {Realm.Results} the objects that link to this object.
|
||||||
|
* @since 1.9.0
|
||||||
|
*/
|
||||||
|
linkingObjects(objectType, property) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,8 +241,10 @@ Realm.defaultPath;
|
||||||
* @typedef Realm~ObjectSchemaProperty
|
* @typedef Realm~ObjectSchemaProperty
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @property {Realm~PropertyType} type - The type of this property.
|
* @property {Realm~PropertyType} type - The type of this property.
|
||||||
* @property {string} [objectType] - **Required** when `type` is `"list"`, and must match the
|
* @property {string} [objectType] - **Required** when `type` is `"list"` or `"linkingObjects"`,
|
||||||
* type of an object in the same schema.
|
* and must match the type of an object in the same schema.
|
||||||
|
* @property {string} [property] - **Required** when `type` is `"linkingObjects"`, and must match
|
||||||
|
* the name of a property on the type specified in `objectType` that links to the type this property belongs to.
|
||||||
* @property {any} [default] - The default value for this property on creation when not
|
* @property {any} [default] - The default value for this property on creation when not
|
||||||
* otherwise specified.
|
* otherwise specified.
|
||||||
* @property {boolean} [optional] - Signals if this property may be assigned `null` or `undefined`.
|
* @property {boolean} [optional] - Signals if this property may be assigned `null` or `undefined`.
|
||||||
|
@ -262,7 +264,7 @@ Realm.defaultPath;
|
||||||
* A property type may be specified as one of the standard builtin types, or as an object type
|
* A property type may be specified as one of the standard builtin types, or as an object type
|
||||||
* inside the same schema.
|
* inside the same schema.
|
||||||
* @typedef Realm~PropertyType
|
* @typedef Realm~PropertyType
|
||||||
* @type {("bool"|"int"|"float"|"double"|"string"|"date"|"data"|"list"|"<ObjectType>")}
|
* @type {("bool"|"int"|"float"|"double"|"string"|"date"|"data"|"list"|"linkingObjects"|"<ObjectType>")}
|
||||||
* @property {boolean} "bool" - Property value may either be `true` or `false`.
|
* @property {boolean} "bool" - Property value may either be `true` or `false`.
|
||||||
* @property {number} "int" - Property may be assigned any number, but will be stored as a
|
* @property {number} "int" - Property may be assigned any number, but will be stored as a
|
||||||
* round integer, meaning anything after the decimal will be truncated.
|
* round integer, meaning anything after the decimal will be truncated.
|
||||||
|
@ -278,6 +280,9 @@ Realm.defaultPath;
|
||||||
* @property {Realm.List} "list" - Property may be assigned any ordered collection
|
* @property {Realm.List} "list" - Property may be assigned any ordered collection
|
||||||
* (e.g. `Array`, {@link Realm.List}, {@link Realm.Results}) of objects all matching the
|
* (e.g. `Array`, {@link Realm.List}, {@link Realm.Results}) of objects all matching the
|
||||||
* `objectType` specified in the {@link Realm~ObjectSchemaProperty ObjectSchemaProperty}.
|
* `objectType` specified in the {@link Realm~ObjectSchemaProperty ObjectSchemaProperty}.
|
||||||
|
* @property {Realm.Results} "linkingObjects" - Property is read-only and always returns a {@link Realm.Results}
|
||||||
|
* of all the objects matching the `objectType` that are linking to the current object
|
||||||
|
* through the `property` relationship specified in {@link Realm~ObjectSchemaProperty ObjectSchemaProperty}.
|
||||||
* @property {Realm.Object} "<ObjectType>" - A string that matches the `name` of an object in the
|
* @property {Realm.Object} "<ObjectType>" - A string that matches the `name` of an object in the
|
||||||
* same schema (see {@link Realm~ObjectSchema ObjectSchema}) – this property may be assigned
|
* same schema (see {@link Realm~ObjectSchema ObjectSchema}) – this property may be assigned
|
||||||
* any object of this type from inside the same Realm, and will always be _optional_
|
* any object of this type from inside the same Realm, and will always be _optional_
|
||||||
|
|
|
@ -30,7 +30,8 @@ export default class RealmObject {
|
||||||
// Non-mutating methods:
|
// Non-mutating methods:
|
||||||
createMethods(RealmObject.prototype, objectTypes.OBJECT, [
|
createMethods(RealmObject.prototype, objectTypes.OBJECT, [
|
||||||
'isValid',
|
'isValid',
|
||||||
'objectSchema'
|
'objectSchema',
|
||||||
|
'linkingObjects'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function clearRegisteredConstructors() {
|
export function clearRegisteredConstructors() {
|
||||||
|
|
|
@ -24,7 +24,7 @@ declare namespace Realm {
|
||||||
* PropertyType
|
* PropertyType
|
||||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.html#~PropertyType }
|
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.html#~PropertyType }
|
||||||
*/
|
*/
|
||||||
type PropertyType = string | 'bool' | 'int' | 'float' | 'double' | 'string' | 'data' | 'date' | 'list';
|
type PropertyType = string | 'bool' | 'int' | 'float' | 'double' | 'string' | 'data' | 'date' | 'list' | 'linkingObjects';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ObjectSchemaProperty
|
* ObjectSchemaProperty
|
||||||
|
@ -33,6 +33,7 @@ declare namespace Realm {
|
||||||
interface ObjectSchemaProperty {
|
interface ObjectSchemaProperty {
|
||||||
type: PropertyType;
|
type: PropertyType;
|
||||||
objectType?: string;
|
objectType?: string;
|
||||||
|
property?: string;
|
||||||
default?: any;
|
default?: any;
|
||||||
optional?: boolean;
|
optional?: boolean;
|
||||||
indexed?: boolean;
|
indexed?: boolean;
|
||||||
|
@ -102,6 +103,11 @@ declare namespace Realm {
|
||||||
* @returns ObjectSchema
|
* @returns ObjectSchema
|
||||||
*/
|
*/
|
||||||
objectSchema(): ObjectSchema;
|
objectSchema(): ObjectSchema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Results<T>
|
||||||
|
*/
|
||||||
|
linkingObjects<T>(objectType: string, property: string): Results<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Object: {
|
const Object: {
|
||||||
|
|
|
@ -52,6 +52,7 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
|
||||||
|
|
||||||
static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||||
static void get_object_schema(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
static void get_object_schema(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||||
|
static void linking_objects(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||||
|
|
||||||
const std::string name = "RealmObject";
|
const std::string name = "RealmObject";
|
||||||
|
|
||||||
|
@ -64,6 +65,7 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
|
||||||
MethodMap<T> const methods = {
|
MethodMap<T> const methods = {
|
||||||
{"isValid", wrap<is_valid>},
|
{"isValid", wrap<is_valid>},
|
||||||
{"objectSchema", wrap<get_object_schema>},
|
{"objectSchema", wrap<get_object_schema>},
|
||||||
|
{"linkingObjects", wrap<linking_objects>},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,3 +154,37 @@ std::vector<String<T>> RealmObjectClass<T>::get_property_names(ContextType ctx,
|
||||||
|
|
||||||
} // js
|
} // js
|
||||||
} // realm
|
} // realm
|
||||||
|
|
||||||
|
// move this all the way here because it needs to include "js_results.hpp" which in turn includes this file
|
||||||
|
|
||||||
|
#include "js_results.hpp"
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void realm::js::RealmObjectClass<T>::linking_objects(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||||
|
validate_argument_count(argc, 2);
|
||||||
|
|
||||||
|
std::string object_type = Value::validated_to_string(ctx, arguments[0], "objectType");
|
||||||
|
std::string property_name = Value::validated_to_string(ctx, arguments[1], "property");
|
||||||
|
|
||||||
|
auto object = get_internal<T, RealmObjectClass<T>>(this_object);
|
||||||
|
|
||||||
|
auto target_object_schema = object->realm()->schema().find(object_type);
|
||||||
|
if (target_object_schema == object->realm()->schema().end()) {
|
||||||
|
throw std::logic_error(util::format("Could not find schema for type '%1'", object_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto link_property = target_object_schema->property_for_name(property_name);
|
||||||
|
if (!link_property) {
|
||||||
|
throw std::logic_error(util::format("Type '%1' does not contain property '%2'", object_type, property_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link_property->object_type != object->get_object_schema().name) {
|
||||||
|
throw std::logic_error(util::format("'%1.%2' is not a relationship to '%3'", object_type, property_name, object->get_object_schema().name));
|
||||||
|
}
|
||||||
|
|
||||||
|
realm::TableRef table = ObjectStore::table_for_object_type(object->realm()->read_group(), target_object_schema->name);
|
||||||
|
auto row = object->row();
|
||||||
|
auto tv = row.get_table()->get_backlink_view(row.get_index(), table.get(), link_property->table_column);
|
||||||
|
|
||||||
|
return_value.set(ResultsClass<T>::create_instance(ctx, realm::Results(object->realm(), std::move(tv))));
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ Property Schema<T>::parse_property(ContextType ctx, ValueType attributes, std::s
|
||||||
static const String type_string = "type";
|
static const String type_string = "type";
|
||||||
static const String object_type_string = "objectType";
|
static const String object_type_string = "objectType";
|
||||||
static const String optional_string = "optional";
|
static const String optional_string = "optional";
|
||||||
|
static const String property_string = "property";
|
||||||
|
|
||||||
Property prop;
|
Property prop;
|
||||||
prop.name = property_name;
|
prop.name = property_name;
|
||||||
|
@ -123,20 +124,29 @@ Property Schema<T>::parse_property(ContextType ctx, ValueType attributes, std::s
|
||||||
prop.type = realm::PropertyType::Array;
|
prop.type = realm::PropertyType::Array;
|
||||||
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||||
}
|
}
|
||||||
else {
|
else if (type == "linkingObjects") {
|
||||||
|
prop.type = realm::PropertyType::LinkingObjects;
|
||||||
|
|
||||||
|
if (!Value::is_valid(property_object)) {
|
||||||
|
throw std::runtime_error("Object property must specify 'objectType'");
|
||||||
|
}
|
||||||
|
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||||
|
prop.link_origin_property_name = Object::validated_get_string(ctx, property_object, property_string);
|
||||||
|
}
|
||||||
|
else if (type == "object") {
|
||||||
prop.type = realm::PropertyType::Object;
|
prop.type = realm::PropertyType::Object;
|
||||||
prop.is_nullable = true;
|
prop.is_nullable = true;
|
||||||
|
|
||||||
// The type could either be 'object' or the name of another object type in the same schema.
|
if (!Value::is_valid(property_object)) {
|
||||||
if (type == "object") {
|
throw std::runtime_error("Object property must specify 'objectType'");
|
||||||
if (!Value::is_valid(property_object)) {
|
|
||||||
throw std::runtime_error("Object property must specify 'objectType'");
|
|
||||||
}
|
|
||||||
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
prop.object_type = type;
|
|
||||||
}
|
}
|
||||||
|
prop.object_type = Object::validated_get_string(ctx, property_object, object_type_string);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The type could be the name of another object type in the same schema.
|
||||||
|
prop.type = realm::PropertyType::Object;
|
||||||
|
prop.is_nullable = true;
|
||||||
|
prop.object_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Value::is_valid(property_object)) {
|
if (Value::is_valid(property_object)) {
|
||||||
|
@ -177,14 +187,27 @@ ObjectSchema Schema<T>::parse_object_schema(ContextType ctx, ObjectType object_s
|
||||||
for (uint32_t i = 0; i < length; i++) {
|
for (uint32_t i = 0; i < length; i++) {
|
||||||
ObjectType property_object = Object::validated_get_object(ctx, properties_object, i);
|
ObjectType property_object = Object::validated_get_object(ctx, properties_object, i);
|
||||||
std::string property_name = Object::validated_get_string(ctx, property_object, name_string);
|
std::string property_name = Object::validated_get_string(ctx, property_object, name_string);
|
||||||
object_schema.persisted_properties.emplace_back(parse_property(ctx, property_object, property_name, object_defaults));
|
Property property = parse_property(ctx, property_object, property_name, object_defaults);
|
||||||
|
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||||
|
object_schema.computed_properties.emplace_back(std::move(property));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
object_schema.persisted_properties.emplace_back(std::move(property));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto property_names = Object::get_property_names(ctx, properties_object);
|
auto property_names = Object::get_property_names(ctx, properties_object);
|
||||||
for (auto &property_name : property_names) {
|
for (auto &property_name : property_names) {
|
||||||
ValueType property_value = Object::get_property(ctx, properties_object, property_name);
|
ValueType property_value = Object::get_property(ctx, properties_object, property_name);
|
||||||
object_schema.persisted_properties.emplace_back(parse_property(ctx, property_value, property_name, object_defaults));
|
Property property = parse_property(ctx, property_value, property_name, object_defaults);
|
||||||
|
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||||
|
object_schema.computed_properties.emplace_back(std::move(property));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
object_schema.persisted_properties.emplace_back(std::move(property));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,6 +266,9 @@ typename T::Object Schema<T>::object_for_object_schema(ContextType ctx, const Ob
|
||||||
for (auto& property : object_schema.persisted_properties) {
|
for (auto& property : object_schema.persisted_properties) {
|
||||||
Object::set_property(ctx, properties, property.name, object_for_property(ctx, property));
|
Object::set_property(ctx, properties, property.name, object_for_property(ctx, property));
|
||||||
}
|
}
|
||||||
|
for (auto& property : object_schema.computed_properties) {
|
||||||
|
Object::set_property(ctx, properties, property.name, object_for_property(ctx, property));
|
||||||
|
}
|
||||||
|
|
||||||
static const String properties_string = "properties";
|
static const String properties_string = "properties";
|
||||||
Object::set_property(ctx, object, properties_string, properties);
|
Object::set_property(ctx, object, properties_string, properties);
|
||||||
|
@ -271,6 +297,11 @@ typename T::Object Schema<T>::object_for_property(ContextType ctx, const Propert
|
||||||
Object::set_property(ctx, object, object_type_string, Value::from_string(ctx, property.object_type));
|
Object::set_property(ctx, object, object_type_string, Value::from_string(ctx, property.object_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const String property_string = "property";
|
||||||
|
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||||
|
Object::set_property(ctx, object, property_string, Value::from_string(ctx, property.link_origin_property_name));
|
||||||
|
}
|
||||||
|
|
||||||
static const String indexed_string = "indexed";
|
static const String indexed_string = "indexed";
|
||||||
if (property.is_indexed) {
|
if (property.is_indexed) {
|
||||||
Object::set_property(ctx, object, indexed_string, Value::from_boolean(ctx, true));
|
Object::set_property(ctx, object, indexed_string, Value::from_boolean(ctx, true));
|
||||||
|
|
|
@ -463,6 +463,10 @@ json RPCServer::serialize_object_schema(const realm::ObjectSchema &object_schema
|
||||||
properties.push_back(prop.name);
|
properties.push_back(prop.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto &prop : object_schema.computed_properties) {
|
||||||
|
properties.push_back(prop.name);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{"name", object_schema.name},
|
{"name", object_schema.name},
|
||||||
{"properties", properties},
|
{"properties", properties},
|
||||||
|
|
|
@ -22,6 +22,7 @@ var Realm = require('realm');
|
||||||
|
|
||||||
var TESTS = {
|
var TESTS = {
|
||||||
ListTests: require('./list-tests'),
|
ListTests: require('./list-tests'),
|
||||||
|
LinkingObjectsTests: require('./linkingobjects-tests'),
|
||||||
ObjectTests: require('./object-tests'),
|
ObjectTests: require('./object-tests'),
|
||||||
RealmTests: require('./realm-tests'),
|
RealmTests: require('./realm-tests'),
|
||||||
ResultsTests: require('./results-tests'),
|
ResultsTests: require('./results-tests'),
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2017 Realm Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Realm = require('realm');
|
||||||
|
var TestCase = require('./asserts');
|
||||||
|
var schemas = require('./schemas');
|
||||||
|
|
||||||
|
function names(results) {
|
||||||
|
return results.map(function(object) {
|
||||||
|
return object.name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
testBasics: function() {
|
||||||
|
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||||
|
|
||||||
|
var olivier, oliviersParents;
|
||||||
|
realm.write(function() {
|
||||||
|
olivier = realm.create('PersonObject', {name: 'Olivier', age: 0});
|
||||||
|
realm.create('PersonObject', {name: 'Christine', age: 25, children: [olivier]});
|
||||||
|
oliviersParents = olivier.parents;
|
||||||
|
|
||||||
|
TestCase.assertArraysEqual(names(oliviersParents), ['Christine']);
|
||||||
|
});
|
||||||
|
|
||||||
|
TestCase.assertArraysEqual(names(oliviersParents), ['Christine']);
|
||||||
|
|
||||||
|
var jp;
|
||||||
|
realm.write(function() {
|
||||||
|
jp = realm.create('PersonObject', {name: 'JP', age: 28, children: [olivier]});
|
||||||
|
|
||||||
|
TestCase.assertArraysEqual(names(oliviersParents), ['Christine', 'JP']);
|
||||||
|
});
|
||||||
|
|
||||||
|
realm.write(function() {
|
||||||
|
realm.delete(olivier);
|
||||||
|
|
||||||
|
TestCase.assertEqual(oliviersParents.length, 0);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testFilteredLinkingObjects: function() {
|
||||||
|
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||||
|
|
||||||
|
var christine, olivier, oliviersParents;
|
||||||
|
realm.write(function() {
|
||||||
|
olivier = realm.create('PersonObject', {name: 'Olivier', age: 0});
|
||||||
|
christine = realm.create('PersonObject', {name: 'Christine', age: 25, children: [olivier]});
|
||||||
|
realm.create('PersonObject', {name: 'JP', age: 28, children: [olivier]});
|
||||||
|
oliviersParents = olivier.parents;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Three separate queries so that accessing a property on one doesn't invalidate testing of other properties.
|
||||||
|
var resultsA = oliviersParents.filtered('age > 25');
|
||||||
|
var resultsB = oliviersParents.filtered('age > 25');
|
||||||
|
var resultsC = oliviersParents.filtered('age > 25');
|
||||||
|
|
||||||
|
realm.write(function() {
|
||||||
|
var removed = christine.children.splice(0);
|
||||||
|
TestCase.assertEqual(removed.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
TestCase.assertEqual(resultsA.length, 1);
|
||||||
|
TestCase.assertEqual(resultsB.filtered("name = 'Christine'").length, 0);
|
||||||
|
TestCase.assertArraysEqual(names(resultsC), ['JP']);
|
||||||
|
},
|
||||||
|
|
||||||
|
testMethod: function() {
|
||||||
|
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||||
|
|
||||||
|
var person;
|
||||||
|
realm.write(function () {
|
||||||
|
person = realm.create('PersonObject', { name: 'Person 1', age: 50 });
|
||||||
|
});
|
||||||
|
|
||||||
|
TestCase.assertThrows(() => person.linkingObjects('NoSuchSchema', 'noSuchProperty'),
|
||||||
|
"Could not find schema for type 'NoSuchSchema'");
|
||||||
|
|
||||||
|
TestCase.assertThrows(() => person.linkingObjects('PersonObject', 'noSuchProperty'),
|
||||||
|
"Type 'PersonObject' does not contain property 'noSuchProperty'");
|
||||||
|
|
||||||
|
TestCase.assertThrows(() => person.linkingObjects('PersonObject', 'name'),
|
||||||
|
"'PersonObject.name' is not a relationship to 'PersonObject'");
|
||||||
|
|
||||||
|
var olivier, oliviersParents;
|
||||||
|
realm.write(function() {
|
||||||
|
olivier = realm.create('PersonObject', {name: 'Olivier', age: 0});
|
||||||
|
realm.create('PersonObject', {name: 'Christine', age: 25, children: [olivier]});
|
||||||
|
oliviersParents = olivier.linkingObjects('PersonObject', 'children');
|
||||||
|
|
||||||
|
TestCase.assertArraysEqual(names(oliviersParents), ['Christine']);
|
||||||
|
});
|
||||||
|
|
||||||
|
TestCase.assertArraysEqual(names(oliviersParents), ['Christine']);
|
||||||
|
|
||||||
|
var jp;
|
||||||
|
realm.write(function() {
|
||||||
|
jp = realm.create('PersonObject', {name: 'JP', age: 28, children: [olivier]});
|
||||||
|
|
||||||
|
TestCase.assertArraysEqual(names(oliviersParents), ['Christine', 'JP']);
|
||||||
|
});
|
||||||
|
|
||||||
|
realm.write(function() {
|
||||||
|
realm.delete(olivier);
|
||||||
|
|
||||||
|
TestCase.assertEqual(oliviersParents.length, 0);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
|
@ -123,6 +123,43 @@ module.exports = {
|
||||||
TestCase.assertThrows(function() {
|
TestCase.assertThrows(function() {
|
||||||
new Realm({schema: [{properties: {intCol: 'int'}}]});
|
new Realm({schema: [{properties: {intCol: 'int'}}]});
|
||||||
}, 'The schema should be an array of ObjectSchema objects');
|
}, 'The schema should be an array of ObjectSchema objects');
|
||||||
|
|
||||||
|
// linkingObjects property where the source property is missing
|
||||||
|
TestCase.assertThrows(function() {
|
||||||
|
new Realm({schema: [{
|
||||||
|
name: 'InvalidObject',
|
||||||
|
properties: {
|
||||||
|
linkingObjects: {type:'linkingObjects', objectType: 'InvalidObject', property: 'nosuchproperty'}
|
||||||
|
}
|
||||||
|
}]});
|
||||||
|
}, "Property 'InvalidObject.nosuchproperty' declared as origin of linking objects property 'InvalidObject.linkingObjects' does not exist");
|
||||||
|
|
||||||
|
// linkingObjects property where the source property is not a link
|
||||||
|
TestCase.assertThrows(function() {
|
||||||
|
new Realm({schema: [{
|
||||||
|
name: 'InvalidObject',
|
||||||
|
properties: {
|
||||||
|
integer: 'int',
|
||||||
|
linkingObjects: {type:'linkingObjects', objectType: 'InvalidObject', property: 'integer'}
|
||||||
|
}
|
||||||
|
}]});
|
||||||
|
}, "Property 'InvalidObject.integer' declared as origin of linking objects property 'InvalidObject.linkingObjects' is not a link")
|
||||||
|
|
||||||
|
// linkingObjects property where the source property links elsewhere
|
||||||
|
TestCase.assertThrows(function() {
|
||||||
|
new Realm({schema: [{
|
||||||
|
name: 'InvalidObject',
|
||||||
|
properties: {
|
||||||
|
link: 'IntObject',
|
||||||
|
linkingObjects: {type:'linkingObjects', objectType: 'InvalidObject', property: 'link'}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: 'IntObject',
|
||||||
|
properties: {
|
||||||
|
integer: 'int'
|
||||||
|
}
|
||||||
|
}]});
|
||||||
|
}, "Property 'InvalidObject.link' declared as origin of linking objects property 'InvalidObject.linkingObjects' links to type 'IntObject'")
|
||||||
},
|
},
|
||||||
|
|
||||||
testRealmConstructorReadOnly: function() {
|
testRealmConstructorReadOnly: function() {
|
||||||
|
@ -178,7 +215,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
testRealmWrite: function() {
|
testRealmWrite: function() {
|
||||||
var realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject]});
|
var realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]});
|
||||||
|
|
||||||
// exceptions should be propogated
|
// exceptions should be propogated
|
||||||
TestCase.assertThrows(function() {
|
TestCase.assertThrows(function() {
|
||||||
|
@ -271,7 +308,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
testRealmCreateUpsert: function() {
|
testRealmCreateUpsert: function() {
|
||||||
var realm = new Realm({schema: [schemas.IntPrimary, schemas.StringPrimary, schemas.AllTypes, schemas.TestObject]});
|
var realm = new Realm({schema: [schemas.IntPrimary, schemas.StringPrimary, schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]});
|
||||||
realm.write(function() {
|
realm.write(function() {
|
||||||
var values = {
|
var values = {
|
||||||
primaryCol: '0',
|
primaryCol: '0',
|
||||||
|
@ -789,7 +826,7 @@ module.exports = {
|
||||||
|
|
||||||
testSchema: function() {
|
testSchema: function() {
|
||||||
var originalSchema = [schemas.TestObject, schemas.BasicTypes, schemas.NullableBasicTypes, schemas.IndexedTypes, schemas.IntPrimary,
|
var originalSchema = [schemas.TestObject, schemas.BasicTypes, schemas.NullableBasicTypes, schemas.IndexedTypes, schemas.IntPrimary,
|
||||||
schemas.PersonObject, schemas.LinkTypes];
|
schemas.PersonObject, schemas.LinkTypes, schemas.LinkingObjectsObject];
|
||||||
|
|
||||||
var schemaMap = {};
|
var schemaMap = {};
|
||||||
originalSchema.forEach(function(objectSchema) {
|
originalSchema.forEach(function(objectSchema) {
|
||||||
|
@ -827,6 +864,11 @@ module.exports = {
|
||||||
TestCase.assertEqual(prop1.objectType, prop2.objectType);
|
TestCase.assertEqual(prop1.objectType, prop2.objectType);
|
||||||
TestCase.assertEqual(prop1.optional, undefined);
|
TestCase.assertEqual(prop1.optional, undefined);
|
||||||
}
|
}
|
||||||
|
else if (prop1.type == 'linking objects') {
|
||||||
|
TestCase.assertEqual(prop1.objectType, prop2.objectType);
|
||||||
|
TestCase.assertEqual(prop1.property, prop2.property);
|
||||||
|
TestCase.assertEqual(prop1.optional, undefined);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
TestCase.assertEqual(prop1.type, isString(prop2) ? prop2 : prop2.type);
|
TestCase.assertEqual(prop1.type, isString(prop2) ? prop2 : prop2.type);
|
||||||
TestCase.assertEqual(prop1.optional, prop2.optional || undefined);
|
TestCase.assertEqual(prop1.optional, prop2.optional || undefined);
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const Realm = require('realm');
|
||||||
|
|
||||||
exports.TestObject = {
|
exports.TestObject = {
|
||||||
name: 'TestObject',
|
name: 'TestObject',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -29,9 +31,11 @@ function PersonObject() {}
|
||||||
PersonObject.schema = {
|
PersonObject.schema = {
|
||||||
name: 'PersonObject',
|
name: 'PersonObject',
|
||||||
properties: {
|
properties: {
|
||||||
name: 'string',
|
name: 'string',
|
||||||
age: 'double',
|
age: 'double',
|
||||||
married: {type: 'bool', default: false},
|
married: {type: 'bool', default: false},
|
||||||
|
children: {type: 'list', objectType: 'PersonObject'},
|
||||||
|
parents: {type: 'linkingObjects', objectType: 'PersonObject', property: 'children'},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
PersonObject.prototype.description = function() {
|
PersonObject.prototype.description = function() {
|
||||||
|
@ -40,6 +44,8 @@ PersonObject.prototype.description = function() {
|
||||||
PersonObject.prototype.toString = function() {
|
PersonObject.prototype.toString = function() {
|
||||||
return this.name;
|
return this.name;
|
||||||
};
|
};
|
||||||
|
Object.setPrototypeOf(PersonObject, Realm.Object);
|
||||||
|
Object.setPrototypeOf(PersonObject.prototype, Realm.Object.prototype);
|
||||||
exports.PersonObject = PersonObject;
|
exports.PersonObject = PersonObject;
|
||||||
|
|
||||||
exports.PersonList = {
|
exports.PersonList = {
|
||||||
|
@ -117,19 +123,27 @@ exports.AllTypes = {
|
||||||
name: 'AllTypesObject',
|
name: 'AllTypesObject',
|
||||||
primaryKey: 'primaryCol',
|
primaryKey: 'primaryCol',
|
||||||
properties: {
|
properties: {
|
||||||
primaryCol: 'string',
|
primaryCol: 'string',
|
||||||
boolCol: 'bool',
|
boolCol: 'bool',
|
||||||
intCol: 'int',
|
intCol: 'int',
|
||||||
floatCol: 'float',
|
floatCol: 'float',
|
||||||
doubleCol: 'double',
|
doubleCol: 'double',
|
||||||
stringCol: 'string',
|
stringCol: 'string',
|
||||||
dateCol: 'date',
|
dateCol: 'date',
|
||||||
dataCol: 'data',
|
dataCol: 'data',
|
||||||
objectCol: 'TestObject',
|
objectCol: 'TestObject',
|
||||||
arrayCol: {type: 'list', objectType: 'TestObject'},
|
arrayCol: {type: 'list', objectType: 'TestObject'},
|
||||||
|
linkingObjectsCol: {type: 'linkingObjects', objectType: 'LinkToAllTypesObject', property: 'allTypesCol'},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.LinkToAllTypes = {
|
||||||
|
name: 'LinkToAllTypesObject',
|
||||||
|
properties: {
|
||||||
|
allTypesCol: 'AllTypesObject',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exports.DefaultValues = {
|
exports.DefaultValues = {
|
||||||
name: 'DefaultValuesObject',
|
name: 'DefaultValuesObject',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -185,3 +199,12 @@ exports.DateObject = {
|
||||||
nullDate: { type: 'date', optional: true }
|
nullDate: { type: 'date', optional: true }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.LinkingObjectsObject = {
|
||||||
|
name: 'LinkingObjectsObject',
|
||||||
|
properties: {
|
||||||
|
value: 'int',
|
||||||
|
links: {type: 'list', objectType: 'LinkingObjectsObject'},
|
||||||
|
linkingObjects: {type: 'linkingObjects', objectType: 'LinkingObjectsObject', property: 'links'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue