Implement implicit property conversion for date and binary (#1557)

* Implement implicit property conversion for date and binary

Closes #1542
Closes #1551

* fix include

* changelog
This commit is contained in:
Yavor Georgiev 2017-12-07 13:47:20 +01:00 committed by GitHub
parent 70004b9304
commit 23f965060e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 68 additions and 13 deletions

View File

@ -10,7 +10,8 @@ x.y.z Release notes
* When authentication fails due to a misbehaving server, a proper error is thrown. * When authentication fails due to a misbehaving server, a proper error is thrown.
### Internal ### Internal
* None * Strings can now be assigned to Date columns. When that happens the JavaScript Date constructor will be invoked to parse the string.
* Base64 strings can now be assigned to Data columns.
2.0.12 Release notes (2017-12-1) 2.0.12 Release notes (2017-12-1)
============================================================= =============================================================

View File

@ -22,6 +22,10 @@
#include "js_realm_object.hpp" #include "js_realm_object.hpp"
#include "js_schema.hpp" #include "js_schema.hpp"
#if REALM_ENABLE_SYNC
#include <realm/util/base64.hpp>
#endif
namespace realm { namespace realm {
class List; class List;
class Object; class Object;
@ -239,7 +243,23 @@ struct Unbox<JSEngine, BinaryData> {
if (ctx->is_null(value)) { if (ctx->is_null(value)) {
return BinaryData(); return BinaryData();
} }
ctx->m_owned_binary_data = js::Value<JSEngine>::validated_to_binary(ctx->m_ctx, value, "Property"); #if REALM_ENABLE_SYNC
// realm-sync holds the base64-decoding routine
if (js::Value<JSEngine>::is_string(ctx->m_ctx, value)) {
// the incoming value might be a base64 string, so let's try to parse it
std::string str = js::Value<JSEngine>::to_string(ctx->m_ctx, value);
size_t max_size = util::base64_decoded_size(str.size());
std::unique_ptr<char[]> data(new char[max_size]);
if (auto size = util::base64_decode(str, data.get(), max_size)) {
ctx->m_owned_binary_data = OwnedBinaryData(std::move(data), *size);
return ctx->m_owned_binary_data.get();
} else {
throw std::runtime_error("Attempting to populate BinaryData from string that is not valid base64");
}
}
#endif
ctx->m_owned_binary_data = js::Value<JSEngine>::validated_to_binary(ctx->m_ctx, value);
return ctx->m_owned_binary_data.get(); return ctx->m_owned_binary_data.get();
} }
}; };
@ -257,7 +277,14 @@ struct Unbox<JSEngine, Timestamp> {
if (ctx->is_null(value)) { if (ctx->is_null(value)) {
return Timestamp(); return Timestamp();
} }
auto date = js::Value<JSEngine>::validated_to_date(ctx->m_ctx, value, "Property"); typename JSEngine::Value date;
if (js::Value<JSEngine>::is_string(ctx->m_ctx, value)) {
// the incoming value might be a date string, so let the Date constructor have at it
date = js::Value<JSEngine>::to_date(ctx->m_ctx, value);
} else {
date = js::Value<JSEngine>::validated_to_date(ctx->m_ctx, value);
}
double milliseconds = js::Value<JSEngine>::to_number(ctx->m_ctx, date); double milliseconds = js::Value<JSEngine>::to_number(ctx->m_ctx, date);
int64_t seconds = milliseconds / 1000; int64_t seconds = milliseconds / 1000;
int32_t nanoseconds = ((int64_t)milliseconds % 1000) * 1000000; int32_t nanoseconds = ((int64_t)milliseconds % 1000) * 1000000;

View File

@ -389,9 +389,9 @@ inline bool Value<T>::is_valid_for_property_type(ContextType context, const Valu
case PropertyType::String: case PropertyType::String:
return is_string(context, value); return is_string(context, value);
case PropertyType::Data: case PropertyType::Data:
return is_binary(context, value); return is_binary(context, value) || is_string(context, value);
case PropertyType::Date: case PropertyType::Date:
return is_date(context, value); return is_date(context, value) || is_string(context, value);
case PropertyType::Object: case PropertyType::Object:
return true; return true;
case PropertyType::Any: case PropertyType::Any:

View File

@ -203,6 +203,15 @@ inline JSObjectRef jsc::Value::to_constructor(JSContextRef ctx, const JSValueRef
template<> template<>
inline JSObjectRef jsc::Value::to_date(JSContextRef ctx, const JSValueRef &value) { inline JSObjectRef jsc::Value::to_date(JSContextRef ctx, const JSValueRef &value) {
if (JSValueIsString(ctx, value)) {
JSValueRef error;
std::array<JSValueRef, 1> args { value };
if (JSObjectRef result = JSObjectMakeDate(ctx, args.size(), args.data(), &error)) {
return result;
} else {
throw jsc::Exception(ctx, error);
}
}
return to_object(ctx, value); return to_object(ctx, value);
} }

View File

@ -21,9 +21,9 @@
#include "node_string.hpp" #include "node_string.hpp"
#include "node_protected.hpp" #include "node_protected.hpp"
#include "node_context.hpp" #include "node_context.hpp"
#include "node_value.hpp"
#include "node_object.hpp" #include "node_object.hpp"
#include "node_function.hpp" #include "node_function.hpp"
#include "node_value.hpp"
#include "node_exception.hpp" #include "node_exception.hpp"
#include "node_return_value.hpp" #include "node_return_value.hpp"
#include "node_class.hpp" #include "node_class.hpp"

View File

@ -204,11 +204,6 @@ inline v8::Local<v8::Object> node::Value::to_array(v8::Isolate* isolate, const v
return to_object(isolate, value); return to_object(isolate, value);
} }
template<>
inline v8::Local<v8::Object> node::Value::to_date(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return to_object(isolate, value);
}
template<> template<>
inline v8::Local<v8::Function> node::Value::to_function(v8::Isolate* isolate, const v8::Local<v8::Value> &value) { inline v8::Local<v8::Function> node::Value::to_function(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
return value->IsFunction() ? v8::Local<v8::Function>::Cast(value) : v8::Local<v8::Function>(); return value->IsFunction() ? v8::Local<v8::Function>::Cast(value) : v8::Local<v8::Function>();
@ -219,5 +214,15 @@ inline v8::Local<v8::Function> node::Value::to_constructor(v8::Isolate* isolate,
return to_function(isolate, value); return to_function(isolate, value);
} }
template<>
inline v8::Local<v8::Object> node::Value::to_date(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
if (value->IsString()) {
v8::Local<v8::Function> date_constructor = to_constructor(isolate, node::Object::get_property(isolate, isolate->GetCurrentContext()->Global(), "Date"));
std::array<v8::Local<v8::Value>, 1> args { {value} };
return node::Function::construct(isolate, date_constructor, args.size(), args.data());
}
return to_object(isolate, value);
}
} // js } // js
} // realm } // realm

View File

@ -297,6 +297,15 @@ module.exports = {
}); });
TestCase.assertArraysEqual(new Uint8Array(object.dataCol), RANDOM_DATA); TestCase.assertArraysEqual(new Uint8Array(object.dataCol), RANDOM_DATA);
if (Realm.Sync) {
// The base64 decoder comes from realm-sync
// Should be able to also set a data property to base64-encoded string.
realm.write(function() {
object.dataCol = require('buffer/').Buffer.from(RANDOM_DATA).toString('base64');
});
TestCase.assertArraysEqual(new Uint8Array(object.dataCol), RANDOM_DATA);
}
// Should be to set a data property to a DataView. // Should be to set a data property to a DataView.
realm.write(function() { realm.write(function() {
object.dataCol = new DataView(RANDOM_DATA.buffer); object.dataCol = new DataView(RANDOM_DATA.buffer);
@ -348,7 +357,7 @@ module.exports = {
object.dataCol = 1; object.dataCol = 1;
}); });
TestCase.assertThrows(function() { TestCase.assertThrows(function() {
object.dataCol = 'data'; object.dataCol = 'some binary data';
}); });
TestCase.assertThrows(function() { TestCase.assertThrows(function() {
object.dataCol = [1]; object.dataCol = [1];
@ -431,15 +440,18 @@ module.exports = {
// test different dates // test different dates
var realm = new Realm({schema: [schemas.DateObject]}); var realm = new Realm({schema: [schemas.DateObject]});
const stringifiedDate = new Date();
realm.write(function() { realm.write(function() {
realm.create('Date', { currentDate: new Date(10000) }); realm.create('Date', { currentDate: new Date(10000) });
realm.create('Date', { currentDate: new Date(-10000) }); realm.create('Date', { currentDate: new Date(-10000) });
realm.create('Date', { currentDate: new Date(1000000000000) }); realm.create('Date', { currentDate: new Date(1000000000000) });
realm.create('Date', { currentDate: new Date(-1000000000000) }); realm.create('Date', { currentDate: new Date(-1000000000000) });
realm.create('Date', { currentDate: stringifiedDate.toString() });
}); });
TestCase.assertEqual(realm.objects('Date')[0].currentDate.getTime(), 10000); TestCase.assertEqual(realm.objects('Date')[0].currentDate.getTime(), 10000);
TestCase.assertEqual(realm.objects('Date')[1].currentDate.getTime(), -10000); TestCase.assertEqual(realm.objects('Date')[1].currentDate.getTime(), -10000);
TestCase.assertEqual(realm.objects('Date')[2].currentDate.getTime(), 1000000000000); TestCase.assertEqual(realm.objects('Date')[2].currentDate.getTime(), 1000000000000);
TestCase.assertEqual(realm.objects('Date')[3].currentDate.getTime(), -1000000000000); TestCase.assertEqual(realm.objects('Date')[3].currentDate.getTime(), -1000000000000);
TestCase.assertEqual(realm.objects('Date')[4].currentDate.toString(), stringifiedDate.toString());
} }
}; };

View File

@ -3,6 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"dependencies": { "dependencies": {
"buffer": "^5.0.8",
"es6-promise": "^3.2.1" "es6-promise": "^3.2.1"
} }
} }