diff --git a/.gitignore b/.gitignore index bf793ed..fbd150d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ Thumbs.db # dotCover *.dotCover + +# Tests +*.test.json diff --git a/src/Masterloop.Plugin.Application/ExtendedHttpClient.cs b/src/Masterloop.Plugin.Application/ExtendedHttpClient.cs index 250022d..fd0fa8b 100644 --- a/src/Masterloop.Plugin.Application/ExtendedHttpClient.cs +++ b/src/Masterloop.Plugin.Application/ExtendedHttpClient.cs @@ -3,11 +3,12 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Text; +using System.Text.Json; using System.Threading.Tasks; namespace Masterloop.Plugin.Application { - public class ExtendedHttpClient + internal class ExtendedHttpClient { private const int DefaultTimeoutInSeconds = 30; private const string OriginAddressHeader = "OriginAddress"; @@ -16,6 +17,9 @@ public class ExtendedHttpClient private readonly HttpClient _httpClient; + private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions + { PropertyNameCaseInsensitive = true }; + public ExtendedHttpClient(string username, string password, bool useCompression, string originAddress, ApplicationMetadata applicationMetadata, HttpClient httpClient) { Username = username; @@ -76,6 +80,30 @@ public void SetMetaData(ApplicationMetadata applicationMetadata) _httpClient.DefaultRequestHeaders.Add(OriginReferenceHeader, applicationMetadata.Reference); } + public async Task> DownloadAsync(string url, string accept) + { + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); + + using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) + { + if (response == null) + return null; + + StatusCode = response.StatusCode; + StatusDescription = response.ReasonPhrase; + + var stream = await response.Content.ReadAsStreamAsync(); + + if (response.IsSuccessStatusCode) + { + return new HttpTypeResponse(response.StatusCode, response.ReasonPhrase, await JsonSerializer.DeserializeAsync(stream, JsonSerializerOptions)); + } + + return new HttpTypeResponse(response.StatusCode, response.ReasonPhrase); + } + } + public async Task DownloadStringAsync(string url, string accept) { var request = new HttpRequestMessage(HttpMethod.Get, url); @@ -108,8 +136,34 @@ public async Task DownloadBytesAsync(string url, string accept response.IsSuccessStatusCode ? await response.Content?.ReadAsByteArrayAsync() : null); } - public async Task UploadStringAsync(string url, string body, string accept, - string contentType) + public async Task> UploadAsync(string url, string body, string accept, string contentType) + { + var request = new HttpRequestMessage(HttpMethod.Post, url) + { + Content = new StringContent(body, Encoding.UTF8, contentType) + }; + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); + + using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) + { + if (response == null) + return null; + + StatusCode = response.StatusCode; + StatusDescription = response.ReasonPhrase; + + var stream = await response.Content.ReadAsStreamAsync(); + + if (response.IsSuccessStatusCode) + { + return new HttpTypeResponse(response.StatusCode, response.ReasonPhrase, await JsonSerializer.DeserializeAsync(stream, JsonSerializerOptions)); + } + + return new HttpTypeResponse(response.StatusCode, response.ReasonPhrase); + } + } + + public async Task UploadStringAsync(string url, string body, string accept, string contentType) { var request = new HttpRequestMessage(HttpMethod.Post, url) { diff --git a/src/Masterloop.Plugin.Application/HttpByteResponse.cs b/src/Masterloop.Plugin.Application/HttpByteResponse.cs index ea83599..226dc85 100644 --- a/src/Masterloop.Plugin.Application/HttpByteResponse.cs +++ b/src/Masterloop.Plugin.Application/HttpByteResponse.cs @@ -2,7 +2,7 @@ namespace Masterloop.Plugin.Application { - public class HttpByteResponse + internal class HttpByteResponse { public HttpByteResponse(HttpStatusCode statusCode, string statusDescription, byte[] content) { diff --git a/src/Masterloop.Plugin.Application/HttpStringResponse.cs b/src/Masterloop.Plugin.Application/HttpStringResponse.cs index 696c26b..799ebf0 100644 --- a/src/Masterloop.Plugin.Application/HttpStringResponse.cs +++ b/src/Masterloop.Plugin.Application/HttpStringResponse.cs @@ -2,7 +2,7 @@ namespace Masterloop.Plugin.Application { - public class HttpStringResponse + internal class HttpStringResponse { public HttpStringResponse(HttpStatusCode statusCode, string statusDescription, string content) { diff --git a/src/Masterloop.Plugin.Application/HttpTypeResponse.cs b/src/Masterloop.Plugin.Application/HttpTypeResponse.cs new file mode 100644 index 0000000..898691f --- /dev/null +++ b/src/Masterloop.Plugin.Application/HttpTypeResponse.cs @@ -0,0 +1,22 @@ +using System.Net; + +namespace Masterloop.Plugin.Application +{ + internal class HttpTypeResponse + { + public HttpTypeResponse(HttpStatusCode statusCode, string statusDescription) + { + StatusCode = statusCode; + StatusDescription = statusDescription; + } + + public HttpTypeResponse(HttpStatusCode statusCode, string statusDescription, T content): this(statusCode, statusDescription) + { + Content = content; + } + + public HttpStatusCode StatusCode { get; } + public string StatusDescription { get; } + public T Content { get; } + } +} \ No newline at end of file diff --git a/src/Masterloop.Plugin.Application/Masterloop.Plugin.Application.csproj b/src/Masterloop.Plugin.Application/Masterloop.Plugin.Application.csproj index e41ceed..92b3c92 100644 --- a/src/Masterloop.Plugin.Application/Masterloop.Plugin.Application.csproj +++ b/src/Masterloop.Plugin.Application/Masterloop.Plugin.Application.csproj @@ -32,5 +32,6 @@ + \ No newline at end of file diff --git a/src/Masterloop.Plugin.Application/MasterloopServerConnection.cs b/src/Masterloop.Plugin.Application/MasterloopServerConnection.cs index 55c27e3..5e5e072 100644 --- a/src/Masterloop.Plugin.Application/MasterloopServerConnection.cs +++ b/src/Masterloop.Plugin.Application/MasterloopServerConnection.cs @@ -614,6 +614,7 @@ public async Task GetCurrentDeviceTemplateVariantFirm #endregion #region Observations + /// /// Get the latest observation value for specified device and observation identifier. /// @@ -624,12 +625,7 @@ public async Task GetCurrentDeviceTemplateVariantFirm public Observation GetCurrentObservation(string MID, int observationId, DataType dataType) { string url = string.Format(_addressDeviceObservationCurrent, MID, observationId); - Tuple result = GetString(url); - if (result.Item1) - { - return DeserializeObservation(result.Item2, dataType); - } - return null; + return GetObservation(url, dataType); } /// @@ -642,12 +638,7 @@ public Observation GetCurrentObservation(string MID, int observationId, DataType public async Task GetCurrentObservationAsync(string MID, int observationId, DataType dataType) { string url = string.Format(_addressDeviceObservationCurrent, MID, observationId); - Tuple result = await GetStringAsync(url); - if (result.Item1) - { - return DeserializeObservation(result.Item2, dataType); - } - return null; + return await GetObservationAsync(url, dataType); } /// @@ -658,12 +649,7 @@ public async Task GetCurrentObservationAsync(string MID, int observ public IdentifiedObservation[] GetCurrentObservations(string MID) { string url = string.Format(_addressDeviceObservationsCurrent, MID); - Tuple result = GetString(url); - if (result.Item1) - { - return DeserializeIdentifiedObservations(result.Item2); - } - return null; + return GetIdentifiedObservations(url); } /// @@ -674,12 +660,7 @@ public IdentifiedObservation[] GetCurrentObservations(string MID) public async Task GetCurrentObservationsAsync(string MID) { string url = string.Format(_addressDeviceObservationsCurrent, MID); - Tuple result = await GetStringAsync(url); - if (result.Item1) - { - return DeserializeIdentifiedObservations(result.Item2); - } - return null; + return await GetIdentifiedObservationsAsync(url); } /// @@ -694,12 +675,7 @@ public async Task GetCurrentObservationsAsync(string MI public Observation[] GetObservations(string MID, int observationId, DataType dataType, DateTime from, DateTime to) { string url = string.Format(_addressDeviceObservations, MID, observationId, from.ToString("o"), to.ToString("o")); - Tuple result = GetString(url); - if (result.Item1) - { - return DeserializeObservations(result.Item2, dataType); - } - return null; + return GetObservations(url, dataType); } /// @@ -714,12 +690,7 @@ public Observation[] GetObservations(string MID, int observationId, DataType dat public async Task GetObservationsAsync(string MID, int observationId, DataType dataType, DateTime from, DateTime to) { string url = string.Format(_addressDeviceObservations, MID, observationId, from.ToString("o"), to.ToString("o")); - Tuple result = await GetStringAsync(url); - if (result.Item1) - { - return DeserializeObservations(result.Item2, dataType); - } - return null; + return await GetObservationsAsync(url, dataType); } /// @@ -769,6 +740,7 @@ public async Task DeleteObservationsAsync(string MID, int observationId, Da } return 0; } + #endregion #region Commands @@ -1150,7 +1122,6 @@ public async Task CreatePersistentSubscriptionWhitelistAsync(string subscr return result.Item1; } - #endregion #region Pulse @@ -1245,6 +1216,7 @@ public async Task CanPingAsync() #endregion #region InternalMethods + private Observation DeserializeObservation(string json, DataType dataType) { if (json != string.Empty) @@ -1275,20 +1247,26 @@ private IdentifiedObservation[] DeserializeIdentifiedObservations(string json) if (json != string.Empty && json.Length > 0) { ExpandedObservationValue[] values = JsonConvert.DeserializeObject(json); - if (values != null && values.Length > 0) + return ConvertToIdentifiedObservations(values); + } + return null; + } + + private IdentifiedObservation[] ConvertToIdentifiedObservations(ExpandedObservationValue[] expandedObservationValues) + { + if (expandedObservationValues != null && expandedObservationValues.Length > 0) + { + var ios = new List(); + foreach (ExpandedObservationValue v in expandedObservationValues) { - List ios = new List(); - foreach (ExpandedObservationValue v in values) + var io = new IdentifiedObservation() { - IdentifiedObservation io = new IdentifiedObservation() - { - ObservationId = v.Id, - Observation = ObservationStringConverter.StringToObservation(v.Timestamp, v.Value, v.DataType) - }; - ios.Add(io); - } - return ios.ToArray(); + ObservationId = v.Id, + Observation = ObservationStringConverter.StringToObservation(v.Timestamp, v.Value, v.DataType) + }; + ios.Add(io); } + return ios.ToArray(); } return null; } @@ -1318,30 +1296,6 @@ private Observation[] DeserializeObservations(string json, DataType dataType) return null; } - private Tuple GetString(string addressExtension, string accept = DefaultAcceptHeader) - { - if (UseHttpClientInsteadOfWebRequests) - { - return GetStringUsingHttpClientAsync(addressExtension, accept).Result; - } - else - { - return GetStringUsingWebRequest(addressExtension, accept); - } - } - - private async Task> GetStringAsync(string addressExtension, string accept = DefaultAcceptHeader) - { - if (UseHttpClientInsteadOfWebRequests) - { - return await GetStringUsingHttpClientAsync(addressExtension, accept); - } - else - { - return await GetStringUsingWebRequestAsync(addressExtension, accept); - } - } - private Tuple GetBytes(string addressExtension, string accept = DefaultAcceptHeader) { if (UseHttpClientInsteadOfWebRequests) @@ -1416,7 +1370,33 @@ private async Task> DeleteAsync(string addressExtension) #region Http methods using WebRequests - private Tuple GetStringUsingWebRequest(string addressExtension, string accept) + private T GetDeserializedUsingWebRequest(string url, string accept = DefaultAcceptHeader) + { + Tuple result = GetStringUsingWebRequest(url, accept); + if (result.Item1) + { + if (result.Item2 != string.Empty) + { + return JsonConvert.DeserializeObject(result.Item2); + } + } + return default(T); + } + + private async Task GetDeserializedUsingWebRequestAsync(string url, string accept = DefaultAcceptHeader) + { + Tuple result = await GetStringUsingWebRequestAsync(url, accept); + if (result.Item1) + { + if (result.Item2 != string.Empty) + { + return JsonConvert.DeserializeObject(result.Item2); + } + } + return default(T); + } + + private Tuple GetStringUsingWebRequest(string addressExtension, string accept = DefaultAcceptHeader) { var result = GetBytes(addressExtension, accept); if (result.Item1 && result.Item2 != null) @@ -1429,7 +1409,7 @@ private Tuple GetStringUsingWebRequest(string addressExtension, st } } - private async Task> GetStringUsingWebRequestAsync(string addressExtension, string accept) + private async Task> GetStringUsingWebRequestAsync(string addressExtension, string accept = DefaultAcceptHeader) { var result = await GetBytesAsync(addressExtension, accept); if (result.Item1 && result.Item2 != null) @@ -1728,35 +1708,73 @@ private async Task> DeleteUsingWebRequestAsync(string addres #region Http methods using HttpClient - private async Task> GetStringUsingHttpClientAsync(string addressExtension, string accept = DefaultAcceptHeader) + private async Task GetDeserializedUsingHttpClientAsync(string addressExtension, string accept = DefaultAcceptHeader) { var url = _baseAddress + addressExtension; - var success = false; - string result = null; try { - var response = await _extendedHttpClient.DownloadStringAsync(url, accept); - result = response.Content; + var response = await _extendedHttpClient.DownloadAsync(url, accept); LastHttpStatusCode = response.StatusCode; if (response.StatusCode == HttpStatusCode.OK) { LastErrorMessage = string.Empty; - success = true; - } - else - { - LastErrorMessage = response.StatusDescription; + return response.Content; } + + LastErrorMessage = response.StatusDescription; } catch (Exception e) { LastHttpStatusCode = _extendedHttpClient.StatusCode; LastErrorMessage = e.Message; } - return new Tuple(success, result); + + return default(TResult); + } + + private async Task GetObservationUsingHttpClientAsync(string addressExtension, DataType observationDataType, string accept = DefaultAcceptHeader) + { + switch (observationDataType) + { + case DataType.Boolean: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Double: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Integer: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Position: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.String: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Statistics: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + default: + throw new NotSupportedException("Unsupported data type: " + observationDataType.ToString()); + } } + private async Task GetObservationsUsingHttpClientAsync(string addressExtension, DataType observationDataType, string accept = DefaultAcceptHeader) + { + switch (observationDataType) + { + case DataType.Boolean: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Double: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Integer: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Position: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.String: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + case DataType.Statistics: + return await GetDeserializedUsingHttpClientAsync(addressExtension, accept); + default: + throw new NotSupportedException("Unsupported data type: " + observationDataType.ToString()); + } + } + private async Task> GetBytesUsingHttpClientAsync(string addressExtension, string accept = DefaultAcceptHeader) { var url = _baseAddress + addressExtension; @@ -1786,6 +1804,31 @@ private async Task> GetBytesUsingHttpClientAsync(string addr return new Tuple(success, result); } + private async Task PostUsingHttpClientAsync(string addressExtension, string body, string accept = DefaultAcceptHeader, string contentType = DefaultContentType) + { + var url = _baseAddress + addressExtension; + try + { + var response = await _extendedHttpClient.UploadAsync(url, body, accept, contentType); + LastHttpStatusCode = response.StatusCode; + + if (response.StatusCode == HttpStatusCode.OK) + { + LastErrorMessage = string.Empty; + return response.Content; + } + + LastErrorMessage = response.StatusDescription; + } + catch (Exception e) + { + LastHttpStatusCode = _extendedHttpClient.StatusCode; + LastErrorMessage = e.Message; + } + + return default(TResult); + } + private async Task> PostUsingHttpClientAsync(string addressExtension, string body, string accept = DefaultAcceptHeader, string contentType = DefaultContentType) { var url = _baseAddress + addressExtension; @@ -1845,57 +1888,173 @@ private async Task> DeleteUsingHttpClientAsync(string addres } #endregion - + private T GetDeserialized(string url) { - Tuple result = GetString(url); - if (result.Item1) + if (UseHttpClientInsteadOfWebRequests) { - if (result.Item2 != string.Empty) + return GetDeserializedUsingHttpClientAsync(url).Result; + } + else + { + return GetDeserializedUsingWebRequest(url); + } + } + + private async Task GetDeserializedAsync(string url) + { + if (UseHttpClientInsteadOfWebRequests) + { + return await GetDeserializedUsingHttpClientAsync(url); + } + else + { + return await GetDeserializedUsingWebRequestAsync(url); + } + } + + private Observation GetObservation(string url, DataType dataType) + { + if (UseHttpClientInsteadOfWebRequests) + { + return GetObservationUsingHttpClientAsync(url, dataType).Result; + } + else + { + Tuple result = GetStringUsingWebRequest(url); + if (result.Item1) { - return JsonConvert.DeserializeObject(result.Item2); + return DeserializeObservation(result.Item2, dataType); } + return null; } - return default(T); } - private async Task GetDeserializedAsync(string url) + private async Task GetObservationAsync(string url, DataType dataType) { - Tuple result = await GetStringAsync(url); - if (result.Item1) + if (UseHttpClientInsteadOfWebRequests) { - if (result.Item2 != string.Empty) + return await GetObservationUsingHttpClientAsync(url, dataType); + } + else + { + Tuple result = await GetStringUsingWebRequestAsync(url); + if (result.Item1) { - return JsonConvert.DeserializeObject(result.Item2); + return DeserializeObservation(result.Item2, dataType); + } + return null; + } + } + + private Observation[] GetObservations(string url, DataType dataType) + { + if (UseHttpClientInsteadOfWebRequests) + { + return GetObservationsUsingHttpClientAsync(url, dataType).Result; + } + else + { + Tuple result = GetStringUsingWebRequest(url); + if (result.Item1) + { + return DeserializeObservations(result.Item2, dataType); } + return null; + } + } + + private async Task GetObservationsAsync(string url, DataType dataType) + { + if (UseHttpClientInsteadOfWebRequests) + { + return await GetObservationsUsingHttpClientAsync(url, dataType); + } + else + { + Tuple result = await GetStringUsingWebRequestAsync(url); + if (result.Item1) + { + return DeserializeObservations(result.Item2, dataType); + } + return null; + } + } + + private IdentifiedObservation[] GetIdentifiedObservations(string url) + { + if (UseHttpClientInsteadOfWebRequests) + { + var expandedObservationValues = GetDeserializedUsingHttpClientAsync(url).Result; + return ConvertToIdentifiedObservations(expandedObservationValues); + } + else + { + Tuple result = GetStringUsingWebRequest(url); + if (result.Item1) + { + return DeserializeIdentifiedObservations(result.Item2); + } + return null; + } + } + + private async Task GetIdentifiedObservationsAsync(string url) + { + if (UseHttpClientInsteadOfWebRequests) + { + var expandedObservationValues = await GetDeserializedUsingHttpClientAsync(url); + return ConvertToIdentifiedObservations(expandedObservationValues); + } + else + { + Tuple result = await GetStringUsingWebRequestAsync(url); + if (result.Item1) + { + return DeserializeIdentifiedObservations(result.Item2); + } + return null; } - return default(T); } private T PostDeserialized(string url, string body, string contentType = DefaultContentType) { - Tuple result = Post(url, body, contentType); - if (result.Item1) + if (UseHttpClientInsteadOfWebRequests) { - if (result.Item2 != string.Empty) + return PostUsingHttpClientAsync(url, body, contentType: contentType).Result; + } + else + { + Tuple result = Post(url, body, contentType); + if (result.Item1) { - return JsonConvert.DeserializeObject(result.Item2); + if (result.Item2 != string.Empty) + { + return JsonConvert.DeserializeObject(result.Item2); + } } + return default(T); } - return default(T); } private async Task PostDeserializedAsync(string url, string body, string contentType = DefaultContentType) { - Tuple result = await PostAsync(url, body, contentType); - if (result.Item1) + if (UseHttpClientInsteadOfWebRequests) { - if (result.Item2 != string.Empty) + return await PostUsingHttpClientAsync(url, body, contentType: contentType); + } + else + { + Tuple result = await PostAsync(url, body, contentType); + if (result.Item1) { - return JsonConvert.DeserializeObject(result.Item2); + if (result.Item2 != string.Empty) + { + return JsonConvert.DeserializeObject(result.Item2); + } } + return default(T); } - return default(T); } private string GetLocalIPAddress() @@ -1921,6 +2080,7 @@ private string GetLocalIPAddress() } return null; } + #endregion } } diff --git a/tests/Masterloop.Plugin.Application.Tests/ApplicationBase.cs b/tests/Masterloop.Plugin.Application.Tests/ApplicationBase.cs index b4430fd..5c66fef 100644 --- a/tests/Masterloop.Plugin.Application.Tests/ApplicationBase.cs +++ b/tests/Masterloop.Plugin.Application.Tests/ApplicationBase.cs @@ -1,81 +1,89 @@ using System; -using Masterloop.Core.Types.LiveConnect; -using Microsoft.Extensions.Configuration; - -namespace Masterloop.Plugin.Application.Tests -{ - public abstract class ApplicationBase - { - protected static IConfiguration _config; - - protected static IConfiguration GetConfig() - { - if (_config == null) - { - _config = new ConfigurationBuilder().AddJsonFile("appsettings.test.json").Build(); - } - return _config; - } - - protected static IMasterloopServerConnection GetMCSAPI() - { - IConfiguration config = GetConfig(); - return new MasterloopServerConnection(config["Hostname"], config["Username"], config["Password"], Boolean.Parse(config["UseHTTPS"])); - } - - protected static MasterloopLiveConnection GetMCSLiveTemporary() - { - var mcs = GetMCSAPI(); - LiveAppRequest lar = new LiveAppRequest() - { - MID = GetMID(), - ConnectAllCommands = true, - ConnectAllObservations = true, - InitObservationValues = false, - ReceiveDevicePulse = true - }; - LiveConnectionDetails lcd = mcs.RequestLiveConnection(new LiveAppRequest[] { lar }); - return new MasterloopLiveConnection(lcd); - } - - protected static MasterloopLiveConnection GetMCSPersistentConnection() - { - var mcs = GetMCSAPI(); - string subscriptionKey = GetPersistentSubscriptionKey(); - mcs.DeleteLivePersistentSubscription(subscriptionKey); - LivePersistentSubscriptionRequest request = new LivePersistentSubscriptionRequest() - { - SubscriptionKey = subscriptionKey, - TID = GetTID(), - ConnectAllCommands = true - }; - if (mcs.CreateLivePersistentSubscription(request)) - { - LiveConnectionDetails lcd = mcs.GetLivePersistentSubscriptionConnection(subscriptionKey); - return new MasterloopLiveConnection(lcd); - } - else - { - throw new Exception("Could not create subscription"); - } - } - - protected static string GetMID() - { - IConfiguration config = GetConfig(); - return config["MID"]; - } - - protected static string GetTID() - { - IConfiguration config = GetConfig(); - return config["TID"]; - } - - protected static string GetPersistentSubscriptionKey() - { - IConfiguration config = GetConfig(); - return config["PersistentSubscriptionKey"]; - } - } +using System.Net.Http; +using Masterloop.Core.Types.LiveConnect; +using Microsoft.Extensions.Configuration; + +namespace Masterloop.Plugin.Application.Tests +{ + public abstract class ApplicationBase + { + protected static IConfiguration _config; + + protected static IConfiguration GetConfig() + { + if (_config == null) + { + _config = new ConfigurationBuilder().AddJsonFile("appsettings.test.json").Build(); + } + return _config; + } + + protected static IMasterloopServerConnection GetMCSAPI(bool useHttpClient = true) + { + IConfiguration config = GetConfig(); + if (useHttpClient) + { + return new MasterloopServerConnection(config["Hostname"], config["Username"], config["Password"], new HttpClient(), Boolean.Parse(config["UseHTTPS"])); + } + else + { + return new MasterloopServerConnection(config["Hostname"], config["Username"], config["Password"], Boolean.Parse(config["UseHTTPS"])); + } + } + + protected static MasterloopLiveConnection GetMCSLiveTemporary() + { + var mcs = GetMCSAPI(); + LiveAppRequest lar = new LiveAppRequest() + { + MID = GetMID(), + ConnectAllCommands = true, + ConnectAllObservations = true, + InitObservationValues = false, + ReceiveDevicePulse = true + }; + LiveConnectionDetails lcd = mcs.RequestLiveConnection(new LiveAppRequest[] { lar }); + return new MasterloopLiveConnection(lcd); + } + + protected static MasterloopLiveConnection GetMCSPersistentConnection() + { + var mcs = GetMCSAPI(); + string subscriptionKey = GetPersistentSubscriptionKey(); + mcs.DeleteLivePersistentSubscription(subscriptionKey); + LivePersistentSubscriptionRequest request = new LivePersistentSubscriptionRequest() + { + SubscriptionKey = subscriptionKey, + TID = GetTID(), + ConnectAllCommands = true + }; + if (mcs.CreateLivePersistentSubscription(request)) + { + LiveConnectionDetails lcd = mcs.GetLivePersistentSubscriptionConnection(subscriptionKey); + return new MasterloopLiveConnection(lcd); + } + else + { + throw new Exception("Could not create subscription"); + } + } + + protected static string GetMID() + { + IConfiguration config = GetConfig(); + return config["MID"]; + } + + protected static string GetTID() + { + IConfiguration config = GetConfig(); + return config["TID"]; + } + + protected static string GetPersistentSubscriptionKey() + { + IConfiguration config = GetConfig(); + return config["PersistentSubscriptionKey"]; + } + } } \ No newline at end of file diff --git a/tests/Masterloop.Plugin.Application.Tests/Rest.cs b/tests/Masterloop.Plugin.Application.Tests/Rest.cs index a6339b8..364815a 100644 --- a/tests/Masterloop.Plugin.Application.Tests/Rest.cs +++ b/tests/Masterloop.Plugin.Application.Tests/Rest.cs @@ -1,17 +1,44 @@ using System; using System.Linq; +using System.Threading.Tasks; +using Masterloop.Core.Types.Base; using Masterloop.Core.Types.Commands; using Masterloop.Core.Types.Devices; +using Masterloop.Core.Types.Firmware; +using Masterloop.Core.Types.Observations; using Xunit; namespace Masterloop.Plugin.Application.Tests { + /// + /// TODO: Will need to work on new testing work-flow where a new template and devices are created on the fly and do testing against them + /// public class Rest : ApplicationBase { [Fact] - public void GetAllDevices() + public void GetAllTemplates() { - Device[] devices = GetMCSAPI().GetDevices(false); + DeviceTemplate[] templates = GetMCSAPI().GetTemplates(); + Assert.NotNull(templates); + Assert.Contains(GetTID(), templates.Select(d => GetTID())); + } + + [Fact] + public void GetTemplate() + { + DeviceTemplate template = GetMCSAPI().GetTemplate(GetTID()); + Assert.NotNull(template); + Assert.NotEmpty(template.Observations); + Assert.NotEmpty(template.Settings); + Assert.NotEmpty(template.Commands); + Assert.NotEmpty(template.Pulses); + Assert.Equal(GetTID(), template.Id); + } + + [Fact] + public void GetTemplateDevices() + { + Device[] devices = GetMCSAPI().GetTemplateDevices(GetTID()); Assert.NotNull(devices); Assert.Contains(GetMID(), devices.Select(d => d.MID)); } @@ -25,13 +52,50 @@ public void GetDevicelets() } [Fact] - public void GetTemplateDevices() + public void GetAllDevices() { - Device[] devices = GetMCSAPI().GetTemplateDevices(GetTID()); + Device[] devices = GetMCSAPI().GetDevices(false); Assert.NotNull(devices); Assert.Contains(GetMID(), devices.Select(d => d.MID)); } + [Fact] + public async Task GetAllDevicesWithDetails() + { + DetailedDevice[] devices = await GetMCSAPI(true).GetDevicesAsync(true, true); + Assert.NotNull(devices); + Assert.Contains(GetMID(), devices.Select(d => d.MID)); + } + + [Fact] + public async Task GetDeviceDetails() + { + Device device = await GetMCSAPI().GetDeviceDetailsAsync(GetMID()); + Assert.NotNull(device); + Assert.Equal(GetMID(), device.MID); + } + + [Fact] + public async Task GetSecureDeviceDetails() + { + SecureDetailedDevice device = await GetMCSAPI().GetSecureDeviceDetailsAsync(GetMID()); + Assert.NotNull(device); + Assert.NotNull(device.PreSharedKey); + Assert.Equal(GetMID(), device.MID); + } + + [Fact] + public async Task GetDeviceTemplate() + { + DeviceTemplate template = await GetMCSAPI().GetDeviceTemplateAsync(GetMID()); + Assert.NotNull(template); + Assert.NotEmpty(template.Observations); + Assert.NotEmpty(template.Settings); + Assert.NotEmpty(template.Commands); + Assert.NotEmpty(template.Pulses); + Assert.Equal(GetTID(), template.Id); + } + /*[Fact] public void CreateDevice() { @@ -47,6 +111,50 @@ public void CreateDevice() Assert.True(detailedDevice != null); }*/ + [Fact] + public async Task GetDeviceLatestLoginTimestamp() + { + DateTime? timestamp = await GetMCSAPI().GetLatestLoginTimestampAsync(GetMID()); + Assert.NotNull(timestamp); + } + + [Fact] + public async Task GetCurrentDeviceTemplateFirmwareDetails() + { + FirmwareReleaseDescriptor firmware = await GetMCSAPI().GetCurrentDeviceTemplateFirmwareDetailsAsync(GetTID()); + Assert.NotNull(firmware); + } + + [Fact] + public void GetDeviceTemplateFirmwareVariants() + { + var variants = GetMCSAPI().GetDeviceTemplateFirmwareVariants(GetTID()); + Assert.NotNull(variants); + } + + [Fact] + public async Task GetDeviceTemplateFirmwareVariantsAsync() + { + var variants = await GetMCSAPI().GetDeviceTemplateFirmwareVariantsAsync(GetTID()); + Assert.NotNull(variants); + } + + [Fact] + public void GetCurrentDeviceTemplateVariantFirmwareDetails() + { + var firmwareVariantId = 0; + var result = GetMCSAPI().GetCurrentDeviceTemplateVariantFirmwareDetails(GetTID(), firmwareVariantId); + Assert.NotNull(result); + } + + [Fact] + public async Task GetCurrentDeviceTemplateVariantFirmwareDetailsAsync() + { + var firmwareVariantId = 0; + var result = await GetMCSAPI().GetCurrentDeviceTemplateVariantFirmwareDetailsAsync(GetTID(), firmwareVariantId); + Assert.NotNull(result); + } + [Fact] public void SendDeviceCommand() { @@ -62,6 +170,26 @@ public void SendDeviceCommand() Assert.True(GetMCSAPI().SendDeviceCommand(GetMID(), cmd)); } + [Fact] + public async Task SendMultipleDeviceCommands() + { + Command cmd = new Command() + { + Id = MLTEST.Constants.Commands.PollSingle, + Timestamp = DateTime.UtcNow, + ExpiresAt = DateTime.UtcNow.AddMinutes(5), + Arguments = new CommandArgument[] { + new CommandArgument() { Id = MLTEST.Constants.Commands.PollSingleArguments.ObsId, Value = "4" } + } + }; + var commandPackage = new CommandsPackage + { + Commands = new[] { cmd }, + MID = GetMID() + }; + Assert.True(await GetMCSAPI().SendMultipleDeviceCommandAsync(new[] { commandPackage })); + } + [Fact] public void SendDeviceCommandWithMetadata() { @@ -84,68 +212,102 @@ public void SendDeviceCommandWithMetadata() } [Fact] - public void GetNonExistingPersistentWhitelist() + public async Task SendMultipleDeviceCommandsWithMetadata() { - var devices = GetMCSAPI().GetPersistentSubscriptionWhitelist(GetPersistentSubscriptionKey()); - Assert.Null(devices); + var mcs = GetMCSAPI(); + mcs.Metadata = new ApplicationMetadata() + { + Application = "Tests.ServerConnection", + Reference = "SendDeviceCommandWithMetadata" + }; + Command cmd = new Command() + { + Id = MLTEST.Constants.Commands.PollSingle, + Timestamp = DateTime.UtcNow, + ExpiresAt = DateTime.UtcNow.AddMinutes(5), + Arguments = new CommandArgument[] { + new CommandArgument() { Id = MLTEST.Constants.Commands.PollSingleArguments.ObsId, Value = "4" } + } + }; + var commandPackage = new CommandsPackage + { + Commands = new[] { cmd }, + MID = GetMID() + }; + Assert.True(await mcs.SendMultipleDeviceCommandAsync(new[] { commandPackage })); } [Fact] - public async void GetNonExistingPersistentWhitelistAsync() + public async Task GetDeviceCurrentObservation() { - var devices = await GetMCSAPI().GetPersistentSubscriptionWhitelistAsync(GetPersistentSubscriptionKey()); - Assert.Null(devices); + var observation = await GetMCSAPI().GetCurrentObservationAsync(GetMID(), MLTEST.Constants.Observations.BoolTest, DataType.Boolean); + Assert.IsType(observation); } [Fact] - public void GetExistingPersistentWhitelist() + public async Task GetDeviceCurrentObservations() { - var devices = GetMCSAPI().GetPersistentSubscriptionWhitelist(GetPersistentSubscriptionKey()); - Assert.True(devices.Length == 1); + var observations = await GetMCSAPI().GetCurrentObservationsAsync(GetMID()); + Assert.NotNull(observations); } [Fact] - public void CreatePersistentWhitelist() + public async Task GetDeviceObservations() { - var result = GetMCSAPI().CreatePersistentSubscriptionWhitelist(GetPersistentSubscriptionKey()); - Assert.True(result); + var observations = await GetMCSAPI(false).GetObservationsAsync(GetMID(), MLTEST.Constants.Observations.BoolTest, + DataType.Boolean, DateTime.UtcNow.AddDays(-7), DateTime.UtcNow); + Assert.NotNull(observations); } [Fact] - public async void CreatePersistentWhitelistAsync() + public async Task GetDeviceCommandHistory() { - var result = await GetMCSAPI().CreatePersistentSubscriptionWhitelistAsync(GetPersistentSubscriptionKey()); - Assert.True(result); + SendDeviceCommand(); + var commandHistory = await GetMCSAPI().GetDeviceCommandHistoryAsync(GetMID(), DateTime.UtcNow.AddMinutes(-2), DateTime.UtcNow); + Assert.NotEmpty(commandHistory); + Assert.Equal(CommandStatus.Sent, commandHistory.First().Status); } [Fact] - public void GetDeviceTemplateFirmwareVariants() + public async Task GetDeviceSettings() { - var variants = GetMCSAPI().GetDeviceTemplateFirmwareVariants(GetTID()); - Assert.True(variants.Length == 2); + var settings = await GetMCSAPI().GetSettingsAsync(GetMID()); + Assert.NotNull(settings); } [Fact] - public async void GetDeviceTemplateFirmwareVariantsAsync() + public void GetNonExistingPersistentWhitelist() { - var variants = await GetMCSAPI().GetDeviceTemplateFirmwareVariantsAsync(GetTID()); - Assert.True(variants.Length == 2); + var devices = GetMCSAPI().GetPersistentSubscriptionWhitelist(GetPersistentSubscriptionKey()); + Assert.Null(devices); } [Fact] - public void GetCurrentDeviceTemplateVariantFirmwareDetails() + public async void GetNonExistingPersistentWhitelistAsync() { - var firmwareVariantId = 0; - var result = GetMCSAPI().GetCurrentDeviceTemplateVariantFirmwareDetails(GetTID(), firmwareVariantId); - Assert.NotNull(result); + var devices = await GetMCSAPI().GetPersistentSubscriptionWhitelistAsync(GetPersistentSubscriptionKey()); + Assert.Null(devices); } [Fact] - public async void GetCurrentDeviceTemplateVariantFirmwareDetailsAsync() + public void GetExistingPersistentWhitelist() { - var firmwareVariantId = 0; - var result = await GetMCSAPI().GetCurrentDeviceTemplateVariantFirmwareDetailsAsync(GetTID(), firmwareVariantId); - Assert.NotNull(result); + var devices = GetMCSAPI().GetPersistentSubscriptionWhitelist(GetPersistentSubscriptionKey()); + Assert.True(devices.Length == 1); + } + + [Fact] + public void CreatePersistentWhitelist() + { + var result = GetMCSAPI().CreatePersistentSubscriptionWhitelist(GetPersistentSubscriptionKey()); + Assert.True(result); + } + + [Fact] + public async void CreatePersistentWhitelistAsync() + { + var result = await GetMCSAPI().CreatePersistentSubscriptionWhitelistAsync(GetPersistentSubscriptionKey()); + Assert.True(result); } } } diff --git a/tests/Masterloop.Plugin.Application.Tests/appsettings.test.json b/tests/Masterloop.Plugin.Application.Tests/appsettings.test.json deleted file mode 100644 index 99c5cdd..0000000 --- a/tests/Masterloop.Plugin.Application.Tests/appsettings.test.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Hostname": "", - "Username": "", - "Password": "", - "UseHTTPS": true, - "MID": "", - "TID": "", - "PersistentSubscriptionKey": "" -} \ No newline at end of file