NUC-31: Finish working implementation for FirebaseRestTranslator

This commit is contained in:
Denis-Cosmin Nutiu 2020-02-16 15:45:41 +02:00
parent 24e0630421
commit ff492258a8
4 changed files with 50 additions and 23 deletions

View file

@ -16,7 +16,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Cloud.Firestore" Version="1.1.0" />
<PackageReference Include="Google.Protobuf" Version="3.10.1" /> <PackageReference Include="Google.Protobuf" Version="3.10.1" />
<PackageReference Include="Grpc" Version="2.25.0" /> <PackageReference Include="Grpc" Version="2.25.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0" /> <PackageReference Include="Grpc.Tools" Version="2.25.0" />

View file

@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Google.Cloud.Firestore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NucuCar.Domain.Utilities; using NucuCar.Domain.Utilities;
namespace NucuCar.Domain.Telemetry namespace NucuCar.Domain.Telemetry
@ -22,8 +24,7 @@ namespace NucuCar.Domain.Telemetry
/// </summary> /// </summary>
public class TelemetryPublisherFirestore : TelemetryPublisher public class TelemetryPublisherFirestore : TelemetryPublisher
{ {
private readonly FirestoreDb _database; private readonly HttpClient _httpClient;
private readonly string _firestoreCollection;
private readonly int _timeout; private readonly int _timeout;
public TelemetryPublisherFirestore(TelemetryPublisherBuilderOptions opts) : base(opts) public TelemetryPublisherFirestore(TelemetryPublisherBuilderOptions opts) : base(opts)
@ -36,16 +37,19 @@ namespace NucuCar.Domain.Telemetry
$"Missing ProjectId!"); $"Missing ProjectId!");
} }
if (!options.TryGetValue("CollectionName", out _firestoreCollection)) if (!options.TryGetValue("CollectionName", out var firestoreCollection))
{ {
Logger?.LogCritical( Logger?.LogCritical(
$"Can't start {nameof(TelemetryPublisherFirestore)}! Malformed connection string! " + $"Can't start {nameof(TelemetryPublisherFirestore)}! Malformed connection string! " +
$"Missing CollectionName!"); $"Missing CollectionName!");
} }
_timeout = int.Parse(options.GetValueOrDefault("Timeout", "10000")); _timeout = int.Parse(options.GetValueOrDefault("Timeout", "10000"));
var requestUrl = $"https://firestore.googleapis.com/v1/projects/{firestoreProjectId}/" +
_database = FirestoreDb.Create(firestoreProjectId); $"databases/(default)/documents/{firestoreCollection}/";
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(requestUrl);
Logger?.LogInformation($"Initialized {nameof(TelemetryPublisherFirestore)}"); Logger?.LogInformation($"Initialized {nameof(TelemetryPublisherFirestore)}");
} }
@ -56,13 +60,13 @@ namespace NucuCar.Domain.Telemetry
return; return;
} }
var docRef = _database.Collection(_firestoreCollection).Document();
var data = GetTelemetry();
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
cts.CancelAfter(_timeout); cts.CancelAfter(_timeout);
try try
{ {
await docRef.SetAsync(data, cancellationToken: cts.Token); var data = FirebaseRestTranslator.Translate(null, GetTelemetry());
var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
await _httpClient.PostAsync("", content, cts.Token);
Logger?.LogInformation("Published data to Firestore!"); Logger?.LogInformation("Published data to Firestore!");
} }
catch (Exception e) catch (Exception e)

View file

@ -16,7 +16,11 @@ namespace NucuCar.Domain.Utilities
private static Dictionary<string, object> BuildRoot(string name, Dictionary<string, object> dict) private static Dictionary<string, object> BuildRoot(string name, Dictionary<string, object> dict)
{ {
var root = new Dictionary<string, object>(); var root = new Dictionary<string, object>();
root["name"] = name; if (name != null)
{
root["name"] = name;
}
root["fields"] = new Dictionary<string, object>(); root["fields"] = new Dictionary<string, object>();
// iterate through fields and build leaf // iterate through fields and build leaf
foreach (var entry in dict) foreach (var entry in dict)
@ -24,6 +28,7 @@ namespace NucuCar.Domain.Utilities
var fields = (Dictionary<string, object>) root["fields"]; var fields = (Dictionary<string, object>) root["fields"];
fields[entry.Key] = BuildNode(entry.Value); fields[entry.Key] = BuildNode(entry.Value);
} }
return root; return root;
} }
@ -33,10 +38,6 @@ namespace NucuCar.Domain.Utilities
{ {
case string v: case string v:
{ {
if (DateTime.TryParse(v, out _))
{
return BuildTimestamp(v);
}
return BuildString(v); return BuildString(v);
} }
case int v: case int v:
@ -51,16 +52,31 @@ namespace NucuCar.Domain.Utilities
{ {
return BuildBool(v); return BuildBool(v);
} }
case DateTime v:
{
return BuildTimestamp(v);
}
case List<Dictionary<string, object>> v: case List<Dictionary<string, object>> v:
{ {
return BuildArray(v); return BuildArray(v);
} }
case Dictionary<string, object>[] v:
{
return BuildArray(new List<Dictionary<string, object>>(v));
}
case Dictionary<string, object> v: case Dictionary<string, object> v:
{ {
return BuildMap(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}"); throw new ArgumentException($"Can't build leaf! Unknown type for: {value}");
} }
@ -71,26 +87,32 @@ namespace NucuCar.Domain.Utilities
[type] = value [type] = value
}; };
} }
private static Dictionary<string, object> BuildString(string value) private static Dictionary<string, object> BuildString(string value)
{ {
return BuildSimpleValue("stringValue", value); return BuildSimpleValue("stringValue", value);
} }
private static Dictionary<string, object> BuildInteger(int value) private static Dictionary<string, object> BuildInteger(int value)
{ {
return BuildSimpleValue("integerValue", value); return BuildSimpleValue("integerValue", value);
} }
private static Dictionary<string, object> BuildTimestamp(string value)
private static Dictionary<string, object> BuildTimestamp(DateTime value)
{ {
return BuildSimpleValue("timestampValue", value); return BuildSimpleValue("timestampValue", value);
} }
private static Dictionary<string, object> BuildDouble(double value) private static Dictionary<string, object> BuildDouble(double value)
{ {
return BuildSimpleValue("doubleValue", value); return BuildSimpleValue("doubleValue", value);
} }
private static Dictionary<string, object> BuildBool(bool value) private static Dictionary<string, object> BuildBool(bool value)
{ {
return BuildSimpleValue("booleanValue", value); return BuildSimpleValue("booleanValue", value);
} }
private static Dictionary<string, object> BuildArray(List<Dictionary<string, object>> array) private static Dictionary<string, object> BuildArray(List<Dictionary<string, object>> array)
{ {
var values = new List<Dictionary<string, object>>(); var values = new List<Dictionary<string, object>>();
@ -106,6 +128,7 @@ namespace NucuCar.Domain.Utilities
{ {
values.Add(BuildNode(entry)); values.Add(BuildNode(entry));
} }
return root; return root;
} }
@ -123,6 +146,7 @@ namespace NucuCar.Domain.Utilities
{ {
fields[entry.Key] = BuildNode(entry.Value); fields[entry.Key] = BuildNode(entry.Value);
} }
return root; return root;
} }
} }

View file

@ -66,7 +66,7 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Tests.Utilities
public void Test_FireBaseTranslator_Parse() public void Test_FireBaseTranslator_Parse()
{ {
var expectedJson = var expectedJson =
"{\"name\":\"Test\",\"fields\":{\"source\":{\"stringValue\":\"NucuCar.Sensors\"},\"timestamp\":{\"timestampValue\":\"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\"}}}}]}}}}"; "{\"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 basicTelemetryData = getBasicTelemetryData();
var result = FirebaseRestTranslator.Translate("Test", basicTelemetryData); var result = FirebaseRestTranslator.Translate("Test", basicTelemetryData);
var json = JsonConvert.SerializeObject(result); var json = JsonConvert.SerializeObject(result);