diff --git a/src/js_realm_object.hpp b/src/js_realm_object.hpp index 4ccf84b6..a42a9718 100644 --- a/src/js_realm_object.hpp +++ b/src/js_realm_object.hpp @@ -56,6 +56,7 @@ struct RealmObjectClass : ClassDefinition { static void linking_objects_count(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &); static void get_object_id(ContextType, ObjectType, Arguments, ReturnValue &); static void is_same_object(ContextType, ObjectType, Arguments, ReturnValue &); + static void set_link(ContextType, ObjectType, Arguments, ReturnValue &); const std::string name = "RealmObject"; @@ -72,6 +73,7 @@ struct RealmObjectClass : ClassDefinition { {"linkingObjectsCount", wrap}, {"_objectId", wrap}, {"_isSameObject", wrap}, + {"_setLink", wrap}, }; }; @@ -140,6 +142,53 @@ bool RealmObjectClass::set_property(ContextType ctx, ObjectType object, const return true; } +template +void RealmObjectClass::set_link(ContextType ctx, ObjectType object, Arguments args, ReturnValue& return_value) { + args.validate_count(2); + + auto realm_object = get_internal>(object); + realm_object->realm()->verify_in_write(); + + NativeAccessor accessor(ctx, realm_object->realm(), realm_object->get_object_schema()); + std::string property_name = Value::validated_to_string(ctx, args[0], "propertyName"); + const Property* prop = realm_object->get_object_schema().property_for_name(property_name); + if (!prop) { + throw std::invalid_argument(util::format("No such property: %1", property_name)); + } + if (prop->type != realm::PropertyType::Object) { + throw TypeErrorException(accessor, realm_object->get_object_schema().name, *prop, args[1]); + } + auto& linked_schema = *realm_object->realm()->schema().find(prop->object_type); + auto linked_pk = linked_schema.primary_key_property(); + if (!linked_pk) { + throw std::invalid_argument("Linked object type must have a primary key."); + } + + auto table = realm_object->row().get_table(); + auto linked_table = table->get_link_target(prop->table_column); + + size_t row_ndx = realm::not_found; + if (linked_pk->type == realm::PropertyType::String) { + row_ndx = linked_table->find_first(linked_pk->table_column, + accessor.template unbox(args[1])); + } + else if (is_nullable(linked_pk->type)) { + row_ndx = linked_table->find_first(linked_pk->table_column, + accessor.template unbox>(args[1])); + } + else { + row_ndx = linked_table->find_first(linked_pk->table_column, + accessor.template unbox(args[1])); + } + + if (row_ndx == realm::not_found) { + realm_object->row().set_null(prop->table_column); + } + else { + realm_object->row().set_link(prop->table_column, row_ndx); + } +} + template std::vector> RealmObjectClass::get_property_names(ContextType ctx, ObjectType object) { auto realm_object = get_internal>(object); diff --git a/tests/js/object-tests.js b/tests/js/object-tests.js index 6b8fa25d..8095f556 100644 --- a/tests/js/object-tests.js +++ b/tests/js/object-tests.js @@ -473,5 +473,87 @@ module.exports = { TestCase.assertTrue(new Date('2017-12-07T20:16:03.837Z').toISOString() === objects[0].dateCol.toISOString()) realm.close() + }, + + testSetLink: function() { + const schema = [ + { + name: 'PrimaryInt', + primaryKey: 'pk', + properties: { + pk: 'int', + value: 'int' + } + }, + { + name: 'PrimaryOptionalInt', + primaryKey: 'pk', + properties: { + pk: 'int?', + value: 'int' + } + }, + { + name: 'PrimaryString', + primaryKey: 'pk', + properties: { + pk: 'string?', + value: 'int' + } + }, + { + name: 'Links', + properties: { + intLink: 'PrimaryInt', + optIntLink: 'PrimaryOptionalInt', + stringLink: 'PrimaryString' + } + } + ]; + + const realm = new Realm({schema: schema}); + realm.write(function() { + realm.create('PrimaryInt', {pk: 1, value: 2}) + realm.create('PrimaryInt', {pk: 2, value: 4}) + realm.create('PrimaryOptionalInt', {pk: 1, value: 2}) + realm.create('PrimaryOptionalInt', {pk: 2, value: 4}) + realm.create('PrimaryOptionalInt', {pk: null, value: 6}) + realm.create('PrimaryString', {pk: 'a', value: 2}) + realm.create('PrimaryString', {pk: 'b', value: 4}) + realm.create('PrimaryString', {pk: null, value: 6}) + + const obj = realm.create('Links', {}); + + obj._setLink('intLink', 3); + TestCase.assertEqual(obj.intLink, null); + obj._setLink('intLink', 1); + TestCase.assertEqual(obj.intLink.value, 2); + obj._setLink('intLink', 2); + TestCase.assertEqual(obj.intLink.value, 4); + obj._setLink('intLink', 3); + TestCase.assertEqual(obj.intLink, null); + + obj._setLink('optIntLink', 3); + TestCase.assertEqual(obj.optIntLink, null); + obj._setLink('optIntLink', 1); + TestCase.assertEqual(obj.optIntLink.value, 2); + obj._setLink('optIntLink', 2); + TestCase.assertEqual(obj.optIntLink.value, 4); + obj._setLink('optIntLink', null); + TestCase.assertEqual(obj.optIntLink.value, 6); + obj._setLink('optIntLink', 3); + TestCase.assertEqual(obj.optIntLink, null); + + obj._setLink('stringLink', 'c'); + TestCase.assertEqual(obj.stringLink, null); + obj._setLink('stringLink', 'a'); + TestCase.assertEqual(obj.stringLink.value, 2); + obj._setLink('stringLink', 'b'); + TestCase.assertEqual(obj.stringLink.value, 4); + obj._setLink('stringLink', null); + TestCase.assertEqual(obj.stringLink.value, 6); + obj._setLink('stringLink', 'c'); + TestCase.assertEqual(obj.stringLink, null); + }); } };