From fe5841a2f64ae8015e968b38d6070b597ea5ed3a Mon Sep 17 00:00:00 2001 From: Denis-Cosmin Nutiu Date: Sat, 7 Mar 2020 16:13:48 +0200 Subject: [PATCH] Initial commit --- .gitignore | 4 + .../FirebaseRestTranslator.UnitTests.csproj | 20 ++ .../FirebaseRestTranslatorTests.cs | 235 ++++++++++++++++++ FirebaseRestTranslator.sln | 22 ++ .../FirebaseRestTranslator.csproj | 10 + FirebaseRestTranslator/GeoPointValue.cs | 25 ++ FirebaseRestTranslator/ReferenceValue.cs | 17 ++ FirebaseRestTranslator/Translator.cs | 193 ++++++++++++++ readme.md | 22 ++ 9 files changed, 548 insertions(+) create mode 100644 .gitignore create mode 100644 FirebaseRestTranslator.UnitTests/FirebaseRestTranslator.UnitTests.csproj create mode 100644 FirebaseRestTranslator.UnitTests/FirebaseRestTranslatorTests.cs create mode 100644 FirebaseRestTranslator.sln create mode 100644 FirebaseRestTranslator/FirebaseRestTranslator.csproj create mode 100644 FirebaseRestTranslator/GeoPointValue.cs create mode 100644 FirebaseRestTranslator/ReferenceValue.cs create mode 100644 FirebaseRestTranslator/Translator.cs create mode 100644 readme.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14a4d8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +obj/ +/packages/ +.idea diff --git a/FirebaseRestTranslator.UnitTests/FirebaseRestTranslator.UnitTests.csproj b/FirebaseRestTranslator.UnitTests/FirebaseRestTranslator.UnitTests.csproj new file mode 100644 index 0000000..646d899 --- /dev/null +++ b/FirebaseRestTranslator.UnitTests/FirebaseRestTranslator.UnitTests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.0 + + false + + + + + + + + + + + + + + diff --git a/FirebaseRestTranslator.UnitTests/FirebaseRestTranslatorTests.cs b/FirebaseRestTranslator.UnitTests/FirebaseRestTranslatorTests.cs new file mode 100644 index 0000000..2b85b17 --- /dev/null +++ b/FirebaseRestTranslator.UnitTests/FirebaseRestTranslatorTests.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Xunit; +using Xunit.Abstractions; + +namespace FirebaseRestTranslator.UnitTests +{ + enum Season + { + Spring, + Summer, + Autumn, + Winter + } + + + public class FirebaseRestTranslatorTests + { + public FirebaseRestTranslatorTests(ITestOutputHelper testOutputHelper) + { + } + + private Dictionary getBasicTelemetryData() + { + var basicTelemetryDict = new Dictionary + { + ["source"] = "NucuCar.Sensors", + ["timestamp"] = "2019-12-01T23:26:13.5537227+02:00" + }; + var data = new List> + { + new Dictionary() + { + ["sensor_state"] = 2, + ["cpu_temperature"] = 48.849998474121094, + ["_id"] = "CpuTemperature", + }, + new Dictionary() + { + ["sensor_state"] = 2, + ["temperature"] = 32.65, + ["humidity"] = 100.0, + ["pressure"] = 62228.49, + ["voc"] = 0.0, + ["_id"] = "Bme680-Sensor" + } + }; + basicTelemetryDict["data"] = data; + return basicTelemetryDict; + } + + [Fact] + public void Test_FirebaseTranslator_Parse() + { + var expectedJson = + "{\"name\":\"Test\",\"fields\":{\"source\":{\"stringValue\":\"NucuCar.Sensors\"},\"timestamp\":{\"stringValue\":\"2019-12-01T23:26:13.5537227+02:00\"},\"data\":{\"arrayValue\":{\"values\":[{\"mapValue\":{\"fields\":{\"sensor_state\":{\"integerValue\":2},\"cpu_temperature\":{\"doubleValue\":48.849998474121094},\"_id\":{\"stringValue\":\"CpuTemperature\"}}}},{\"mapValue\":{\"fields\":{\"sensor_state\":{\"integerValue\":2},\"temperature\":{\"doubleValue\":32.65},\"humidity\":{\"doubleValue\":100.0},\"pressure\":{\"doubleValue\":62228.49},\"voc\":{\"doubleValue\":0.0},\"_id\":{\"stringValue\":\"Bme680-Sensor\"}}}}]}}}}"; + var basicTelemetryData = getBasicTelemetryData(); + var result = Translator.Translate("Test", basicTelemetryData); + var json = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, json); + } + + [Fact] + public void Test_FirebaseTranslator_StringValue() + { + var data = new Dictionary() + { + ["myKey"] = "myValue" + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator_StringValue\",\"fields\":{\"myKey\":{\"stringValue\":\"myValue\"}}}"; + var result = Translator.Translate("Test_FirebaseTranslator_StringValue", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_IntegerValue() + { + var data = new Dictionary() + { + ["myKey"] = 23 + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"integerValue\":23}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_DoubleValue() + { + var data = new Dictionary() + { + ["myKey"] = 11.20 + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"doubleValue\":11.2}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_BoolValue() + { + var data = new Dictionary() + { + ["myKey"] = false + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"booleanValue\":false}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_TimestampValue() + { + var data = new Dictionary() + { + ["myKey"] = new DateTime(2020, 2, 29, 0, 0, 0, 0) + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"timestampValue\":\"2020-02-29T00:00:00\"}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_EnumValue() + { + var data = new Dictionary() + { + ["myKey"] = Season.Winter + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"integerValue\":3}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_ArrayValue() + { + var data = new Dictionary() + { + ["myKey"] = new List> + { + new Dictionary + { + ["arrayIndex0"] = 11.20 + }, + new Dictionary + { + ["arrayIndex1"] = "test" + } + } + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"arrayValue\":{\"values\":[{\"mapValue\":{\"fields\":{\"arrayIndex0\":{\"doubleValue\":11.2}}}},{\"mapValue\":{\"fields\":{\"arrayIndex1\":{\"stringValue\":\"test\"}}}}]}}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_MapValue() + { + var data = new Dictionary() + { + ["myKey"] = new Dictionary + { + ["stringKey"] = "test", + ["boolKey"] = true, + ["intKey"] = 99 + } + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"mapValue\":{\"fields\":{\"stringKey\":{\"stringValue\":\"test\"},\"boolKey\":{\"booleanValue\":true},\"intKey\":{\"integerValue\":99}}}}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_NullValue() + { + var data = new Dictionary() + { + ["myKey"] = null + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"nullValue\":null}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_BytesValue() + { + var data = new Dictionary() + { + ["myKey"] = new byte[] {97, 98, 99, 100, 101, 102, 103, 104, 105 } + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"bytesValue\":\"YWJjZGVmZ2hp\"}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_ReferenceValue() + { + var data = new Dictionary() + { + ["myKey"] = new ReferenceValue("test") + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"referenceValue\":\"test\"}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + + [Fact] + public void Test_FirebaseTranslator_GeoPointvalue() + { + var data = new Dictionary() + { + ["myKey"] = new GeoPointValue(10, 22) + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"geoPointValue\":{\"latitude\":10.0,\"longitude\":22.0}}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } + } +} \ No newline at end of file diff --git a/FirebaseRestTranslator.sln b/FirebaseRestTranslator.sln new file mode 100644 index 0000000..ca0932e --- /dev/null +++ b/FirebaseRestTranslator.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FirebaseRestTranslator", "FirebaseRestTranslator\FirebaseRestTranslator.csproj", "{B1444209-4E08-441E-BE6B-0B2D41145D2C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FirebaseRestTranslator.UnitTests", "FirebaseRestTranslator.UnitTests\FirebaseRestTranslator.UnitTests.csproj", "{2862F03A-8A57-45F4-80D5-797D8E326850}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B1444209-4E08-441E-BE6B-0B2D41145D2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1444209-4E08-441E-BE6B-0B2D41145D2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1444209-4E08-441E-BE6B-0B2D41145D2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1444209-4E08-441E-BE6B-0B2D41145D2C}.Release|Any CPU.Build.0 = Release|Any CPU + {2862F03A-8A57-45F4-80D5-797D8E326850}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2862F03A-8A57-45F4-80D5-797D8E326850}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2862F03A-8A57-45F4-80D5-797D8E326850}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2862F03A-8A57-45F4-80D5-797D8E326850}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/FirebaseRestTranslator/FirebaseRestTranslator.csproj b/FirebaseRestTranslator/FirebaseRestTranslator.csproj new file mode 100644 index 0000000..6dc011e --- /dev/null +++ b/FirebaseRestTranslator/FirebaseRestTranslator.csproj @@ -0,0 +1,10 @@ + + + netcoreapp3.0 + FirebaseRestTranslator + 0.1.0 + Denis Nutiu + NucuLabs + true + + diff --git a/FirebaseRestTranslator/GeoPointValue.cs b/FirebaseRestTranslator/GeoPointValue.cs new file mode 100644 index 0000000..31f001b --- /dev/null +++ b/FirebaseRestTranslator/GeoPointValue.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace FirebaseRestTranslator +{ + public class GeoPointValue + { + private readonly double _latitude; + private readonly double _longitude; + + public GeoPointValue(double latitude, double longitude) + { + _latitude = latitude; + _longitude = longitude; + } + + public Dictionary ToValue() + { + return new Dictionary + { + ["latitude"] = _latitude, + ["longitude"] = _longitude + }; + } + } +} \ No newline at end of file diff --git a/FirebaseRestTranslator/ReferenceValue.cs b/FirebaseRestTranslator/ReferenceValue.cs new file mode 100644 index 0000000..591361c --- /dev/null +++ b/FirebaseRestTranslator/ReferenceValue.cs @@ -0,0 +1,17 @@ +namespace FirebaseRestTranslator +{ + public class ReferenceValue + { + private readonly string _value; + + public ReferenceValue(string value) + { + _value = value; + } + + public override string ToString() + { + return _value; + } + } +} \ No newline at end of file diff --git a/FirebaseRestTranslator/Translator.cs b/FirebaseRestTranslator/Translator.cs new file mode 100644 index 0000000..3a27775 --- /dev/null +++ b/FirebaseRestTranslator/Translator.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; + +namespace FirebaseRestTranslator +{ + /// + /// This class is used to translate C# dictionaries into Firebase json format. + /// + public static class Translator + { + + public static Dictionary Translate(string name, Dictionary dict) + { + return BuildRoot(name, dict); + } + + private static Dictionary BuildRoot(string name, Dictionary dict) + { + var root = new Dictionary(); + if (name != null) + { + root["name"] = name; + } + + root["fields"] = new Dictionary(); + // iterate through fields and build leaf + foreach (var entry in dict) + { + var fields = (Dictionary) root["fields"]; + fields[entry.Key] = BuildNode(entry.Value); + } + + return root; + } + + private static Dictionary BuildNode(object value) + { + switch (value) + { + case string v: + { + return BuildString(v); + } + case int v: + { + return BuildInteger(v); + } + case double v: + { + return BuildDouble(v); + } + case bool v: + { + return BuildBool(v); + } + case null: + { + return BuildNull(null); + } + case byte[] v: + { + return BuildBytes(v); + } + case DateTime v: + { + return BuildTimestamp(v); + } + case ReferenceValue v: + { + return BuildReference(v); + } + case GeoPointValue v: + { + return BuildGeoPoint(v); + } + case List> v: + { + return BuildArray(v); + } + case Dictionary[] v: + { + return BuildArray(new List>(v)); + } + case Dictionary v: + { + return BuildMap(v); + } + default: + { + if (value.GetType().IsEnum) + { + return BuildInteger((int) value); + } + + break; + } + } + + throw new ArgumentException($"Can't build leaf! Unknown type for: {value}"); + } + + private static Dictionary BuildSimpleValue(string type, object value) + { + return new Dictionary() + { + [type] = value + }; + } + + private static Dictionary BuildString(string value) + { + return BuildSimpleValue("stringValue", value); + } + + private static Dictionary BuildInteger(int value) + { + return BuildSimpleValue("integerValue", value); + } + + private static Dictionary BuildTimestamp(DateTime value) + { + return BuildSimpleValue("timestampValue", value); + } + + private static Dictionary BuildDouble(double value) + { + return BuildSimpleValue("doubleValue", value); + } + + private static Dictionary BuildBool(bool value) + { + return BuildSimpleValue("booleanValue", value); + } + + private static Dictionary BuildNull(object value) + { + return BuildSimpleValue("nullValue", value); + } + + private static Dictionary BuildBytes(byte[] value) + { + return BuildSimpleValue("bytesValue", Convert.ToBase64String(value)); + } + + private static Dictionary BuildReference(ReferenceValue value) + { + return BuildSimpleValue("referenceValue", value.ToString()); + } + + private static Dictionary BuildGeoPoint(GeoPointValue value) + { + return BuildSimpleValue("geoPointValue", value.ToValue()); + } + + + private static Dictionary BuildArray(List> array) + { + var values = new List>(); + var root = new Dictionary + { + ["arrayValue"] = new Dictionary + { + ["values"] = values + } + }; + + foreach (var entry in array) + { + values.Add(BuildNode(entry)); + } + + return root; + } + + private static Dictionary BuildMap(Dictionary map) + { + var fields = new Dictionary(); + var root = new Dictionary + { + ["mapValue"] = new Dictionary + { + ["fields"] = fields + } + }; + foreach (var entry in map) + { + fields[entry.Key] = BuildNode(entry.Value); + } + + return root; + } + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..a13b8d9 --- /dev/null +++ b/readme.md @@ -0,0 +1,22 @@ +# FirebareRestTranslator + +This is a small helper class that can be used to translate C# +dictionaries into the Firebase REST format so they be posted +to Firebase directly. + +See the tests for usage. + +```c# + [Fact] + public void Test_FirebaseTranslator_BoolValue() + { + var data = new Dictionary() + { + ["myKey"] = false + }; + var expectedJson = "{\"name\":\"Test_FirebaseTranslator\",\"fields\":{\"myKey\":{\"booleanValue\":false}}}"; + var result = Translator.Translate("Test_FirebaseTranslator", data); + var actualJson = JsonConvert.SerializeObject(result); + Assert.Equal(expectedJson, actualJson); + } +```