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:
parent
70004b9304
commit
23f965060e
|
@ -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)
|
||||||
=============================================================
|
=============================================================
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue