From 72f477c27246782f5ef57299a76ea17fb7d1e20e Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Mon, 3 Oct 2016 11:22:23 -0400 Subject: [PATCH 1/2] feat(Promise): Adding userInfo to reject error object Using System.Exception.Data IDictionary, users can add arbitrary data to a "userInfo" property on the error object. Fixes #730 --- .../ReactNative.Shared/Bridge/Promise.cs | 69 ++++++++++++++ .../Bridge/ReactDelegateFactoryBase.cs | 59 +----------- .../ReactNative.Shared.projitems | 1 + .../ReactNative.Tests/Bridge/PromiseTests.cs | 89 +++++++++++++++++++ .../ReactNative.Tests.csproj | 1 + 5 files changed, 161 insertions(+), 58 deletions(-) create mode 100644 ReactWindows/ReactNative.Shared/Bridge/Promise.cs create mode 100644 ReactWindows/ReactNative.Tests/Bridge/PromiseTests.cs diff --git a/ReactWindows/ReactNative.Shared/Bridge/Promise.cs b/ReactWindows/ReactNative.Shared/Bridge/Promise.cs new file mode 100644 index 00000000000..b10eecf98cb --- /dev/null +++ b/ReactWindows/ReactNative.Shared/Bridge/Promise.cs @@ -0,0 +1,69 @@ +using Newtonsoft.Json.Linq; +using System; + +namespace ReactNative.Bridge +{ + class Promise : IPromise + { + private const string DefaultError = "EUNSPECIFIED"; + + private readonly ICallback _resolve; + private readonly ICallback _reject; + + public Promise(ICallback resolve, ICallback reject) + { + _resolve = resolve; + _reject = reject; + } + + public void Resolve(object value) + { + if (_resolve != null) + { + _resolve.Invoke(value); + } + } + + public void Reject(string code, string message) + { + Reject(code, message, default(Exception)); + } + + public void Reject(string message) + { + Reject(DefaultError, message, default(Exception)); + } + + public void Reject(string code, Exception e) + { + Reject(code, e.Message, e); + } + + public void Reject(Exception e) + { + if (e == null) + throw new ArgumentNullException(nameof(e)); + + Reject(DefaultError, e.Message, e); + } + + public void Reject(string code, string message, Exception e) + { + if (_reject != null) + { + var errorData = e?.Data; + var userInfo = errorData != null + ? JToken.FromObject(errorData) + : null; + _reject.Invoke(new JObject + { + { "code", code ?? DefaultError }, + { "message", message }, + { "stack", e?.StackTrace }, + { "userInfo", userInfo }, + }); + } + } + } + +} diff --git a/ReactWindows/ReactNative.Shared/Bridge/ReactDelegateFactoryBase.cs b/ReactWindows/ReactNative.Shared/Bridge/ReactDelegateFactoryBase.cs index 99fa33ee5c6..4db8ca63c21 100644 --- a/ReactWindows/ReactNative.Shared/Bridge/ReactDelegateFactoryBase.cs +++ b/ReactWindows/ReactNative.Shared/Bridge/ReactDelegateFactoryBase.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json.Linq; using ReactNative.Bridge; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -141,63 +142,5 @@ public void Invoke(params object[] arguments) _instance.InvokeCallback(_id, JArray.FromObject(arguments ?? s_empty)); } } - - class Promise : IPromise - { - private const string DefaultError = "EUNSPECIFIED"; - - private readonly ICallback _resolve; - private readonly ICallback _reject; - - public Promise(ICallback resolve, ICallback reject) - { - _resolve = resolve; - _reject = reject; - } - - public void Resolve(object value) - { - if (_resolve != null) - { - _resolve.Invoke(value); - } - } - - public void Reject(string code, string message) - { - Reject(code, message, default(Exception)); - } - - public void Reject(string message) - { - Reject(DefaultError, message, default(Exception)); - } - - public void Reject(string code, Exception e) - { - Reject(code, e.Message, e); - } - - public void Reject(Exception e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - - Reject(DefaultError, e.Message, e); - } - - public void Reject(string code, string message, Exception e) - { - if (_reject != null) - { - _reject.Invoke(new JObject - { - { "code", code ?? DefaultError }, - { "message", message }, - { "stack", e?.StackTrace }, - }); - } - } - } } } diff --git a/ReactWindows/ReactNative.Shared/ReactNative.Shared.projitems b/ReactWindows/ReactNative.Shared/ReactNative.Shared.projitems index 1875c871059..cad6e5edb8a 100644 --- a/ReactWindows/ReactNative.Shared/ReactNative.Shared.projitems +++ b/ReactWindows/ReactNative.Shared/ReactNative.Shared.projitems @@ -30,6 +30,7 @@ + diff --git a/ReactWindows/ReactNative.Tests/Bridge/PromiseTests.cs b/ReactWindows/ReactNative.Tests/Bridge/PromiseTests.cs new file mode 100644 index 00000000000..fecc493869b --- /dev/null +++ b/ReactWindows/ReactNative.Tests/Bridge/PromiseTests.cs @@ -0,0 +1,89 @@ +using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; +using Newtonsoft.Json.Linq; +using ReactNative.Bridge; +using System; +using System.Threading; + +namespace ReactNative.Tests.Bridge +{ + [TestClass] + public class PromiseTests + { + [TestMethod] + public void Promise_Resolve() + { + var args = default(object[]); + var are = new AutoResetEvent(false); + var resolve = new MockCallback(a => + { + args = a; + are.Set(); + }); + var reject = new MockCallback(_ => { }); + var promise = new Promise(resolve, reject); + + var o = new object(); + promise.Resolve(o); + are.WaitOne(); + + Assert.IsNotNull(args); + Assert.AreEqual(1, args.Length); + Assert.AreSame(o, args[0]); + } + + [TestMethod] + public void Promise_Reject() + { + var args = default(object[]); + var are = new AutoResetEvent(false); + var resolve = new MockCallback(_ => { }); + var reject = new MockCallback(a => + { + args = a; + are.Set(); + }); + var promise = new Promise(resolve, reject); + + var code = "42"; + var message = "foo"; + promise.Reject(code, message); + are.WaitOne(); + Assert.IsNotNull(args); + Assert.AreEqual(1, args.Length); + + var json = args[0] as JObject; + Assert.IsNotNull(json); + Assert.AreEqual(code, json["code"]); + Assert.AreEqual(message, json["message"]); + } + + [TestMethod] + public void Promise_Reject_UserInfo() + { + var args = default(object[]); + var are = new AutoResetEvent(false); + var resolve = new MockCallback(_ => { }); + var reject = new MockCallback(a => + { + args = a; + are.Set(); + }); + var promise = new Promise(resolve, reject); + + var code = "42"; + var message = "foo"; + var e = new Exception(); + e.Data.Add("qux", "baz"); + promise.Reject(code, message, e); + are.WaitOne(); + + Assert.IsNotNull(args); + Assert.AreEqual(1, args.Length); + var json = args[0] as JObject; + Assert.IsNotNull(json); + var userInfo = json["userInfo"] as JObject; + Assert.IsNotNull(userInfo); + Assert.AreEqual("baz", userInfo["qux"]); + } + } +} diff --git a/ReactWindows/ReactNative.Tests/ReactNative.Tests.csproj b/ReactWindows/ReactNative.Tests/ReactNative.Tests.csproj index 380019b4579..af1105eda22 100644 --- a/ReactWindows/ReactNative.Tests/ReactNative.Tests.csproj +++ b/ReactWindows/ReactNative.Tests/ReactNative.Tests.csproj @@ -95,6 +95,7 @@ + From d6f171d5e9ce7fc7d672815f406da62aa78354ff Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Wed, 5 Oct 2016 09:55:40 -0400 Subject: [PATCH 2/2] Minor cosmetic fix from review. --- ReactWindows/ReactNative.Shared/Bridge/Promise.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ReactWindows/ReactNative.Shared/Bridge/Promise.cs b/ReactWindows/ReactNative.Shared/Bridge/Promise.cs index b10eecf98cb..48b723934d7 100644 --- a/ReactWindows/ReactNative.Shared/Bridge/Promise.cs +++ b/ReactWindows/ReactNative.Shared/Bridge/Promise.cs @@ -56,14 +56,13 @@ public void Reject(string code, string message, Exception e) ? JToken.FromObject(errorData) : null; _reject.Invoke(new JObject - { - { "code", code ?? DefaultError }, - { "message", message }, - { "stack", e?.StackTrace }, - { "userInfo", userInfo }, - }); + { + { "code", code ?? DefaultError }, + { "message", message }, + { "stack", e?.StackTrace }, + { "userInfo", userInfo }, + }); } } } - }