├── .nuke ├── global.json ├── .whitesource ├── tests ├── Mozilla.IoT.WebThing.Integration.Test │ ├── Events │ │ ├── Number │ │ │ ├── IntEvent.cs │ │ │ ├── ByteEvent.cs │ │ │ ├── FloatEvent.cs │ │ │ ├── LongEvent.cs │ │ │ ├── SByteEvent.cs │ │ │ ├── ShortEvent.cs │ │ │ ├── UIntEvent.cs │ │ │ ├── ULongEvent.cs │ │ │ ├── DecimalEvent.cs │ │ │ ├── DoubleEvent.cs │ │ │ └── UShortEvent.cs │ │ ├── String │ │ │ ├── CharEvent.cs │ │ │ ├── StringEvent.cs │ │ │ ├── GuidEvent.cs │ │ │ ├── DateTimeEvent.cs │ │ │ ├── TimeSpanEvent.cs │ │ │ ├── DateTimeOffsetEvent.cs │ │ │ └── EnumEvent.cs │ │ ├── Boolean │ │ │ └── BoolEvent.cs │ │ └── AbstractEventTest.cs │ ├── Web │ │ ├── WebSockets │ │ │ ├── WebSocketRequest.cs │ │ │ └── Serializer.cs │ │ ├── TestHost.cs │ │ ├── Startup.cs │ │ └── Things │ │ │ ├── EventThing.cs │ │ │ └── ActionThing.cs │ ├── Property │ │ ├── Boolean │ │ │ └── BoolProperty.cs │ │ ├── String │ │ │ └── EnumProperty.cs │ │ └── AbstractPropertyTest.cs │ ├── Mozilla.IoT.WebThing.Integration.Test.csproj │ ├── Action │ │ ├── Boolean │ │ │ └── BooleanAction.cs │ │ ├── String │ │ │ └── EnumActionTest.cs │ │ └── AbstractActionTest.cs │ └── mDNS │ │ └── MDnsTest.cs ├── TestThing │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── TestThing.csproj │ ├── Program.cs │ ├── Converters │ │ └── DateTimeConvert.cs │ ├── Startup.cs │ └── Things │ │ └── LampThing.cs └── Mozilla.IoT.WebThing.Test │ ├── Convertibles │ ├── Strings │ │ ├── StringConvertibleTest.cs │ │ ├── CharConvertibleTest.cs │ │ ├── GuidConvertibleTest.cs │ │ ├── TimeSpanConvertibleTest.cs │ │ ├── DateTimeConvertibleTest.cs │ │ └── DateTimeOffsetConvertibleTest.cs │ ├── Boolean │ │ └── BooleanConvertibleTest.cs │ ├── Numbers │ │ ├── IntConvertibleTest.cs │ │ ├── ByteConvertibleTest.cs │ │ ├── LongConvertibleTest.cs │ │ ├── UIntConvertibleTest.cs │ │ ├── SByteConvertibleTest.cs │ │ ├── ShortConvertibleTest.cs │ │ ├── ULongConvertibleTest.cs │ │ ├── UShortConvertibleTest.cs │ │ ├── FloatConvertibleTest.cs │ │ ├── DoubleConvertibleTest.cs │ │ └── DecimalConvertibleTest.cs │ └── BaseConvertibleTest.cs │ ├── Mozilla.IoT.WebThing.Test.csproj │ ├── Events │ └── EventCollectionTest.cs │ └── Extensions │ ├── FixtureExtensions.cs │ └── IServiceExtensionsTest.cs ├── sample ├── SingleThing │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── SingleThing.csproj │ ├── Program.cs │ ├── Startup.cs │ └── Things │ │ └── LampThing.cs └── MultiThing │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── MultiThing.csproj │ ├── Program.cs │ ├── Startup.cs │ └── Things │ └── FakeGpioHumiditySensor.cs ├── src ├── Mozilla.IoT.WebThing │ ├── Assembly.cs │ ├── Exceptions │ │ ├── InvalidValueException.cs │ │ ├── ReadOnlyPropertyException.cs │ │ ├── WriteOnlyPropertyException.cs │ │ └── ThingException.cs │ ├── Convertibles │ │ ├── IConvertable.cs │ │ ├── ObjectConvertible.cs │ │ ├── Strings │ │ │ ├── StringConvertible.cs │ │ │ ├── TimeSpanConvertible.cs │ │ │ ├── CharConvertible.cs │ │ │ ├── EnumConvertible.cs │ │ │ ├── GuidConvertible.cs │ │ │ ├── DateTimeConvertible.cs │ │ │ └── DateTimeOffsetConvertible.cs │ │ ├── BooleanConvertible.cs │ │ ├── Collection │ │ │ ├── ListConvertible.cs │ │ │ ├── HashSetConvertible.cs │ │ │ ├── ArrayConvertible.cs │ │ │ └── LinkedListConvertible.cs │ │ └── InputConvertible.cs │ ├── Extensions │ │ ├── JsonSerializerOptionsExtensions.cs │ │ ├── StringExtensions.cs │ │ ├── IThingCollectionBuilder.cs │ │ ├── ThingOption.cs │ │ └── IEndpointRouteBuilderExtensions.cs │ ├── WebSockets │ │ ├── RequestCommand.cs │ │ ├── WebSocketResponse.cs │ │ ├── IWebSocketAction.cs │ │ └── AddEventSubscription.cs │ ├── Const.cs │ ├── Actions │ │ ├── ActionStatus.cs │ │ └── IActionInformationFactory.cs │ ├── Json │ │ ├── SchemaValidations │ │ │ ├── IJsonSchemaValidation.cs │ │ │ ├── Boolean │ │ │ │ └── BooleanJsonSchemaValidation.cs │ │ │ ├── String │ │ │ │ ├── EnumJsonSchemaValidation.cs │ │ │ │ ├── CharJsonSchemaValidation.cs │ │ │ │ ├── GuidJsonSchemaValidation.cs │ │ │ │ ├── TimeSpanJsonSchemaValidation.cs │ │ │ │ ├── DateTimeJsonSchemaValidation.cs │ │ │ │ ├── DateTimeOffsetJsonSchemaValidation.cs │ │ │ │ └── StringJsonSchemaValidation.cs │ │ │ ├── Input │ │ │ │ └── InputJsonSchemaValidation.cs │ │ │ └── Number │ │ │ │ └── NumberJsonSchemaValidation.cs │ │ ├── Convertibles │ │ │ ├── IJsonConvertible.cs │ │ │ ├── String │ │ │ │ ├── SystemTexJsonStringConvertible.cs │ │ │ │ ├── SystemTexJsonGuidConvertible.cs │ │ │ │ ├── SystemTexJsonDateTimeConvertible.cs │ │ │ │ ├── SystemTexJsonCharConvertible.cs │ │ │ │ ├── SystemTexJsonTimeSpanConvertible.cs │ │ │ │ ├── SystemTexJsonDateTimeOffsetConvertible.cs │ │ │ │ └── SystemTexJsonEnumConvertible.cs │ │ │ ├── Number │ │ │ │ └── SystemTexJsonNumberConvertible.cs │ │ │ ├── Boolean │ │ │ │ └── SystemTexJsonBooleanConvertible.cs │ │ │ ├── SystemTexJsonConvertible.cs │ │ │ ├── Array │ │ │ │ └── SystemTexJsonArrayConvertible.cs │ │ │ └── Input │ │ │ │ └── SystemTextJsonInputConvertible.cs │ │ ├── JsonType.cs │ │ ├── IJsonConvert.cs │ │ └── SystemTextJson.cs │ ├── SetPropertyResult.cs │ ├── Factories │ │ ├── IJsonConvertibleFactory.cs │ │ ├── IConvertibleFactory.cs │ │ ├── IThingContextFactory.cs │ │ ├── IJsonShemaValidationFactory.cs │ │ ├── IPropertyFactory.cs │ │ └── Imp │ │ │ └── SystemTexJsonConvertibleFactory.cs │ ├── Events │ │ ├── InternalEventHandle.cs │ │ ├── Event.cs │ │ └── EventAddedEventArgs.cs │ ├── Link.cs │ ├── IThingProperty.cs │ ├── Converts │ │ └── ActionStatusConverter.cs │ ├── Mozilla.IoT.WebThing.csproj │ ├── Endpoints │ │ ├── GetAllThings.cs │ │ ├── GetThing.cs │ │ ├── GetActions.cs │ │ ├── GetProperties.cs │ │ ├── GetEvents.cs │ │ ├── GetAction.cs │ │ └── GetEvent.cs │ ├── Builders │ │ ├── IPropertyBuilder.cs │ │ ├── IEventBuilder.cs │ │ ├── IActionBuilder.cs │ │ └── IThingResponseBuilder.cs │ ├── Attributes │ │ └── ThingActionAttribute.cs │ └── Middlewares │ │ └── ThingAdapter.cs └── Mozilla.IoT.WebThing.Newtonsoft │ ├── Convertibles │ ├── Boolean │ │ └── NewtonsoftBooleanConvertible.cs │ ├── Strings │ │ ├── NewtonsoftStringConvertible.cs │ │ ├── NewtonsoftDateTimeConvertible.cs │ │ ├── NewtonsoftDateTimeOffsetConvertible.cs │ │ ├── NewtonsoftCharConvertible.cs │ │ ├── NewtonsoftGuidConvertible.cs │ │ ├── NewtonsoftTimeSpanConvertible.cs │ │ └── NewtonsoftEnumConvertible.cs │ ├── Number │ │ └── NewtonsoftNumberConvertible.cs │ ├── NewtonsoftConvertible.cs │ ├── Array │ │ └── NewtonsoftArrayConvertible.cs │ └── Input │ │ └── NewtonsoftInputConvertible.cs │ ├── Extensions │ ├── IThingCollectionBuilderExtension.cs │ └── ThingOptionExtensions.cs │ ├── Mozilla.IoT.WebThing.Newtonsoft.csproj │ └── NewtonsoftJsonConvert.cs ├── Mozzila.IoT.WebThing.sln.DotSettings ├── GitVersion.yml └── Directory.Build.props /.nuke: -------------------------------------------------------------------------------- 1 | Mozzila.IoT.WebThing.sln -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "3.1.301" 4 | } 5 | } -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "generalSettings": { 3 | "shouldScanRepo": true 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure" 7 | } 8 | } -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/IntEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class IntEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/ByteEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class ByteEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/FloatEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class FloatEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/LongEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class LongEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/SByteEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class SByteEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/ShortEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class ShortEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/UIntEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class UIntEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/ULongEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class ULongEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/String/CharEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.String 2 | { 3 | public class CharEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /sample/SingleThing/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/DecimalEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class DecimalEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/DoubleEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class DoubleEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Number/UShortEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Number 2 | { 3 | public class UShortEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/String/StringEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.String 2 | { 3 | public class StringEvent : AbstractClassEventTest 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/TestThing/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/MultiThing/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/Boolean/BoolEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.Boolean 2 | { 3 | public class BoolEvent : AbstractStructEventTest 4 | { 5 | 6 | 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sample/SingleThing/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /tests/TestThing/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /sample/MultiThing/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/String/GuidEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.String 4 | { 5 | public class GuidEvent : AbstractStructEventTest 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/String/DateTimeEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.String 4 | { 5 | public class DateTimeEvent : AbstractStructEventTest 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/String/TimeSpanEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.String 4 | { 5 | public class TimeSpanEvent : AbstractStructEventTest 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Assembly.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Mozilla.IoT.WebThing.Test")] 4 | [assembly: InternalsVisibleTo("Mozilla.IoT.WebThing.Integration.Test")] 5 | [assembly: InternalsVisibleTo("Mozilla.IoT.WebThing.Intregration.Test")] 6 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Exceptions/InvalidValueException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Exceptions 4 | { 5 | /// 6 | /// 7 | /// 8 | public class InvalidValueException : Exception 9 | { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/String/DateTimeOffsetEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.String 4 | { 5 | public class DateTimeOffsetEvent : AbstractStructEventTest 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Web/WebSockets/WebSocketRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Web.WebSockets 2 | { 3 | public class WebSocketRequest 4 | { 5 | public string MessageType { get; set; } 6 | public object Data { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/String/EnumEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Integration.Test.Events.String 2 | { 3 | public class EnumEvent : AbstractStructEventTest 4 | { 5 | 6 | } 7 | 8 | public enum Foo 9 | { 10 | A, 11 | B, 12 | C 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/SingleThing/SingleThing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ThingAppTargetFrameworks) 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/TestThing/TestThing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ThingAppTargetFrameworks) 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/MultiThing/MultiThing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Mozzila.IoT.WebThing.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Strings/StringConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using Mozilla.IoT.WebThing.Convertibles.Strings; 2 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 3 | 4 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Strings 5 | { 6 | public class StringConvertibleTest : BaseConvertibleTest 7 | { 8 | protected override IConvertible CreateConvertible() 9 | { 10 | return StringConvertible.Instance; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/IConvertable.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Convertibles 2 | { 3 | /// 4 | /// Represent convertible object to other object. 5 | /// 6 | public interface IConvertible 7 | { 8 | /// 9 | /// Convert object to other object. 10 | /// 11 | /// The instance to be convert. 12 | /// The convert object. 13 | object? Convert(object? value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Extensions/JsonSerializerOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Text.Json 2 | { 3 | internal static class JsonSerializerOptionsExtensions 4 | { 5 | public static string GetPropertyName(this JsonSerializerOptions options, string propertyName) 6 | { 7 | if (options.PropertyNamingPolicy != null) 8 | { 9 | return options.PropertyNamingPolicy.ConvertName(propertyName); 10 | } 11 | 12 | return propertyName; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDeployment 2 | build-metadata-padding: 5 3 | tag-prefix: 'v' 4 | assembly-versioning-scheme: MajorMinorPatch 5 | assembly-versioning-format: '{Major}.{Minor}.{Patch}' 6 | branches: 7 | master: 8 | regex: main 9 | tag: 'unstable' 10 | increment: None 11 | release: 12 | regex: releases?[/-] 13 | mode: ContinuousDelivery 14 | increment: None 15 | tag: '' 16 | is-release-branch: true 17 | pull-request: 18 | increment: None 19 | ignore: 20 | sha: [] -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/WebSockets/RequestCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.WebSockets 2 | { 3 | /// 4 | /// Represent the Web Socket request command. 5 | /// 6 | public class RequestCommand 7 | { 8 | /// 9 | /// The message type. 10 | /// 11 | public string? MessageType { get; set; } 12 | 13 | /// 14 | /// The object data. 15 | /// 16 | public object? Data { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Const.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing 2 | { 3 | /// 4 | /// The default values 5 | /// 6 | public class Const 7 | { 8 | /// 9 | /// Default value of thing context. 10 | /// 11 | public const string DefaultContext = "https://iot.mozilla.org/schemas"; 12 | 13 | /// 14 | /// Default value of content type. 15 | /// 16 | public const string ContentType = "application/json"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Actions/ActionStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Actions 2 | { 3 | /// 4 | /// Action status 5 | /// 6 | public enum ActionStatus 7 | { 8 | /// 9 | /// Waiting to be execute. 10 | /// 11 | Created, 12 | 13 | /// 14 | /// Executing action. 15 | /// 16 | Pending, 17 | 18 | /// 19 | /// Action completed. 20 | /// 21 | Completed 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/IJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations 2 | { 3 | /// 4 | /// Validate Json Schema. 5 | /// 6 | public interface IJsonSchemaValidation 7 | { 8 | /// 9 | /// Check if the object match with schema. 10 | /// 11 | /// The value to be compared 12 | /// Return true if is valid, otherwise return false. 13 | bool IsValid(object? value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/ObjectConvertible.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Convertibles 2 | { 3 | /// 4 | /// Convert value to 5 | /// 6 | public class ObjectConvertible : IConvertible 7 | { 8 | /// 9 | /// Static Instance of 10 | /// 11 | public static ObjectConvertible Instance { get; } = new ObjectConvertible(); 12 | 13 | /// 14 | public object? Convert(object? value) 15 | => value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/SetPropertyResult.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing 2 | { 3 | /// 4 | /// Result of set property 5 | /// 6 | public enum SetPropertyResult 7 | { 8 | /// 9 | /// Set property is OK. 10 | /// 11 | Ok, 12 | 13 | /// 14 | /// Invalid value to set value. 15 | /// 16 | InvalidValue, 17 | 18 | /// 19 | /// If property is read-only. 20 | /// 21 | ReadOnly 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Strings/StringConvertible.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Convertibles.Strings 2 | { 3 | /// 4 | /// Convert value to 5 | /// 6 | public class StringConvertible : IConvertible 7 | { 8 | /// 9 | /// Static Instance of 10 | /// 11 | public static StringConvertible Instance { get; } = new StringConvertible(); 12 | 13 | /// 14 | public object? Convert(object? value) => value?.ToString(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sample/MultiThing/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace MultiThing 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/SingleThing/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace SingleThing 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Extensions 4 | { 5 | internal static class StringExtensions 6 | { 7 | public static string FirstCharToUpper(this string input) 8 | { 9 | return input switch 10 | { 11 | null => throw new ArgumentNullException(nameof(input)), 12 | "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)), 13 | _ => input[0].ToString().ToUpper() + input.Substring(1) 14 | }; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Factories/IJsonConvertibleFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mozilla.IoT.WebThing.Json.Convertibles; 3 | 4 | namespace Mozilla.IoT.WebThing.Factories 5 | { 6 | /// 7 | /// The factory of . 8 | /// 9 | public interface IJsonConvertibleFactory 10 | { 11 | /// 12 | /// Create new instance of for . 13 | /// 14 | /// New instance of . 15 | IJsonConvertible Create(TypeCode typeCode, Type type); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Factories/IConvertibleFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 3 | 4 | namespace Mozilla.IoT.WebThing.Factories 5 | { 6 | /// 7 | /// The factory of . 8 | /// 9 | public interface IConvertibleFactory 10 | { 11 | /// 12 | /// Create new instance of for . 13 | /// 14 | /// New instance of . 15 | IConvertible? Create(TypeCode typeCode, Type type); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Exceptions/ReadOnlyPropertyException.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Exceptions 2 | { 3 | /// 4 | /// 5 | /// 6 | public class ReadOnlyPropertyException : ThingException 7 | { 8 | /// 9 | /// 10 | /// 11 | public string PropertyName { get; } 12 | 13 | /// 14 | /// 15 | /// 16 | /// 17 | public ReadOnlyPropertyException(string propertyName) 18 | : base($"Trying to set {propertyName}.") 19 | { 20 | PropertyName = propertyName; 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Exceptions/WriteOnlyPropertyException.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Exceptions 2 | { 3 | /// 4 | /// 5 | /// 6 | public class WriteOnlyPropertyException : ThingException 7 | { 8 | /// 9 | /// 10 | /// 11 | public string PropertyName { get; } 12 | 13 | /// 14 | /// 15 | /// 16 | /// 17 | public WriteOnlyPropertyException(string propertyName) 18 | : base($"Trying to get {propertyName}.") 19 | { 20 | PropertyName = propertyName; 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Factories/IThingContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Mozilla.IoT.WebThing.Extensions; 2 | 3 | namespace Mozilla.IoT.WebThing.Factories 4 | { 5 | /// 6 | /// The factory 7 | /// 8 | public interface IThingContextFactory 9 | { 10 | /// 11 | /// Create new instance of . 12 | /// 13 | /// The . 14 | /// The . 15 | /// The new instance of . 16 | ThingContext Create(Thing thing, ThingOption option); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/IJsonConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles 4 | { 5 | /// 6 | /// Represent convertible/getter value from json object. 7 | /// 8 | public interface IJsonConvertible 9 | { 10 | /// 11 | /// Try convert json value to specific value. 12 | /// 13 | /// The json object; 14 | /// The result. 15 | /// return true if could get value, otherwise return false. 16 | bool TryConvert(object source, [MaybeNull]out object? result); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Actions/IActionInformationFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Mozilla.IoT.WebThing.Actions 4 | { 5 | /// 6 | /// Create new instance of based in value of . 7 | /// 8 | public interface IActionInformationFactory 9 | { 10 | /// 11 | /// Create new instance of . 12 | /// 13 | /// The value of input. 14 | /// New instance of . 15 | ThingActionInformation Convert(Dictionary? values); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/WebSockets/WebSocketResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.WebSockets 2 | { 3 | internal class WebSocketResponse 4 | { 5 | public WebSocketResponse(string messageType, object data) 6 | { 7 | MessageType = messageType; 8 | Data = data; 9 | } 10 | 11 | public string MessageType { get; } 12 | public object Data { get; } 13 | } 14 | 15 | internal class ErrorResponse 16 | { 17 | public ErrorResponse(string status, string message) 18 | { 19 | Status = status; 20 | Message = message; 21 | } 22 | 23 | public string Status { get; } 24 | public string Message { get; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Events/InternalEventHandle.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Events 2 | { 3 | /// 4 | /// Handler all event 5 | /// 6 | public static class InternalEventHandle 7 | { 8 | /// 9 | /// Handle event and enqueue the event 10 | /// 11 | /// The sender, it must be . 12 | /// The received args. 13 | /// The event name 14 | public static void Handler(object sender, object args, string name) 15 | { 16 | ((Thing)sender).ThingContext.Events[name].Enqueue(new Event(args), name, (Thing)sender); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Events/AbstractEventTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Mozilla.IoT.WebThing.Factories; 4 | 5 | namespace Mozilla.IoT.WebThing.Integration.Test.Events 6 | { 7 | public abstract class AbstractEventTest 8 | { 9 | protected IThingContextFactory Factory { get; } 10 | protected Fixture Fixture { get; } 11 | 12 | protected AbstractEventTest() 13 | { 14 | var collection = new ServiceCollection(); 15 | collection.AddThings(); 16 | var provider = collection.BuildServiceProvider(); 17 | Factory = provider.GetRequiredService(); 18 | Fixture = new Fixture(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp3.1 4 | netcoreapp3.1 5 | 6 | Mozilla.IoT.WebThings 7 | en-US 8 | 9 | https://github.com/lillo42/webthing-csharp 10 | false 11 | true 12 | true 13 | 8 14 | true 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/TestThing/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace TestThing 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Strings/TimeSpanConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Convertibles.Strings 4 | { 5 | /// 6 | /// Convert value to 7 | /// 8 | public class TimeSpanConvertible : IConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static TimeSpanConvertible Instance { get; } = new TimeSpanConvertible(); 14 | 15 | /// 16 | public object? Convert(object? value) 17 | { 18 | if (value == null) 19 | { 20 | return null; 21 | } 22 | 23 | return TimeSpan.Parse(value.ToString()!); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Events/Event.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Events 4 | { 5 | /// 6 | /// Represent raised event. 7 | /// 8 | public class Event 9 | { 10 | /// 11 | /// Initialize a new instance of . 12 | /// 13 | /// The value raised. 14 | public Event(object data) 15 | { 16 | Data = data; 17 | Timestamp = DateTime.UtcNow; 18 | } 19 | 20 | /// 21 | /// Event value. 22 | /// 23 | public object Data { get; } 24 | 25 | /// 26 | /// Datetime the event was raised. 27 | /// 28 | public DateTime Timestamp { get; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/BooleanConvertible.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Convertibles 2 | { 3 | /// 4 | /// Convert value to 5 | /// 6 | public class BooleanConvertible : IConvertible 7 | { 8 | /// 9 | /// Static Instance of 10 | /// 11 | public static BooleanConvertible Instance { get; } = new BooleanConvertible(); 12 | 13 | /// 14 | public object? Convert(object? value) 15 | { 16 | if (value == null) 17 | { 18 | return null; 19 | } 20 | 21 | if (value is bool b) 22 | { 23 | return b; 24 | } 25 | 26 | return System.Convert.ToBoolean(value); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Strings/CharConvertible.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Convertibles.Strings 2 | { 3 | /// 4 | /// Convert value to 5 | /// 6 | public class CharConvertible : IConvertible 7 | { 8 | /// 9 | /// Static Instance of 10 | /// 11 | public static CharConvertible Instance { get; } = new CharConvertible(); 12 | 13 | 14 | /// 15 | public object? Convert(object? value) 16 | { 17 | if (value == null) 18 | { 19 | return null; 20 | } 21 | 22 | if (value is char c) 23 | { 24 | return c; 25 | } 26 | 27 | return char.Parse(value.ToString()!); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Web/TestHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.TestHost; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | namespace Mozilla.IoT.WebThing.Integration.Test.Web 7 | { 8 | public class TestHost : IDisposable 9 | { 10 | public IHost Host { get; } 11 | 12 | public TestHost() 13 | { 14 | Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(null) 15 | .ConfigureWebHostDefaults(builder => 16 | { 17 | builder 18 | .UseTestServer() 19 | .UseStartup(); 20 | }) 21 | .Start(); 22 | } 23 | 24 | public void Dispose() 25 | { 26 | Host.Dispose(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Boolean/BooleanConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles; 4 | using Xunit; 5 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 6 | 7 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Boolean 8 | { 9 | public class BooleanConvertibleTest : BaseConvertibleTest 10 | { 11 | protected override IConvertible CreateConvertible() 12 | { 13 | return BooleanConvertible.Instance; 14 | } 15 | 16 | [Fact] 17 | public void Create_Should_ConvertType_When_ValueIsOtherType() 18 | { 19 | var convertible = CreateConvertible(); 20 | var value = Fixture.Create(); 21 | convertible.Convert(value.ToString()) 22 | .Should().Be(value); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Strings/CharConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Strings; 4 | using Xunit; 5 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 6 | 7 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Strings 8 | { 9 | public class CharConvertibleTest : BaseConvertibleTest 10 | { 11 | protected override IConvertible CreateConvertible() 12 | { 13 | return CharConvertible.Instance; 14 | } 15 | 16 | [Fact] 17 | public void Create_Should_ConvertType_When_ValueIsOtherType() 18 | { 19 | var convertible = CreateConvertible(); 20 | var value = Fixture.Create(); 21 | convertible.Convert(value.ToString()) 22 | .Should().Be(value); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Strings/EnumConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Convertibles.Strings 4 | { 5 | /// 6 | /// Convert value to 7 | /// 8 | public class EnumConvertible : IConvertible 9 | { 10 | private readonly Type _enum; 11 | 12 | /// 13 | /// Initialize a new instance of . 14 | /// 15 | /// The enum type 16 | public EnumConvertible(Type @enum) 17 | { 18 | _enum = @enum ?? throw new ArgumentNullException(nameof(@enum)); 19 | } 20 | 21 | /// 22 | public object? Convert(object? value) 23 | { 24 | return value == null ? null : Enum.Parse(_enum, value.ToString()!, true); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Strings/GuidConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Convertibles.Strings 4 | { 5 | /// 6 | /// Convert value to 7 | /// 8 | public class GuidConvertible : IConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static GuidConvertible Instance { get; } = new GuidConvertible(); 14 | 15 | /// 16 | public object? Convert(object? value) 17 | { 18 | if (value == null) 19 | { 20 | return null; 21 | } 22 | 23 | if (value is Guid guid) 24 | { 25 | return guid; 26 | } 27 | 28 | return Guid.Parse(value.ToString()!); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Strings/GuidConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Convertibles.Strings; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Strings 9 | { 10 | public class GuidConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return GuidConvertible.Instance; 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Strings/TimeSpanConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Convertibles.Strings; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Strings 9 | { 10 | public class TimeSpanConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return TimeSpanConvertible.Instance; 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Factories/IJsonShemaValidationFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mozilla.IoT.WebThing.Builders; 3 | using Mozilla.IoT.WebThing.Json.SchemaValidations; 4 | 5 | namespace Mozilla.IoT.WebThing.Factories 6 | { 7 | /// 8 | /// The factory of . 9 | /// 10 | public interface IJsonSchemaValidationFactory 11 | { 12 | /// 13 | /// Create new instance of for . 14 | /// 15 | /// 16 | /// The . 17 | /// The . 18 | /// New instance of . 19 | IJsonSchemaValidation Create(TypeCode typeCode, JsonSchema jsonSchema, Type type); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Link.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing 4 | { 5 | /// 6 | /// Represent link 7 | /// 8 | public class Link 9 | { 10 | /// 11 | /// Initialize a new instance of . 12 | /// 13 | /// Representation of a URL. 14 | /// Describing a relationship 15 | public Link(string? rel, string href) 16 | { 17 | Href = href ?? throw new ArgumentNullException(nameof(href)); 18 | Rel = rel; 19 | } 20 | 21 | /// 22 | /// Representation of a URL. 23 | /// 24 | public string Href { get; } 25 | 26 | /// 27 | /// Describing a relationship 28 | /// 29 | public string? Rel { get; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/IntConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class IntConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.Int32); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Strings/DateTimeConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Convertibles.Strings 4 | { 5 | /// 6 | /// Convert value to 7 | /// 8 | public class DateTimeConvertible : IConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static DateTimeConvertible Instance { get; } = new DateTimeConvertible(); 14 | 15 | /// 16 | public object? Convert(object? value) 17 | { 18 | if (value == null) 19 | { 20 | return null; 21 | } 22 | 23 | if (value is DateTime dateTime) 24 | { 25 | return dateTime; 26 | } 27 | 28 | return DateTime.Parse(value.ToString()!); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/JsonType.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Json 2 | { 3 | /// 4 | /// Represent json type 5 | /// 6 | public enum JsonType 7 | { 8 | /// 9 | /// The 10 | /// 11 | Boolean, 12 | 13 | /// 14 | /// The 15 | /// 16 | String, 17 | 18 | /// 19 | /// The 20 | /// 21 | Integer, 22 | 23 | /// 24 | /// The 25 | /// 26 | Number, 27 | 28 | /// 29 | /// The array 30 | /// 31 | Array, 32 | 33 | /// 34 | /// The 35 | /// 36 | Object 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/ByteConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class ByteConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.Byte); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/LongConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class LongConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.Int64); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/UIntConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class UIntConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.UInt32); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/SByteConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class SByteConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.SByte); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/ShortConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class ShortConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.Int16); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/ULongConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class ULongConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.UInt64); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/UShortConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles.Number; 4 | using Mozilla.IoT.WebThing.Factories; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 9 | { 10 | public class UShortConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return new NumberConvertible(TypeCode.UInt16); 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString()) 23 | .Should().Be(value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Strings/DateTimeConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Convertibles.Strings; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Strings 9 | { 10 | public class DateTimeConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return DateTimeConvertible.Instance; 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString("O")) 23 | .Should().Be(DateTime.Parse(value.ToString("O"))); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/Boolean/BooleanJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations 2 | { 3 | /// 4 | /// Represent boolean json schema validation. 5 | /// 6 | public class BooleanJsonSchemaValidation : IJsonSchemaValidation 7 | { 8 | private readonly bool _isNullable; 9 | 10 | /// 11 | /// Initialize a new instance of . 12 | /// 13 | /// Accepted null value. 14 | public BooleanJsonSchemaValidation(bool isNullable) 15 | { 16 | _isNullable = isNullable; 17 | } 18 | 19 | /// 20 | public bool IsValid(object? value) 21 | { 22 | if (value == null) 23 | { 24 | return _isNullable; 25 | } 26 | 27 | return value is bool; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/String/EnumJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.String 4 | { 5 | /// 6 | /// Represent json schema validation. 7 | /// 8 | public class EnumJsonSchemaValidation : IJsonSchemaValidation 9 | { 10 | private readonly bool _isNullable; 11 | 12 | /// 13 | /// Initialize a new instance of . 14 | /// 15 | /// Accepted null value. 16 | public EnumJsonSchemaValidation(bool isNullable) 17 | { 18 | _isNullable = isNullable; 19 | } 20 | 21 | /// 22 | public bool IsValid(object? value) 23 | => value switch 24 | { 25 | null => _isNullable, 26 | _ => true 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Strings/DateTimeOffsetConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Convertibles.Strings; 5 | using Xunit; 6 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 7 | 8 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Strings 9 | { 10 | public class DateTimeOffsetConvertibleTest : BaseConvertibleTest 11 | { 12 | protected override IConvertible CreateConvertible() 13 | { 14 | return DateTimeOffsetConvertible.Instance; 15 | } 16 | 17 | [Fact] 18 | public void Create_Should_ConvertType_When_ValueIsOtherType() 19 | { 20 | var convertible = CreateConvertible(); 21 | var value = Fixture.Create(); 22 | convertible.Convert(value.ToString("O")) 23 | .Should().Be(DateTimeOffset.Parse(value.ToString("O"))); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Strings/DateTimeOffsetConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Convertibles.Strings 4 | { 5 | /// 6 | /// Convert value to 7 | /// 8 | public class DateTimeOffsetConvertible : IConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static DateTimeOffsetConvertible Instance { get; } = new DateTimeOffsetConvertible(); 14 | 15 | /// 16 | public object? Convert(object? value) 17 | { 18 | if (value == null) 19 | { 20 | return null; 21 | } 22 | 23 | if (value is DateTimeOffset dateTimeOffset) 24 | { 25 | return dateTimeOffset; 26 | } 27 | 28 | return DateTimeOffset.Parse(value.ToString()!); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/FloatConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Convertibles.Number; 5 | using Mozilla.IoT.WebThing.Factories; 6 | using Xunit; 7 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 8 | 9 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 10 | { 11 | public class FloatConvertibleTest : BaseConvertibleTest 12 | { 13 | protected override IConvertible CreateConvertible() 14 | { 15 | return new NumberConvertible(TypeCode.Float); 16 | } 17 | 18 | [Fact] 19 | public void Create_Should_ConvertType_When_ValueIsOtherType() 20 | { 21 | var convertible = CreateConvertible(); 22 | var value = Fixture.Create(); 23 | convertible.Convert(value.ToString(CultureInfo.InvariantCulture)) 24 | .Should().Be(value); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/DoubleConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Convertibles.Number; 5 | using Mozilla.IoT.WebThing.Factories; 6 | using Xunit; 7 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 8 | 9 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 10 | { 11 | public class DoubleConvertibleTest : BaseConvertibleTest 12 | { 13 | protected override IConvertible CreateConvertible() 14 | { 15 | return new NumberConvertible(TypeCode.Double); 16 | } 17 | 18 | [Fact] 19 | public void Create_Should_ConvertType_When_ValueIsOtherType() 20 | { 21 | var convertible = CreateConvertible(); 22 | var value = Fixture.Create(); 23 | convertible.Convert(value.ToString(CultureInfo.InvariantCulture)) 24 | .Should().Be(value); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/Numbers/DecimalConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Convertibles.Number; 5 | using Mozilla.IoT.WebThing.Factories; 6 | using Xunit; 7 | using IConvertible = Mozilla.IoT.WebThing.Convertibles.IConvertible; 8 | 9 | namespace Mozilla.IoT.WebThing.Test.Convertibles.Numbers 10 | { 11 | public class DeciamlConvertibleTest : BaseConvertibleTest 12 | { 13 | protected override IConvertible CreateConvertible() 14 | { 15 | return new NumberConvertible(TypeCode.Decimal); 16 | } 17 | 18 | [Fact] 19 | public void Create_Should_ConvertType_When_ValueIsOtherType() 20 | { 21 | var convertible = CreateConvertible(); 22 | var value = Fixture.Create(); 23 | convertible.Convert(value.ToString(CultureInfo.InvariantCulture)) 24 | .Should().Be(value); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Boolean/NewtonsoftBooleanConvertible.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Boolean 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class NewtonsoftBooleanConvertible : NewtonsoftConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static NewtonsoftBooleanConvertible Instance { get; } = new NewtonsoftBooleanConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JToken source, out object? result) 17 | { 18 | if (source.Type == JTokenType.Boolean) 19 | { 20 | result = null; 21 | return false; 22 | } 23 | 24 | result = source.Value(); 25 | return true; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Strings/NewtonsoftStringConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Strings 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class NewtonsoftStringConvertible : NewtonsoftConvertible 10 | { 11 | /// 12 | /// Static Instance of 13 | /// 14 | public static NewtonsoftStringConvertible Instance { get; } = new NewtonsoftStringConvertible(); 15 | 16 | /// 17 | protected override bool TryConvert(JToken source, out object? result) 18 | { 19 | result = null; 20 | if (source.Type != JTokenType.String) 21 | { 22 | return false; 23 | } 24 | 25 | result = source.Value(); 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/String/SystemTexJsonStringConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles.String 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class SystemTexJsonStringConvertible : SystemTexJsonConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static SystemTexJsonStringConvertible Instance { get; } = new SystemTexJsonStringConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JsonElement source, out object? result) 17 | { 18 | if (source.ValueKind != JsonValueKind.String) 19 | { 20 | result = null; 21 | return false; 22 | } 23 | 24 | result = source.GetString(); 25 | return true; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Strings/NewtonsoftDateTimeConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Strings 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class NewtonsoftDateTimeConvertible : NewtonsoftConvertible 10 | { 11 | /// 12 | /// Static Instance of 13 | /// 14 | public static NewtonsoftDateTimeConvertible Instance { get; } = new NewtonsoftDateTimeConvertible(); 15 | 16 | /// 17 | protected override bool TryConvert(JToken source, out object? result) 18 | { 19 | result = null; 20 | if (source.Type != JTokenType.Date) 21 | { 22 | return false; 23 | } 24 | 25 | result = source.Value(); 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Factories/IPropertyFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mozilla.IoT.WebThing.Builders; 3 | 4 | namespace Mozilla.IoT.WebThing.Factories 5 | { 6 | /// 7 | /// The factory of . 8 | /// 9 | public interface IPropertyFactory 10 | { 11 | /// 12 | /// Create new instance of . 13 | /// 14 | /// The of property. 15 | /// The . 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// New instance of . 21 | IThingProperty Create(Type propertyType, JsonSchema jsonSchema, Thing thing, 22 | Action? setter, Func getter, string originPropertyName); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Convertibles/BaseConvertibleTest.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using FluentAssertions; 3 | using Mozilla.IoT.WebThing.Convertibles; 4 | using Xunit; 5 | 6 | namespace Mozilla.IoT.WebThing.Test.Convertibles 7 | { 8 | public abstract class BaseConvertibleTest 9 | { 10 | protected Fixture Fixture { get; } 11 | 12 | public BaseConvertibleTest() 13 | { 14 | Fixture = new Fixture(); 15 | } 16 | 17 | protected abstract IConvertible CreateConvertible(); 18 | 19 | [Fact] 20 | public void Convert_Should_ReturnNull_When_ValueIsNull() 21 | { 22 | var convertible = CreateConvertible(); 23 | convertible.Convert(null).Should().Be(null); 24 | } 25 | 26 | [Fact] 27 | public void Convert_Should_ReturnValue_When_ValueIsSameType() 28 | { 29 | var convertible = CreateConvertible(); 30 | var value = Fixture.Create(); 31 | convertible.Convert(value).Should().Be(value); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/IThingProperty.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Mozilla.IoT.WebThing 4 | { 5 | /// 6 | /// Represent the thing property. 7 | /// 8 | public interface IThingProperty 9 | { 10 | /// 11 | /// Name of origin property 12 | /// 13 | string OriginPropertyName { get; } 14 | 15 | /// 16 | /// Try get value. it only can get value if property is write-only 17 | /// 18 | /// The result value 19 | /// The if could get value 20 | bool TryGetValue([MaybeNull] out object? value); 21 | 22 | /// 23 | /// Try set value 24 | /// 25 | /// Value to be set. This value should be 26 | /// The . 27 | SetPropertyResult TrySetValue(object value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/Number/SystemTexJsonNumberConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class SystemTexJsonNumberConvertible : SystemTexJsonConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static SystemTexJsonNumberConvertible Instance { get; } = new SystemTexJsonNumberConvertible(); 14 | 15 | 16 | /// 17 | protected override bool TryConvert(JsonElement source, out object? result) 18 | { 19 | if (source.ValueKind != JsonValueKind.Number || !source.TryGetDecimal(out var number)) 20 | { 21 | result = null; 22 | return false; 23 | } 24 | 25 | result = number; 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Number/NewtonsoftNumberConvertible.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Number 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class NewtonsoftNumberConvertible : NewtonsoftConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static NewtonsoftNumberConvertible Instance { get; } = new NewtonsoftNumberConvertible(); 14 | 15 | 16 | /// 17 | protected override bool TryConvert(JToken source, out object? result) 18 | { 19 | if (source.Type == JTokenType.Integer || source.Type == JTokenType.Float) 20 | { 21 | result = source.Value(); 22 | return true; 23 | } 24 | 25 | result = null; 26 | return false; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/Boolean/SystemTexJsonBooleanConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class SystemTexJsonBooleanConvertible : SystemTexJsonConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static SystemTexJsonBooleanConvertible Instance { get; } = new SystemTexJsonBooleanConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JsonElement source, out object? result) 17 | { 18 | if (source.ValueKind != JsonValueKind.True && source.ValueKind != JsonValueKind.False) 19 | { 20 | result = null; 21 | return false; 22 | } 23 | 24 | result = source.GetBoolean(); 25 | return true; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Converts/ActionStatusConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | using Mozilla.IoT.WebThing.Actions; 5 | 6 | namespace Mozilla.IoT.WebThing.Converts 7 | { 8 | /// 9 | /// Convert 10 | /// 11 | public class ActionStatusConverter : JsonConverter 12 | { 13 | private static readonly Type s_status = typeof(ActionStatus); 14 | 15 | /// 16 | public override bool CanConvert(Type typeToConvert) 17 | => s_status == typeToConvert; 18 | 19 | /// 20 | public override ActionStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 21 | => Enum.Parse(reader.GetString(), true); 22 | 23 | /// 24 | public override void Write(Utf8JsonWriter writer, ActionStatus value, JsonSerializerOptions options) 25 | => writer.WriteStringValue(value.ToString().ToLower()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Extensions/IThingCollectionBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Mozilla.IoT.WebThing.Extensions; 3 | using Mozilla.IoT.WebThing.Factories; 4 | using Mozilla.IoT.WebThing.Json; 5 | using Mozilla.IoT.WebThing.Newtonsoft.Convertibles; 6 | using Mozilla.IoT.WebThing.Newtonsoft.Factories; 7 | 8 | namespace Mozilla.IoT.WebThing.Newtonsoft 9 | { 10 | /// 11 | /// Extension class 12 | /// 13 | public static class IThingCollectionBuilderExtension 14 | { 15 | /// 16 | /// Add Newtonsoft 17 | /// 18 | /// 19 | /// 20 | public static IThingCollectionBuilder AddNewtonsoft(this IThingCollectionBuilder builder) 21 | { 22 | builder.ServiceCollection.AddSingleton(); 23 | builder.ServiceCollection.AddSingleton(); 24 | return builder; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Strings/NewtonsoftDateTimeOffsetConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Strings 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class NewtonsoftDateTimeOffsetConvertible : NewtonsoftConvertible 10 | { 11 | /// 12 | /// Static Instance of 13 | /// 14 | public static NewtonsoftDateTimeOffsetConvertible Instance { get; } = new NewtonsoftDateTimeOffsetConvertible(); 15 | 16 | /// 17 | protected override bool TryConvert(JToken source, out object? result) 18 | { 19 | result = null; 20 | if (source.Type != JTokenType.Date) 21 | { 22 | return false; 23 | } 24 | 25 | result = source.Value(); 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Mozilla.IoT.WebThing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(ThingAppTargetFrameworks) 4 | Library 5 | enable 6 | Mozilla.IoT.WebThing 7 | Implementation of an HTTP Mozilla Web Thing. 8 | 9 | true 10 | https://github.com/lillo42/webthing-csharp 11 | git 12 | 13 | true 14 | true 15 | snupkg 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Web/WebSockets/Serializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.WebSockets; 3 | using System.Text; 4 | using System.Text.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace Mozilla.IoT.WebThing.Integration.Test.Web.WebSockets 8 | { 9 | public class Serializer 10 | { 11 | public static ArraySegment Serialize(T value) 12 | { 13 | return new ArraySegment(System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(value, new JsonSerializerOptions 14 | { 15 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase, 16 | DictionaryKeyPolicy = JsonNamingPolicy.CamelCase 17 | })); 18 | } 19 | 20 | public static JToken Deserialize(ArraySegment arraySegment, WebSocketReceiveResult socketReceiveResult) 21 | { 22 | var array = new byte[socketReceiveResult.Count]; 23 | 24 | Array.Copy(arraySegment.Array, 0, array, 0, array.Length); 25 | 26 | return JToken.Parse(Encoding.UTF8.GetString(array)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Property/Boolean/BoolProperty.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json; 3 | using AutoFixture; 4 | 5 | namespace Mozilla.IoT.WebThing.Integration.Test.Property.Boolean 6 | { 7 | public class BoolProperty : AbstractStructPropertyTest 8 | { 9 | protected override JsonElement CreateJson(bool value) 10 | { 11 | return JsonSerializer.Deserialize($@"{{ ""input"": {value.ToString().ToLower()} }}") 12 | .GetProperty("input"); 13 | } 14 | 15 | protected override JsonElement[] CreateInvalidJson() 16 | { 17 | var result = new List 18 | { 19 | JsonSerializer.Deserialize($@"{{ ""input"": ""{Fixture.Create()}"" }}") 20 | .GetProperty("input"), 21 | JsonSerializer.Deserialize($@"{{ ""input"": {Fixture.Create()} }}") 22 | .GetProperty("input") 23 | }; 24 | 25 | return result.ToArray(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Mozilla.IoT.WebThing.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ThingAppTargetFrameworks) 5 | false 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Events/EventAddedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Events 4 | { 5 | /// 6 | /// The when event had added. 7 | /// 8 | public class EventAddedEventArgs : EventArgs 9 | { 10 | /// 11 | /// Initialize a new instance of . 12 | /// 13 | /// The event name 14 | /// The . 15 | public EventAddedEventArgs(string eventName, Event @event) 16 | { 17 | EventName = eventName ?? throw new ArgumentNullException(nameof(eventName)); 18 | Event = @event ?? throw new ArgumentNullException(nameof(@event)); 19 | } 20 | 21 | /// 22 | /// The event name 23 | /// 24 | public string EventName { get; } 25 | 26 | /// 27 | /// The 28 | /// 29 | public Event Event { get; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Endpoints/GetAllThings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Net; 4 | using System.Text.Json; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | using Mozilla.IoT.WebThing.Extensions; 10 | using Mozilla.IoT.WebThing.Middlewares; 11 | 12 | namespace Mozilla.IoT.WebThing.Endpoints 13 | { 14 | internal class GetAllThings 15 | { 16 | internal static async Task InvokeAsync(HttpContext context) 17 | { 18 | var service = context.RequestServices; 19 | var logger = service.GetRequiredService>(); 20 | var things = service.GetRequiredService>(); 21 | 22 | logger.LogInformation("Request all things."); 23 | await context.WriteBodyAsync(HttpStatusCode.OK, things.Select(thing => 24 | { 25 | ThingAdapter.Adapt(context, thing); 26 | return thing.ThingContext.Response; 27 | })).ConfigureAwait(false); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Strings/NewtonsoftCharConvertible.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Strings 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class NewtonsoftCharConvertible : NewtonsoftConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static NewtonsoftCharConvertible Instance { get; } = new NewtonsoftCharConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JToken source, out object? result) 17 | { 18 | result = null; 19 | if (source.Type != JTokenType.String) 20 | { 21 | return false; 22 | } 23 | 24 | var value = source.Value(); 25 | if (value.Length != 1) 26 | { 27 | return false; 28 | } 29 | 30 | result = value[0]; 31 | return true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Strings/NewtonsoftGuidConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Strings 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class NewtonsoftGuidConvertible : NewtonsoftConvertible 10 | { 11 | /// 12 | /// Static Instance of 13 | /// 14 | public static NewtonsoftGuidConvertible Instance { get; } = new NewtonsoftGuidConvertible(); 15 | 16 | /// 17 | protected override bool TryConvert(JToken source, out object? result) 18 | { 19 | result = null; 20 | if (source.Type != JTokenType.String) 21 | { 22 | return false; 23 | } 24 | 25 | if (!Guid.TryParse(source.Value(), out var value)) 26 | { 27 | return false; 28 | } 29 | 30 | result = value; 31 | return true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/String/SystemTexJsonGuidConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles.String 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class SystemTexJsonGuidConvertible : SystemTexJsonConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static SystemTexJsonGuidConvertible Instance { get; } = new SystemTexJsonGuidConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JsonElement source, out object? result) 17 | { 18 | result = null; 19 | 20 | if (source.ValueKind != JsonValueKind.String) 21 | { 22 | return false; 23 | } 24 | 25 | if (!source.TryGetGuid(out var value)) 26 | { 27 | return false; 28 | } 29 | 30 | result = value; 31 | return true; 32 | 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Strings/NewtonsoftTimeSpanConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Strings 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class NewtonsoftTimeSpanConvertible : NewtonsoftConvertible 10 | { 11 | /// 12 | /// Static Instance of 13 | /// 14 | public static NewtonsoftTimeSpanConvertible Instance { get; } = new NewtonsoftTimeSpanConvertible(); 15 | 16 | /// 17 | protected override bool TryConvert(JToken source, out object? result) 18 | { 19 | result = null; 20 | if (source.Type != JTokenType.String) 21 | { 22 | return false; 23 | } 24 | 25 | if (!TimeSpan.TryParse(source.Value(), out var value)) 26 | { 27 | return false; 28 | } 29 | 30 | result = value; 31 | return true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/String/SystemTexJsonDateTimeConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles.String 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class SystemTexJsonDateTimeConvertible : SystemTexJsonConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static SystemTexJsonDateTimeConvertible Instance { get; } = new SystemTexJsonDateTimeConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JsonElement source, out object? result) 17 | { 18 | result = null; 19 | 20 | if (source.ValueKind != JsonValueKind.String) 21 | { 22 | return false; 23 | } 24 | 25 | if (!source.TryGetDateTime(out var value)) 26 | { 27 | return false; 28 | } 29 | 30 | result = value; 31 | return true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Exceptions/ThingException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Exceptions 4 | { 5 | /// 6 | /// Base . 7 | /// 8 | public class ThingException : Exception 9 | { 10 | /// 11 | /// Initialize a new instance of . 12 | /// 13 | public ThingException() 14 | { 15 | } 16 | 17 | /// 18 | /// Initialize a new instance of . 19 | /// 20 | /// The error message. 21 | public ThingException(string? message) 22 | : base(message) 23 | { 24 | } 25 | 26 | /// 27 | /// Initialize a new instance of . 28 | /// 29 | /// The error message. 30 | /// The inner . 31 | public ThingException(string? message, Exception? innerException) 32 | : base(message, innerException) 33 | { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/String/SystemTexJsonCharConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles.String 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class SystemTexJsonCharConvertible : SystemTexJsonConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static SystemTexJsonCharConvertible Instance { get; } = new SystemTexJsonCharConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JsonElement source, out object? result) 17 | { 18 | if (source.ValueKind != JsonValueKind.String) 19 | { 20 | result = null; 21 | return false; 22 | } 23 | 24 | var value = source.GetString(); 25 | 26 | if (value.Length != 1) 27 | { 28 | result = null; 29 | return false; 30 | } 31 | 32 | result = value[0]; 33 | return true; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/String/SystemTexJsonTimeSpanConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.Convertibles.String 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class SystemTexJsonTimeSpanConvertible : SystemTexJsonConvertible 10 | { 11 | /// 12 | /// Static Instance of 13 | /// 14 | public static SystemTexJsonTimeSpanConvertible Instance { get; } = new SystemTexJsonTimeSpanConvertible(); 15 | 16 | /// 17 | protected override bool TryConvert(JsonElement source, out object? result) 18 | { 19 | result = null; 20 | 21 | if (source.ValueKind != JsonValueKind.String) 22 | { 23 | return false; 24 | } 25 | 26 | if (!TimeSpan.TryParse(source.GetString(), out var value)) 27 | { 28 | return false; 29 | } 30 | 31 | result = value; 32 | return true; 33 | 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/String/CharJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.String 4 | { 5 | /// 6 | /// Represent json schema validation. 7 | /// 8 | public class ChardJsonSchemaValidation : IJsonSchemaValidation 9 | { 10 | private readonly bool _isNullable; 11 | private readonly HashSet? _enums; 12 | 13 | /// 14 | /// Initialize a new instance of . 15 | /// 16 | /// Accepted null value.= 17 | /// The accepted values. 18 | public ChardJsonSchemaValidation(bool isNullable, HashSet? enums) 19 | { 20 | _isNullable = isNullable; 21 | _enums = enums; 22 | } 23 | 24 | /// 25 | public bool IsValid(object? value) 26 | => value switch 27 | { 28 | null => _isNullable, 29 | char obj => _enums == null || _enums.Contains(obj), 30 | _ => false 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/String/SystemTexJsonDateTimeOffsetConvertible.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.Convertibles.String 4 | { 5 | /// 6 | /// Represent convertible/getter from . 7 | /// 8 | public class SystemTexJsonDateTimeOffsetConvertible : SystemTexJsonConvertible 9 | { 10 | /// 11 | /// Static Instance of 12 | /// 13 | public static SystemTexJsonDateTimeOffsetConvertible Instance { get; } = new SystemTexJsonDateTimeOffsetConvertible(); 14 | 15 | /// 16 | protected override bool TryConvert(JsonElement source, out object? result) 17 | { 18 | result = null; 19 | 20 | if (source.ValueKind != JsonValueKind.String) 21 | { 22 | return false; 23 | } 24 | 25 | if (!source.TryGetDateTimeOffset(out var value)) 26 | { 27 | return false; 28 | } 29 | 30 | result = value; 31 | return true; 32 | 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/String/GuidJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.String 5 | { 6 | /// 7 | /// Represent json schema validation. 8 | /// 9 | public class GuidJsonSchemaValidation : IJsonSchemaValidation 10 | { 11 | private readonly bool _isNullable; 12 | private readonly HashSet? _enums; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// Accepted null value.= 18 | /// The accepted values. 19 | public GuidJsonSchemaValidation(bool isNullable, HashSet? enums) 20 | { 21 | _isNullable = isNullable; 22 | _enums = enums; 23 | } 24 | 25 | /// 26 | public bool IsValid(object? value) 27 | => value switch 28 | { 29 | null => _isNullable, 30 | Guid obj => _enums == null || _enums.Contains(obj), 31 | _ => false 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Extensions/IThingCollectionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Mozilla.IoT.WebThing.Extensions 4 | { 5 | /// 6 | /// The builder. 7 | /// 8 | public interface IThingCollectionBuilder 9 | { 10 | /// 11 | /// The . 12 | /// 13 | IServiceCollection ServiceCollection { get; } 14 | 15 | /// 16 | /// Add thing. 17 | /// 18 | /// The 19 | /// Current . 20 | IThingCollectionBuilder AddThing() 21 | where T : Thing; 22 | 23 | 24 | /// 25 | /// Add thing instance. 26 | /// 27 | /// The instance of . 28 | /// The 29 | /// Current . 30 | IThingCollectionBuilder AddThing(T thing) 31 | where T : Thing; 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Mozilla.IoT.WebThing.Integration.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ThingAppTargetFrameworks) 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/String/TimeSpanJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.String 5 | { 6 | /// 7 | /// Represent json schema validation. 8 | /// 9 | public class TimeSpanJsonSchemaValidation : IJsonSchemaValidation 10 | { 11 | private readonly bool _isNullable; 12 | private readonly HashSet? _enums; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// Accepted null value.= 18 | /// The accepted values. 19 | public TimeSpanJsonSchemaValidation(bool isNullable, HashSet? enums) 20 | { 21 | _isNullable = isNullable; 22 | _enums = enums; 23 | } 24 | 25 | /// 26 | public bool IsValid(object? value) 27 | => value switch 28 | { 29 | null => _isNullable, 30 | TimeSpan obj => _enums == null || _enums.Contains(obj), 31 | _ => false 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/String/DateTimeJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.String 5 | { 6 | /// 7 | /// Represent json schema validation. 8 | /// 9 | public class DateTimeJsonSchemaValidation : IJsonSchemaValidation 10 | { 11 | private readonly bool _isNullable; 12 | private readonly HashSet? _enums; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// Accepted null value.= 18 | /// The accepted values. 19 | public DateTimeJsonSchemaValidation(bool isNullable, HashSet? enums) 20 | { 21 | _isNullable = isNullable; 22 | _enums = enums; 23 | } 24 | 25 | /// 26 | public bool IsValid(object? value) 27 | => value switch 28 | { 29 | null => _isNullable, 30 | DateTime obj => _enums == null || _enums.Contains(obj), 31 | _ => false 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/SystemTexJsonConvertible.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Text.Json; 4 | 5 | namespace Mozilla.IoT.WebThing.Json.Convertibles 6 | { 7 | /// 8 | /// Represent convertible/getter value from . 9 | /// 10 | public abstract class SystemTexJsonConvertible : IJsonConvertible 11 | { 12 | 13 | /// 14 | public bool TryConvert(object source, [MaybeNull]out object? result) 15 | { 16 | result = null; 17 | if (source == null) 18 | { 19 | return true; 20 | } 21 | 22 | var jsonElement = (JsonElement)source; 23 | return jsonElement.ValueKind == JsonValueKind.Null || TryConvert(jsonElement, out result); 24 | } 25 | 26 | 27 | /// 28 | /// Try convert json value to specific value. 29 | /// 30 | /// The . 31 | /// The result. 32 | /// return true if could get value, otherwise return false. 33 | protected abstract bool TryConvert(JsonElement source, out object? result); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/NewtonsoftConvertible.cs: -------------------------------------------------------------------------------- 1 | using Mozilla.IoT.WebThing.Json.Convertibles; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles 5 | { 6 | /// 7 | /// Represent convertible/getter value from . 8 | /// 9 | public abstract class NewtonsoftConvertible : IJsonConvertible 10 | { 11 | /// 12 | public bool TryConvert(object source, out object? result) 13 | { 14 | result = null; 15 | if (source == null) 16 | { 17 | return true; 18 | } 19 | 20 | var token = (JToken)source; 21 | return token.Type == JTokenType.Null 22 | || token.Type == JTokenType.Undefined 23 | || TryConvert(source, out result); 24 | } 25 | 26 | /// 27 | /// Try convert json value to specific value. 28 | /// 29 | /// The . 30 | /// The result. 31 | /// return true if could get value, otherwise return false. 32 | protected abstract bool TryConvert(JToken source, out object? result); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Mozilla.IoT.WebThing.Newtonsoft.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ThingAppTargetFrameworks) 5 | Library 6 | enable 7 | Mozilla.IoT.WebThing.Newtonsof 8 | Add support to Newtonsoft. 9 | 10 | true 11 | https://github.com/lillo42/webthing-csharp 12 | git 13 | 14 | true 15 | true 16 | snupkg 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | <_ContentIncludedByDefault Remove="Properties\launchSettings.json" /> 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Action/Boolean/BooleanAction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json; 3 | using AutoFixture; 4 | 5 | namespace Mozilla.IoT.WebThing.Integration.Test.Action.Boolean 6 | { 7 | public class BooleanAction : AbstractStructActionTest 8 | { 9 | protected override JsonElement CreateJson(bool value) 10 | { 11 | return JsonSerializer.Deserialize( 12 | $@"{{ ""action"": {{ ""input"": {{ ""value"": {value.ToString().ToLower()} }} }} }}").GetProperty("action"); 13 | } 14 | 15 | protected override IEnumerable CreateInvalidJson() 16 | { 17 | yield return JsonSerializer.Deserialize( 18 | $@"{{ ""action"": {{ ""input"": {{ ""value"": ""{Fixture.Create()}"" }} }} }}").GetProperty("action"); 19 | yield return JsonSerializer.Deserialize( 20 | $@"{{ ""action"": {{ ""input"": {{ ""value"": {Fixture.Create()} }} }} }}").GetProperty("action"); 21 | 22 | yield return JsonSerializer.Deserialize( 23 | $@"{{ ""action"": {{ ""input"": {{ ""value"": [ {Fixture.Create().ToString().ToLower()} ] }} }} }}").GetProperty("action"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/String/DateTimeOffsetJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.String 5 | { 6 | /// 7 | /// Represent json schema validation. 8 | /// 9 | public class DateTimeOffsetJsonSchemaValidation : IJsonSchemaValidation 10 | { 11 | private readonly bool _isNullable; 12 | private readonly HashSet? _enums; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// Accepted null value.= 18 | /// The accepted values. 19 | public DateTimeOffsetJsonSchemaValidation(bool isNullable, HashSet? enums) 20 | { 21 | _isNullable = isNullable; 22 | _enums = enums; 23 | } 24 | 25 | /// 26 | public bool IsValid(object? value) 27 | => value switch 28 | { 29 | null => _isNullable, 30 | DateTimeOffset obj => _enums == null || _enums.Contains(obj), 31 | _ => false 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Strings/NewtonsoftEnumConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Strings 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class NewtonsoftEnumConvertible : NewtonsoftConvertible 10 | { 11 | private readonly Type _enum; 12 | 13 | /// 14 | /// Initialize a new instance of . 15 | /// 16 | /// The enum type 17 | public NewtonsoftEnumConvertible(Type @enum) 18 | { 19 | _enum = @enum ?? throw new ArgumentNullException(nameof(@enum)); 20 | } 21 | 22 | /// 23 | protected override bool TryConvert(JToken source, out object? result) 24 | { 25 | result = null; 26 | if (source.Type != JTokenType.String) 27 | { 28 | return false; 29 | } 30 | 31 | if (!Enum.TryParse(_enum, source.Value(), out var value)) 32 | { 33 | return false; 34 | } 35 | 36 | result = value; 37 | return true; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/WebSockets/IWebSocketAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Mozilla.IoT.WebThing.WebSockets 6 | { 7 | /// 8 | /// Web socket action. 9 | /// 10 | public interface IWebSocketAction 11 | { 12 | /// 13 | /// The Action name. This value should be unique. 14 | /// 15 | string Action { get; } 16 | 17 | /// 18 | /// Execute this action when web socket request action where action name match with 19 | /// 20 | /// The origin of this action. 21 | /// The associated with action. 22 | /// The request with this action. 23 | /// The for this action. Every request is generate new scope. 24 | /// The . 25 | /// 26 | ValueTask ExecuteAsync(System.Net.WebSockets.WebSocket socket, Thing thing, object data, 27 | IServiceProvider provider, CancellationToken cancellationToken); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Property/String/EnumProperty.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AutoFixture; 3 | using System.Text.Json; 4 | 5 | namespace Mozilla.IoT.WebThing.Integration.Test.Property.String 6 | { 7 | public class EnumProperty : AbstractStructPropertyTest 8 | { 9 | protected override JsonElement CreateJson(Foo value) 10 | { 11 | return JsonSerializer.Deserialize($@"{{ ""input"": ""{value}"" }}") 12 | .GetProperty("input"); 13 | } 14 | 15 | protected override JsonElement[] CreateInvalidJson() 16 | { 17 | var result = new List 18 | { 19 | JsonSerializer.Deserialize($@"{{ ""input"": ""{Fixture.Create()}"" }}") 20 | .GetProperty("input"), 21 | JsonSerializer.Deserialize($@"{{ ""input"": {Fixture.Create().ToString().ToLower()} }}") 22 | .GetProperty("input"), 23 | JsonSerializer.Deserialize($@"{{ ""input"": {Fixture.Create()} }}") 24 | .GetProperty("input") 25 | }; 26 | 27 | return result.ToArray(); 28 | } 29 | } 30 | 31 | public enum Foo 32 | { 33 | A, 34 | B, 35 | C 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/String/SystemTexJsonEnumConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.Convertibles.String 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class SystemTexJsonEnumConvertible : SystemTexJsonConvertible 10 | { 11 | private readonly Type _enum; 12 | 13 | /// 14 | /// Initialize a new instance of . 15 | /// 16 | /// The enum type 17 | public SystemTexJsonEnumConvertible(Type @enum) 18 | { 19 | _enum = @enum ?? throw new ArgumentNullException(nameof(@enum)); 20 | } 21 | 22 | /// 23 | protected override bool TryConvert(JsonElement source, out object? result) 24 | { 25 | result = null; 26 | 27 | if (source.ValueKind != JsonValueKind.String) 28 | { 29 | return false; 30 | } 31 | 32 | if (!Enum.TryParse(_enum, source.GetString(), out var value)) 33 | { 34 | return false; 35 | } 36 | 37 | result = value; 38 | return true; 39 | 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/IJsonConvert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Json 5 | { 6 | /// 7 | /// Read json value from buffer. 8 | /// 9 | public interface IJsonConvert 10 | { 11 | /// 12 | /// Retrieve the value. 13 | /// 14 | /// The Type to be deserialize. 15 | /// The to be convert. 16 | /// 17 | T Deserialize(ReadOnlySpan values); 18 | 19 | /// 20 | /// Convert to 21 | /// 22 | /// The value to be convert. 23 | /// The Type to be serialize. 24 | /// The . 25 | byte[] Serialize(T value); 26 | 27 | /// 28 | /// Convert object to 29 | /// 30 | /// The to be converted 31 | /// Returns of 32 | IEnumerable> ToEnumerable(object data); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Collection/ListConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Convertibles.Collection 5 | { 6 | /// 7 | /// Convert value to 8 | /// 9 | public class ListConvertible : IConvertible 10 | { 11 | private readonly IConvertible _convertible; 12 | 13 | /// 14 | /// Initialize a new instance of . 15 | /// 16 | /// The . 17 | public ListConvertible(IConvertible convertible) 18 | { 19 | _convertible = convertible; 20 | } 21 | 22 | /// 23 | public object? Convert(object? value) 24 | { 25 | if (value == null) 26 | { 27 | return null; 28 | } 29 | 30 | if (value is object[] array) 31 | { 32 | var result = new List(); 33 | 34 | foreach (var item in array) 35 | { 36 | result.Add((T)_convertible.Convert(item)!); 37 | } 38 | 39 | return result; 40 | } 41 | 42 | throw new ArgumentException("Invalid value", nameof(value)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Collection/HashSetConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Convertibles.Collection 5 | { 6 | /// 7 | /// Convert value to 8 | /// 9 | public class HashSetConvertible : IConvertible 10 | { 11 | private readonly IConvertible _convertible; 12 | 13 | /// 14 | /// Initialize a new instance of . 15 | /// 16 | /// The . 17 | public HashSetConvertible(IConvertible convertible) 18 | { 19 | _convertible = convertible; 20 | } 21 | 22 | /// 23 | public object? Convert(object? value) 24 | { 25 | if (value == null) 26 | { 27 | return null; 28 | } 29 | 30 | if (value is object[] array) 31 | { 32 | var result = new HashSet(); 33 | 34 | foreach (var item in array) 35 | { 36 | result.Add((T)_convertible.Convert(item)!); 37 | } 38 | 39 | return result; 40 | } 41 | 42 | throw new ArgumentException("Invalid value", nameof(value)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Collection/ArrayConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mozilla.IoT.WebThing.Convertibles.Collection 4 | { 5 | /// 6 | /// Convert value to array of T 7 | /// 8 | /// The value. 9 | public class ArrayConvertible : IConvertible 10 | { 11 | private readonly IConvertible _convertible; 12 | 13 | /// 14 | /// Initialize a new instance of . 15 | /// 16 | /// The . 17 | public ArrayConvertible(IConvertible convertible) 18 | { 19 | _convertible = convertible; 20 | } 21 | 22 | /// 23 | public object? Convert(object? value) 24 | { 25 | if (value == null) 26 | { 27 | return null; 28 | } 29 | 30 | if (value is object[] array) 31 | { 32 | var result = new T[array.Length]; 33 | 34 | for (var i = 0; i < array.Length; i++) 35 | { 36 | result[i] = (T)_convertible.Convert(array[i])!; 37 | } 38 | 39 | return result; 40 | } 41 | 42 | throw new ArgumentException("Invalid value", nameof(value)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Action/String/EnumActionTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json; 3 | using AutoFixture; 4 | 5 | namespace Mozilla.IoT.WebThing.Integration.Test.Action.String 6 | { 7 | public class EnumActionTest : AbstractStructActionTest 8 | { 9 | protected override JsonElement CreateJson(String.Foo value) 10 | { 11 | return JsonSerializer.Deserialize($@"{{ ""action"": {{ ""input"": {{ ""value"": ""{value}"" }} }} }}") 12 | .GetProperty("action"); 13 | } 14 | 15 | protected override IEnumerable CreateInvalidJson() 16 | { 17 | yield return JsonSerializer.Deserialize($@"{{ ""action"": {{ ""input"": {{ ""value"": ""{Fixture.Create()}"" }} }} }}") 18 | .GetProperty("action"); 19 | 20 | yield return JsonSerializer 21 | .Deserialize($@"{{ ""action"": {{ ""input"": {{ ""value"": {Fixture.Create().ToString().ToLower()} }} }} }}") 22 | .GetProperty("action"); 23 | 24 | yield return JsonSerializer.Deserialize($@"{{ ""action"": {{ ""input"": {{ ""value"": {Fixture.Create()} }} }} }}") 25 | .GetProperty("action"); 26 | } 27 | } 28 | 29 | public enum Foo 30 | { 31 | A, 32 | B, 33 | C 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Builders/IPropertyBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using Mozilla.IoT.WebThing.Extensions; 4 | 5 | namespace Mozilla.IoT.WebThing.Builders 6 | { 7 | /// 8 | /// Create property. 9 | /// 10 | public interface IPropertyBuilder 11 | { 12 | /// 13 | /// Set . 14 | /// 15 | /// The to be set. 16 | /// 17 | IPropertyBuilder SetThing(Thing thing); 18 | 19 | /// 20 | /// Set 21 | /// 22 | /// The to be set. 23 | /// 24 | IPropertyBuilder SetThingOption(ThingOption option); 25 | 26 | /// 27 | /// Add property. 28 | /// 29 | /// The property. 30 | /// The about property 31 | void Add(PropertyInfo property, JsonSchema jsonSchema); 32 | 33 | /// 34 | /// Build the 35 | /// 36 | /// New of the 37 | Dictionary Build(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/Collection/LinkedListConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Convertibles.Collection 5 | { 6 | /// 7 | /// Convert value to 8 | /// 9 | /// The value. 10 | public class LinkedListConvertible : IConvertible 11 | { 12 | private readonly IConvertible _convertible; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// The . 18 | public LinkedListConvertible(IConvertible convertible) 19 | { 20 | _convertible = convertible; 21 | } 22 | 23 | /// 24 | public object? Convert(object? value) 25 | { 26 | if (value == null) 27 | { 28 | return null; 29 | } 30 | 31 | if (value is object[] array) 32 | { 33 | var result = new LinkedList(); 34 | 35 | foreach (var item in array) 36 | { 37 | result.AddLast((T)_convertible.Convert(item)!); 38 | } 39 | 40 | return result; 41 | } 42 | 43 | throw new ArgumentException("Invalid value", nameof(value)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Convertibles/InputConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Mozilla.IoT.WebThing.Convertibles 6 | { 7 | /// 8 | /// Convert data for input 9 | /// 10 | public class InputConvertible : IConvertible 11 | { 12 | private readonly Dictionary _convertibles; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// The convertibles 18 | public InputConvertible(Dictionary convertibles) 19 | { 20 | _convertibles = convertibles ?? throw new ArgumentNullException(nameof(convertibles)); 21 | } 22 | 23 | /// 24 | public object? Convert(object? value) 25 | { 26 | if (_convertibles == null) 27 | { 28 | return value; 29 | } 30 | 31 | var dic = (Dictionary)value!; 32 | 33 | foreach (var (propertyName, propertyValue) in dic.ToArray()) 34 | { 35 | if (_convertibles.TryGetValue(propertyName, out var convertible) && convertible != null) 36 | { 37 | dic[propertyName] = convertible.Convert(propertyValue); 38 | } 39 | } 40 | 41 | return dic; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Events/EventCollectionTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AutoFixture; 3 | using FluentAssertions; 4 | using Mozilla.IoT.WebThing.Events; 5 | using NSubstitute; 6 | using Xunit; 7 | 8 | namespace Mozilla.IoT.WebThing.Test 9 | { 10 | public class EventCollectionTest 11 | { 12 | private readonly Fixture _fixture; 13 | 14 | public EventCollectionTest() 15 | { 16 | _fixture = new Fixture(); 17 | } 18 | 19 | 20 | [Theory] 21 | [InlineData(10)] 22 | [InlineData(100)] 23 | public void MaxSize(int size) 24 | { 25 | var collection = new EventCollection(size); 26 | var data = new LinkedList(); 27 | 28 | for (var i = 0; i < size; i++) 29 | { 30 | var @event = new Event(_fixture.Create()); 31 | data.AddLast(@event); 32 | collection.Enqueue(@event, "", Substitute.For()); 33 | } 34 | 35 | collection.ToArray().Length.Should().Be(size); 36 | collection.ToArray().Should().BeEquivalentTo(data); 37 | 38 | var event2 = new Event(_fixture.Create()); 39 | data.AddLast(@event2); 40 | data.RemoveFirst(); 41 | collection.Enqueue(@event2, "", Substitute.For()); 42 | 43 | collection.ToArray().Length.Should().Be(size); 44 | collection.ToArray().Should().BeEquivalentTo(data); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Property/AbstractPropertyTest.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using AutoFixture; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Mozilla.IoT.WebThing.Extensions; 5 | using Mozilla.IoT.WebThing.Factories; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace Mozilla.IoT.WebThing.Integration.Test.Property 9 | { 10 | public abstract class AbstractPropertyTest 11 | { 12 | protected IThingContextFactory Factory { get; } 13 | protected Fixture Fixture { get; } 14 | 15 | protected AbstractPropertyTest() 16 | { 17 | var collection = new ServiceCollection(); 18 | collection.AddThings(); 19 | var provider = collection.BuildServiceProvider(); 20 | Factory = provider.GetRequiredService(); 21 | Fixture = new Fixture(); 22 | } 23 | 24 | protected abstract JsonElement CreateJson(T value); 25 | protected abstract JsonElement[] CreateInvalidJson(); 26 | 27 | protected void TestResponseProperty(string response) 28 | where TThing : Thing, new() 29 | { 30 | var thing = new TThing(); 31 | var context = Factory.Create(thing, new ThingOption()); 32 | 33 | var message = JsonSerializer.Serialize(context.Response, 34 | new ThingOption().ToJsonSerializerOptions()); 35 | 36 | FluentAssertions.Json.JsonAssertionExtensions.Should(JToken.Parse(message)) 37 | .BeEquivalentTo(JToken.Parse(response)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Endpoints/GetThing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text.Json; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | using Mozilla.IoT.WebThing.Extensions; 11 | using Mozilla.IoT.WebThing.Middlewares; 12 | 13 | namespace Mozilla.IoT.WebThing.Endpoints 14 | { 15 | internal class GetThing 16 | { 17 | internal static async Task InvokeAsync(HttpContext context) 18 | { 19 | var service = context.RequestServices; 20 | var logger = service.GetRequiredService>(); 21 | var things = service.GetRequiredService>(); 22 | 23 | var name = context.GetRouteData("name"); 24 | logger.LogInformation("Requesting Thing. [Thing: {name}]", name); 25 | 26 | var thing = things.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); 27 | 28 | if (thing == null) 29 | { 30 | logger.LogInformation("Thing not found. [Thing: {name}]", name); 31 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 32 | return; 33 | } 34 | 35 | ThingAdapter.Adapt(context, thing); 36 | logger.LogInformation("Found 1 Thing. [Thing: {name}]", thing.Name); 37 | await context.WriteBodyAsync(HttpStatusCode.OK, thing.ThingContext.Response) 38 | .ConfigureAwait(false); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SystemTextJson.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json; 4 | using Mozilla.IoT.WebThing.Extensions; 5 | 6 | namespace Mozilla.IoT.WebThing.Json 7 | { 8 | /// 9 | /// Implementation of for System.Text.Json 10 | /// 11 | public class SystemTextJson : IJsonConvert 12 | { 13 | private readonly ThingOption _options; 14 | 15 | /// 16 | /// Initialize a new instance of . 17 | /// 18 | /// The . 19 | public SystemTextJson(ThingOption options) 20 | { 21 | _options = options ?? throw new ArgumentNullException(nameof(options)); 22 | } 23 | 24 | /// 25 | public T Deserialize(ReadOnlySpan values) 26 | { 27 | return JsonSerializer.Deserialize(values, _options.ToJsonSerializerOptions()); 28 | } 29 | 30 | /// 31 | public byte[] Serialize(T value) 32 | { 33 | return JsonSerializer.SerializeToUtf8Bytes(value, _options.ToJsonSerializerOptions()); 34 | } 35 | 36 | /// 37 | public IEnumerable> ToEnumerable(object data) 38 | { 39 | if (!(data is JsonElement element)) 40 | { 41 | yield break; 42 | } 43 | 44 | foreach (var property in element.EnumerateObject()) 45 | { 46 | yield return new KeyValuePair(property.Name, property.Value); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/TestThing/Converters/DateTimeConvert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Text; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace TestThing.Converters 7 | { 8 | public class DateTimeConverter : JsonConverter 9 | { 10 | public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 11 | { 12 | if (Utf8Parser.TryParse(reader.ValueSpan, out DateTime value, out _, 'R')) 13 | { 14 | return value; 15 | } 16 | 17 | throw new FormatException(); 18 | } 19 | 20 | public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) 21 | { 22 | writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")); 23 | } 24 | } 25 | 26 | public class NullableDateTimeConverter : JsonConverter 27 | { 28 | public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 29 | { 30 | if (Utf8Parser.TryParse(reader.ValueSpan, out DateTime value, out _, 'R')) 31 | { 32 | return value; 33 | } 34 | 35 | return null; 36 | } 37 | 38 | public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options) 39 | { 40 | if (value.HasValue) 41 | { 42 | writer.WriteStringValue(value.Value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")); 43 | } 44 | else 45 | { 46 | writer.WriteNullValue(); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/Array/SystemTexJsonArrayConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.Convertibles.Array 5 | { 6 | /// 7 | /// Represent convertible/getter from . 8 | /// 9 | public class SystemTexJsonArrayConvertible : SystemTexJsonConvertible 10 | { 11 | /// 12 | /// Initialize a new instance of . 13 | /// 14 | /// The . 15 | public SystemTexJsonArrayConvertible(IJsonConvertible convertible) 16 | { 17 | _convertible = convertible ?? throw new ArgumentNullException(nameof(convertible)); 18 | } 19 | 20 | private readonly IJsonConvertible _convertible; 21 | 22 | /// 23 | protected override bool TryConvert(JsonElement source, out object? result) 24 | { 25 | if (source.ValueKind != JsonValueKind.Array) 26 | { 27 | result = null; 28 | return false; 29 | } 30 | 31 | var values = new object?[source.GetArrayLength()]; 32 | 33 | var i = 0; 34 | foreach (var array in source.EnumerateArray()) 35 | { 36 | if (!_convertible.TryConvert(array, out var value)) 37 | { 38 | result = null; 39 | return false; 40 | } 41 | 42 | values[i] = value; 43 | i++; 44 | } 45 | 46 | result = values; 47 | return true; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/NewtonsoftJsonConvert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Mozilla.IoT.WebThing.Extensions; 5 | using Mozilla.IoT.WebThing.Json; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace Mozilla.IoT.WebThing.Newtonsoft 10 | { 11 | /// 12 | /// 13 | /// 14 | public class NewtonsoftJsonConvert : IJsonConvert 15 | { 16 | private readonly JsonSerializerSettings _settings; 17 | 18 | 19 | /// 20 | /// 21 | /// 22 | /// 23 | public NewtonsoftJsonConvert(ThingOption option) 24 | { 25 | _settings = option?.ToJsonSerializerSettings() ?? throw new ArgumentNullException(nameof(option)); 26 | } 27 | 28 | /// 29 | public T Deserialize(ReadOnlySpan values) 30 | { 31 | return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(values), _settings)!; 32 | } 33 | 34 | /// 35 | public byte[] Serialize(T value) 36 | { 37 | var result = JsonConvert.SerializeObject(value, _settings); 38 | return Encoding.UTF8.GetBytes(result); 39 | } 40 | 41 | /// 42 | public IEnumerable> ToEnumerable(object data) 43 | { 44 | if (!(data is JToken token)) 45 | { 46 | yield break; 47 | } 48 | 49 | foreach (var kv in token.ToObject>()!) 50 | { 51 | yield return kv; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting.Server.Features; 4 | using Microsoft.AspNetCore.Routing; 5 | using Microsoft.AspNetCore.WebSockets; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Mozilla.IoT.WebThing.Integration.Test.Web.Things; 8 | 9 | namespace Mozilla.IoT.WebThing.Integration.Test.Web 10 | { 11 | public class Startup 12 | { 13 | public void ConfigureServices(IServiceCollection services) 14 | { 15 | 16 | services.AddThings(x => x.ServerName = "test-thing") 17 | .AddThing() 18 | .AddThing() 19 | .AddThing() 20 | .AddThing() 21 | .AddThing() 22 | .AddThing(); 23 | 24 | services.AddWebSockets(opt => 25 | { 26 | opt.KeepAliveInterval = TimeSpan.FromSeconds(2); 27 | }); 28 | 29 | services.AddHostedService(); 30 | } 31 | 32 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 33 | public void Configure(IApplicationBuilder app) 34 | { 35 | var address = new ServerAddressesFeature(); 36 | address.Addresses.Add("local:9000"); 37 | app.ServerFeatures.Set(address); 38 | 39 | app.UseRouting(); 40 | 41 | app.UseWebSockets(); 42 | 43 | app.UseEndpoints(endpoints => 44 | { 45 | endpoints.MapThings(); 46 | }); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Array/NewtonsoftArrayConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mozilla.IoT.WebThing.Json.Convertibles; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Array 6 | { 7 | /// 8 | /// Represent convertible/getter object[] from . 9 | /// 10 | public class NewtonsoftArrayConvertible : NewtonsoftConvertible 11 | { 12 | private readonly IJsonConvertible _convertible; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// The . 18 | public NewtonsoftArrayConvertible(IJsonConvertible convertible) 19 | { 20 | _convertible = convertible ?? throw new ArgumentNullException(nameof(convertible)); 21 | } 22 | 23 | /// 24 | protected override bool TryConvert(JToken source, out object? result) 25 | { 26 | if (source.Type == JTokenType.Array) 27 | { 28 | result = null; 29 | return false; 30 | } 31 | 32 | var array = source.Value(); 33 | var values = new object?[array.Count]; 34 | 35 | var i = 0; 36 | foreach (var token in array) 37 | { 38 | if (!_convertible.TryConvert(token, out var value)) 39 | { 40 | result = null; 41 | return false; 42 | } 43 | 44 | values[i] = value; 45 | i++; 46 | } 47 | 48 | result = values; 49 | return true; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Builders/IEventBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Mozilla.IoT.WebThing.Attributes; 5 | using Mozilla.IoT.WebThing.Events; 6 | using Mozilla.IoT.WebThing.Extensions; 7 | 8 | namespace Mozilla.IoT.WebThing.Builders 9 | { 10 | /// 11 | /// Create event bind. 12 | /// 13 | public interface IEventBuilder 14 | { 15 | /// 16 | /// Set . 17 | /// 18 | /// The to be set. 19 | /// 20 | IEventBuilder SetThing(Thing thing); 21 | 22 | /// 23 | /// Set type. 24 | /// 25 | /// The typeto be set. 26 | /// 27 | IEventBuilder SetThingType(Type thingType); 28 | 29 | /// 30 | /// Set 31 | /// 32 | /// The to be set. 33 | /// 34 | IEventBuilder SetThingOption(ThingOption option); 35 | 36 | /// 37 | /// Add event. 38 | /// 39 | /// The event. 40 | /// Extra information about event 41 | void Add(EventInfo @event, ThingEventAttribute? eventInfo); 42 | 43 | /// 44 | /// Build the 45 | /// 46 | /// New of the 47 | Dictionary Build(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Endpoints/GetActions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Mozilla.IoT.WebThing.Endpoints 11 | { 12 | internal class GetActions 13 | { 14 | public static async Task InvokeAsync(HttpContext context) 15 | { 16 | var service = context.RequestServices; 17 | var logger = service.GetRequiredService>(); 18 | var things = service.GetRequiredService>(); 19 | var thingName = context.GetRouteData("name"); 20 | 21 | logger.LogInformation("Requesting Get all Actions for Thing. [Thing: {name}]", thingName); 22 | var thing = things.FirstOrDefault(x => x.Name.Equals(thingName, StringComparison.OrdinalIgnoreCase)); 23 | 24 | if (thing == null) 25 | { 26 | logger.LogInformation("Thing not found. [Thing: {name}]", thingName); 27 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 28 | return; 29 | } 30 | 31 | var result = new LinkedList(); 32 | 33 | foreach (var (key, actionCollection) in thing.ThingContext.Actions) 34 | { 35 | foreach (var value in actionCollection) 36 | { 37 | result.AddLast(new Dictionary {[key] = value}); 38 | } 39 | } 40 | 41 | logger.LogInformation("Found {counter} Actions. [Thing: {name}]", result.Count, thingName); 42 | await context.WriteBodyAsync(HttpStatusCode.OK, result) 43 | .ConfigureAwait(false); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Endpoints/GetProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Mozilla.IoT.WebThing.Endpoints 11 | { 12 | internal class GetProperties 13 | { 14 | public static async Task InvokeAsync(HttpContext context) 15 | { 16 | var service = context.RequestServices; 17 | var logger = service.GetRequiredService>(); 18 | var things = service.GetRequiredService>(); 19 | 20 | var name = context.GetRouteData("name"); 21 | 22 | logger.LogInformation("Requesting get all properties. [Thing: {name}]", name); 23 | var thing = things.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); 24 | 25 | if (thing == null) 26 | { 27 | logger.LogInformation("Thing not found. [Thing: {name}]", name); 28 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 29 | return; 30 | } 31 | 32 | logger.LogInformation("Going to get all properties [Thing: {name}]", name); 33 | 34 | var properties = new Dictionary(); 35 | 36 | foreach (var (propertyName, property) in thing.ThingContext.Properties) 37 | { 38 | if (property.TryGetValue(out var value)) 39 | { 40 | properties.Add(propertyName, value); 41 | } 42 | } 43 | 44 | await context.WriteBodyAsync(HttpStatusCode.OK, properties) 45 | .ConfigureAwait(false); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Attributes/ThingActionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mozilla.IoT.WebThing.Json; 3 | 4 | namespace Mozilla.IoT.WebThing.Attributes 5 | { 6 | /// 7 | /// Action information. 8 | /// 9 | [AttributeUsage(AttributeTargets.Method)] 10 | public class ThingActionAttribute : Attribute, IJsonSchema 11 | { 12 | /// 13 | /// If action should be ignore. 14 | /// 15 | public bool Ignore { get; set; } 16 | 17 | /// 18 | /// Custom action name. 19 | /// 20 | public string? Name { get; set; } 21 | 22 | /// 23 | /// Action title. 24 | /// 25 | public string? Title { get; set; } 26 | 27 | /// 28 | /// Action description. 29 | /// 30 | public string? Description { get; set; } 31 | 32 | /// 33 | /// Action types 34 | /// 35 | public string[]? Type { get; set; } 36 | 37 | string? IJsonSchema.Unit => null; 38 | 39 | bool? IJsonSchema.IsReadOnly => null; 40 | 41 | bool? IJsonSchema.IsWriteOnly => null; 42 | 43 | bool? IJsonSchema.IsNullable => null; 44 | 45 | object[]? IJsonSchema.Enum => null; 46 | 47 | decimal? IJsonSchema.Minimum => null; 48 | 49 | decimal? IJsonSchema.Maximum => null; 50 | 51 | decimal? IJsonSchema.ExclusiveMinimum => null; 52 | 53 | decimal? IJsonSchema.ExclusiveMaximum => null; 54 | 55 | decimal? IJsonSchema.MultipleOf => null; 56 | 57 | int? IJsonSchema.MinimumLength => null; 58 | 59 | int? IJsonSchema.MaximumLength => null; 60 | 61 | string? IJsonSchema.Pattern => null; 62 | 63 | int? IJsonSchema.MinimumItems => null; 64 | 65 | int? IJsonSchema.MaximumItems => null; 66 | 67 | bool? IJsonSchema.UniqueItems => null; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/mDNS/MDnsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using FluentAssertions; 5 | using Makaretu.Dns; 6 | using Mozilla.IoT.WebThing.Integration.Test.Web; 7 | using Xunit; 8 | 9 | namespace Mozilla.IoT.WebThing.Integration.Test.mDNS 10 | { 11 | public class MDnsTest : IClassFixture, IDisposable 12 | { 13 | private readonly TestHost _host; 14 | private readonly ServiceDiscovery _serviceDiscovery; 15 | private DomainName _name; 16 | 17 | public MDnsTest(TestHost testHost) 18 | { 19 | _host = testHost; 20 | _serviceDiscovery = new ServiceDiscovery(); 21 | 22 | _serviceDiscovery.ServiceInstanceDiscovered += OnServiceInstanceDiscovered; 23 | _serviceDiscovery.ServiceDiscovered += OnServiceDiscovered; 24 | } 25 | 26 | [Fact] 27 | public async Task DiscoveryService() 28 | { 29 | await Task.Delay(TimeSpan.FromSeconds(15)); 30 | _name.Should().NotBeNull(); 31 | _name.Labels.Should().Contain("test-thing"); 32 | } 33 | 34 | private void OnServiceDiscovered(object sender, DomainName args) 35 | { 36 | if (args.Labels.Any(x => x.Contains("_webthing"))) 37 | { 38 | _name = args; 39 | } 40 | } 41 | 42 | private void OnServiceInstanceDiscovered(object sender, ServiceInstanceDiscoveryEventArgs args) 43 | { 44 | if (args.ServiceInstanceName.Labels.Any(x => x.Contains("_webthing"))) 45 | { 46 | _name = args.ServiceInstanceName; 47 | } 48 | } 49 | 50 | public void Dispose() 51 | { 52 | _serviceDiscovery.ServiceInstanceDiscovered -= OnServiceInstanceDiscovered; 53 | _serviceDiscovery.ServiceDiscovered -= OnServiceDiscovered; 54 | _serviceDiscovery.Dispose(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sample/SingleThing/Startup.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Mvc.ApplicationParts; 5 | using Microsoft.AspNetCore.Rewrite; 6 | using Microsoft.AspNetCore.Routing; 7 | using Microsoft.AspNetCore.WebSockets; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using SingleThing.Things; 11 | 12 | namespace SingleThing 13 | { 14 | public class Startup 15 | { 16 | // This method gets called by the runtime. Use this method to add services to the container. 17 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 18 | public void ConfigureServices(IServiceCollection services) 19 | { 20 | services.AddThings(opt => opt.UseThingAdapterUrl = true) 21 | .AddThing(); 22 | 23 | 24 | services.AddWebSockets(opt => { }); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.Use(async (context, next) => 36 | { 37 | var url = context.Request.Path.Value; 38 | 39 | // Rewrite to index 40 | if (url == "/") 41 | { 42 | // rewrite and continue processing 43 | context.Request.Path = "/things"; 44 | } 45 | await next(); 46 | }); 47 | 48 | app.UseRouting(); 49 | 50 | app.UseWebSockets(); 51 | 52 | app.UseEndpoints(endpoints => 53 | { 54 | endpoints.MapThings(); 55 | }); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/Convertibles/Input/SystemTextJsonInputConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json; 4 | 5 | namespace Mozilla.IoT.WebThing.Json.Convertibles.Input 6 | { 7 | /// 8 | /// Represent convertible/getter from . 9 | /// 10 | public class SystemTextJsonInputConvertible : SystemTexJsonConvertible 11 | { 12 | private readonly Dictionary _convertibles; 13 | 14 | /// 15 | /// Initialize a new instance of . 16 | /// 17 | /// The convertibles. 18 | public SystemTextJsonInputConvertible(Dictionary convertibles) 19 | { 20 | _convertibles = convertibles; 21 | } 22 | 23 | /// 24 | protected override bool TryConvert(JsonElement source, out object? result) 25 | { 26 | result = null; 27 | if (!source.TryGetProperty("input", out var input)) 28 | { 29 | return false; 30 | } 31 | 32 | if (input.ValueKind != JsonValueKind.Object) 33 | { 34 | return false; 35 | } 36 | 37 | var dict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); 38 | 39 | foreach (var property in input.EnumerateObject()) 40 | { 41 | if (!_convertibles.TryGetValue(property.Name, out var convertible)) 42 | { 43 | return false; 44 | } 45 | 46 | if (!convertible.TryConvert(property.Value, out var value)) 47 | { 48 | return false; 49 | } 50 | 51 | dict.Add(property.Name, value); 52 | } 53 | 54 | result = dict; 55 | return true; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Action/AbstractActionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json; 4 | using AutoFixture; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Mozilla.IoT.WebThing.Extensions; 7 | using Mozilla.IoT.WebThing.Factories; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace Mozilla.IoT.WebThing.Integration.Test.Action 11 | { 12 | public abstract class AbstractActionTest 13 | { 14 | protected IServiceProvider Provider { get; } 15 | protected IThingContextFactory Factory { get; } 16 | protected Fixture Fixture { get; } 17 | 18 | protected virtual void ConfigureServiceCollection(IServiceCollection collection) { } 19 | 20 | protected AbstractActionTest() 21 | { 22 | Fixture = new Fixture(); 23 | 24 | var collection = new ServiceCollection(); 25 | collection.AddThings(); 26 | collection.AddLogging(); 27 | 28 | ConfigureServiceCollection(collection); 29 | 30 | Provider = collection.BuildServiceProvider().CreateScope().ServiceProvider; 31 | Factory = Provider.GetRequiredService(); 32 | } 33 | 34 | protected abstract JsonElement CreateJson(T value); 35 | 36 | protected abstract IEnumerable CreateInvalidJson(); 37 | 38 | protected virtual T CreateValue() => Fixture.Create(); 39 | 40 | protected void TestResponse(string response) 41 | where TThing : Thing, new() 42 | { 43 | var thing = new TThing(); 44 | var context = Factory.Create(thing, new ThingOption()); 45 | 46 | var message = JsonSerializer.Serialize(context.Response, 47 | new ThingOption().ToJsonSerializerOptions()); 48 | 49 | FluentAssertions.Json.JsonAssertionExtensions.Should(JToken.Parse(message)) 50 | .BeEquivalentTo(JToken.Parse(response)); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Convertibles/Input/NewtonsoftInputConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Mozilla.IoT.WebThing.Json.Convertibles; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace Mozilla.IoT.WebThing.Newtonsoft.Convertibles.Input 7 | { 8 | /// 9 | /// Represent convertible/getter to input from . 10 | /// 11 | public class NewtonsoftInputConvertible : NewtonsoftConvertible 12 | { 13 | private readonly Dictionary _convertibles; 14 | 15 | /// 16 | /// Initialize a new instance of . 17 | /// 18 | /// The convertibles. 19 | public NewtonsoftInputConvertible(Dictionary convertibles) 20 | { 21 | _convertibles = convertibles; 22 | } 23 | 24 | /// 25 | protected override bool TryConvert(JToken source, out object? result) 26 | { 27 | result = null; 28 | var input = source["input"]; 29 | if (input == null) 30 | { 31 | return false; 32 | } 33 | 34 | var dict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); 35 | 36 | if (input.Type != JTokenType.Object) 37 | { 38 | return false; 39 | } 40 | 41 | foreach (var (name, jsonValue) in input.Value()) 42 | { 43 | if (!_convertibles.TryGetValue(name, out var convertible)) 44 | { 45 | return false; 46 | } 47 | 48 | if (!convertible.TryConvert(jsonValue!, out var value)) 49 | { 50 | return false; 51 | } 52 | 53 | dict.Add(name, value); 54 | } 55 | 56 | result = dict; 57 | return true; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Middlewares/ThingAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.AspNetCore.Http.Extensions; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Mozilla.IoT.WebThing.Extensions; 8 | 9 | namespace Mozilla.IoT.WebThing.Middlewares 10 | { 11 | internal static class ThingAdapter 12 | { 13 | private static readonly object s_locker = new object(); 14 | public static void Adapt(HttpContext context, Thing thing) 15 | { 16 | var response = thing.ThingContext.Response; 17 | if (!response.ContainsKey("id")) 18 | { 19 | lock (s_locker) 20 | { 21 | if (!response.ContainsKey("id")) 22 | { 23 | var option = context.RequestServices.GetRequiredService(); 24 | var value = UriHelper.BuildAbsolute(context.Request.Scheme, context.Request.Host); 25 | var builder = new UriBuilder(value) {Path = $"/things/{option.PropertyNamingPolicy.ConvertName(thing.Name)}"}; 26 | 27 | if (option.UseThingAdapterUrl) 28 | { 29 | response.TryAdd("id", thing.Id); 30 | response.TryAdd("href", builder.Path); 31 | response.TryAdd("base", builder.Uri.ToString()); 32 | } 33 | else 34 | { 35 | response.TryAdd("id", builder.Uri.ToString()); 36 | } 37 | 38 | builder.Scheme = builder.Scheme == "http" ? "ws" : "wss"; 39 | 40 | var links = (List)response[option.PropertyNamingPolicy.ConvertName("Links")]!; 41 | links.Add(new Link("alternate", builder.Uri.ToString())); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sample/MultiThing/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Routing; 4 | using Microsoft.AspNetCore.WebSockets; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Mozilla.IoT.WebThing.Newtonsoft; 9 | using MultiThing.Things; 10 | 11 | namespace MultiThing 12 | { 13 | public class Startup 14 | { 15 | public Startup(IConfiguration configuration) 16 | { 17 | Configuration = configuration; 18 | } 19 | 20 | public IConfiguration Configuration { get; } 21 | 22 | // This method gets called by the runtime. Use this method to add services to the container. 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | services.AddThings(opt => opt.UseThingAdapterUrl = true) 26 | .AddNewtonsoft() 27 | .AddThing() 28 | .AddThing(); 29 | 30 | services.AddWebSockets(opt => { }); 31 | } 32 | 33 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 34 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 35 | { 36 | if (env.IsDevelopment()) 37 | { 38 | app.UseDeveloperExceptionPage(); 39 | } 40 | 41 | app.Use(async (context, next) => 42 | { 43 | var url = context.Request.Path.Value; 44 | 45 | // Rewrite to index 46 | if (url == "/") 47 | { 48 | // rewrite and continue processing 49 | context.Request.Path = "/things"; 50 | } 51 | await next(); 52 | }); 53 | 54 | app.UseRouting(); 55 | 56 | app.UseWebSockets(); 57 | 58 | app.UseEndpoints(endpoints => 59 | { 60 | endpoints.MapThings(); 61 | }); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sample/MultiThing/Things/FakeGpioHumiditySensor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.Extensions.Logging; 4 | using Mozilla.IoT.WebThing; 5 | using Mozilla.IoT.WebThing.Attributes; 6 | 7 | namespace MultiThing.Things 8 | { 9 | public class FakeGpioHumiditySensor : Thing 10 | { 11 | private readonly Random _random; 12 | private readonly ILogger _logger; 13 | public FakeGpioHumiditySensor(ILogger logger) 14 | { 15 | _logger = logger; 16 | _random = new Random(); 17 | Task.Factory.StartNew(() => 18 | { 19 | while (true) 20 | { 21 | Task.Delay(3_000).GetAwaiter().GetResult(); 22 | Level = ReadFromGPIO(); 23 | } 24 | }, TaskCreationOptions.LongRunning); 25 | } 26 | public override string Name => "my-humidity-sensor-1234"; 27 | 28 | public override string Title => "My Humidity Sensor"; 29 | 30 | public override string[] Type { get; } = new[] {"MultiLevelSensor"}; 31 | 32 | public override string Description => "A web connected humidity sensor"; 33 | 34 | private double _level; 35 | 36 | [ThingProperty(Type = new[] {"LevelProperty"}, Title = "Humidity", Description = "The current humidity in %", 37 | Minimum = 0, Maximum = 100, Unit = "percent")] 38 | public double Level 39 | { 40 | get => _level; 41 | private set 42 | { 43 | _level = value; 44 | _logger.LogInformation("setting new humidity level: {level}", value); 45 | OnPropertyChanged(); 46 | } 47 | } 48 | 49 | /// 50 | /// Mimic an actual sensor updating its reading every couple seconds. 51 | /// 52 | /// 53 | private double ReadFromGPIO() 54 | { 55 | var value = Math.Abs(70.0d * _random.NextDouble() * (-0.5 + _random.NextDouble())); 56 | return value; 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Endpoints/GetEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text.Json; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | using Mozilla.IoT.WebThing.Extensions; 11 | 12 | namespace Mozilla.IoT.WebThing.Endpoints 13 | { 14 | internal class GetEvents 15 | { 16 | public static async Task InvokeAsync(HttpContext context) 17 | { 18 | var service = context.RequestServices; 19 | var logger = service.GetRequiredService>(); 20 | var things = service.GetRequiredService>(); 21 | 22 | var name = context.GetRouteData("name"); 23 | logger.LogInformation("Requesting Events. [Thing: {name}]", name); 24 | 25 | var thing = things.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); 26 | 27 | if (thing == null) 28 | { 29 | logger.LogInformation("Thing not found. [Thing: {name}]", name); 30 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 31 | return; 32 | } 33 | 34 | var option = service.GetRequiredService(); 35 | var result = new LinkedList>(); 36 | 37 | foreach (var (key, events) in thing.ThingContext.Events) 38 | { 39 | var @eventsArray = events.ToArray(); 40 | foreach (var @event in eventsArray) 41 | { 42 | result.AddLast(new Dictionary 43 | { 44 | [option.PropertyNamingPolicy.ConvertName(key)] = @event 45 | }); 46 | } 47 | } 48 | 49 | logger.LogInformation("Found {counter} events. [Thing: {name}]", result.Count, thing.Name); 50 | await context.WriteBodyAsync(HttpStatusCode.OK, result); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Web/Things/EventThing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using AutoFixture; 5 | using Microsoft.Extensions.Hosting; 6 | using Mozilla.IoT.WebThing.Attributes; 7 | 8 | namespace Mozilla.IoT.WebThing.Integration.Test.Web.Things 9 | { 10 | public class EventThing : Thing 11 | { 12 | public override string Name => "event-thing"; 13 | 14 | public event EventHandler Level; 15 | 16 | [ThingEvent(Name = "info")] 17 | public event EventHandler Foo; 18 | 19 | internal void Invoke(int level) 20 | { 21 | Level?.Invoke(this, level); 22 | } 23 | 24 | internal void Invoke(string foo) 25 | { 26 | Foo?.Invoke(this, foo); 27 | } 28 | } 29 | 30 | 31 | public class WebSocketEventThing : EventThing 32 | { 33 | public override string Name => "web-socket-event-thing"; 34 | } 35 | 36 | 37 | public class FireEventService : BackgroundService 38 | { 39 | private readonly Fixture _fixture; 40 | private readonly EventThing _http; 41 | private readonly WebSocketEventThing _socket; 42 | public FireEventService(EventThing thing, WebSocketEventThing socket) 43 | { 44 | _http = thing ?? throw new ArgumentNullException(nameof(thing)); 45 | _socket = socket ?? throw new ArgumentNullException(nameof(socket)); 46 | _fixture = new Fixture(); 47 | } 48 | 49 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 50 | { 51 | while (!stoppingToken.IsCancellationRequested) 52 | { 53 | await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); 54 | _http.Invoke(_fixture.Create()); 55 | _socket.Invoke(_fixture.Create()); 56 | 57 | await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken); 58 | _http.Invoke(_fixture.Create()); 59 | _socket.Invoke(_fixture.Create()); 60 | } 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Builders/IActionBuilder.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using Mozilla.IoT.WebThing.Actions; 6 | using Mozilla.IoT.WebThing.Attributes; 7 | using Mozilla.IoT.WebThing.Extensions; 8 | 9 | namespace Mozilla.IoT.WebThing.Builders 10 | { 11 | /// 12 | /// Create property. 13 | /// 14 | public interface IActionBuilder 15 | { 16 | /// 17 | /// Set . 18 | /// 19 | /// The to be set. 20 | /// 21 | IActionBuilder SetThing(Thing thing); 22 | 23 | /// 24 | /// Set type. 25 | /// 26 | /// The typeto be set. 27 | /// 28 | IActionBuilder SetThingType(Type thingType); 29 | 30 | /// 31 | /// Set 32 | /// 33 | /// The to be set. 34 | /// 35 | IActionBuilder SetThingOption(ThingOption option); 36 | 37 | /// 38 | /// Add property. 39 | /// 40 | /// The action. 41 | /// The about action. 42 | IActionBuilder Add(MethodInfo action, ThingActionAttribute? information); 43 | 44 | /// 45 | /// Add property. 46 | /// 47 | /// The parameter. 48 | /// The about parameter. 49 | IActionBuilder Add(ParameterInfo parameter, JsonSchema jsonSchema); 50 | 51 | /// 52 | /// Build the 53 | /// 54 | /// New of the 55 | Dictionary Build(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Extensions/ThingOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Mozilla.IoT.WebThing.Extensions 5 | { 6 | /// 7 | /// The thing option. 8 | /// 9 | public class ThingOption 10 | { 11 | /// 12 | /// Max size of . 13 | /// The default value is 10. 14 | /// 15 | public int MaxEventSize { get; set; } = 10; 16 | 17 | /// 18 | /// If should ignore case to deserialize. 19 | /// The default value is true. 20 | /// 21 | public bool IgnoreCase { get; set; } = true; 22 | 23 | 24 | /// 25 | /// 26 | /// 27 | public bool IgnoreNullValues { get; set; } = true; 28 | 29 | /// 30 | /// If when serialize thing should serialize for use thing adapter. 31 | /// The default value is true. 32 | /// 33 | public bool UseThingAdapterUrl { get; set; } = true; 34 | 35 | /// 36 | /// Specifies the policy used to convert a property's name on an object to another format, such as camel-casing. 37 | /// The resulting property name is expected to match the JSON payload during deserialization, and 38 | /// will be used when writing the property name during serialization. 39 | /// 40 | public JsonNamingPolicy PropertyNamingPolicy { get; set; } = JsonNamingPolicy.CamelCase; 41 | 42 | /// 43 | /// The server name. 44 | /// 45 | public string ServerName { get; set; } = string.Empty; 46 | 47 | /// 48 | /// If use mDNS 49 | /// 50 | public bool RegistermDNS { get; set; } = true; 51 | 52 | /// 53 | /// The delay before execute mDNS advertise. It's necessary to the application know which port was bind by ASP.NET Core 54 | /// The default value is 5 seconds. 55 | /// 56 | public TimeSpan MDnsDelay { get; set; } = TimeSpan.FromSeconds(5); 57 | internal bool WriteIndented { get; set; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/TestThing/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Routing; 9 | using Microsoft.AspNetCore.WebSockets; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Hosting; 12 | using Mozilla.IoT.WebThing.Extensions; 13 | using TestThing.Converters; 14 | using TestThing.Things; 15 | 16 | namespace TestThing 17 | { 18 | public class Startup 19 | { 20 | // This method gets called by the runtime. Use this method to add services to the container. 21 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddThings(opt => 25 | { 26 | opt.AddConverters(new DateTimeConverter()) 27 | .AddConverters(new NullableDateTimeConverter()); 28 | }) 29 | .AddThing(); 30 | 31 | services.AddWebSockets(_ => { }); 32 | } 33 | 34 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 35 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 36 | { 37 | if (env.IsDevelopment()) 38 | { 39 | app.UseDeveloperExceptionPage(); 40 | } 41 | 42 | app.Use(async (context, next) => 43 | { 44 | var url = context.Request.Path.Value; 45 | 46 | // Rewrite to index 47 | if (url == "/") 48 | { 49 | // rewrite and continue processing 50 | context.Request.Path = "/things"; 51 | } 52 | await next(); 53 | }); 54 | 55 | app.UseRouting(); 56 | 57 | app.UseWebSockets(); 58 | 59 | app.UseEndpoints(endpoints => 60 | { 61 | endpoints.MapThings(); 62 | }); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Endpoints/GetAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Mozilla.IoT.WebThing.Endpoints 11 | { 12 | internal class GetAction 13 | { 14 | public static async Task InvokeAsync(HttpContext context) 15 | { 16 | var service = context.RequestServices; 17 | var logger = service.GetRequiredService>(); 18 | var things = service.GetRequiredService>(); 19 | var thingName = context.GetRouteData("name"); 20 | var actionName = context.GetRouteData("action"); 21 | 22 | logger.LogInformation("Requesting get Action. [Thing: {name}][Action: {actionName}]", 23 | thingName, actionName); 24 | var thing = things.FirstOrDefault(x => x.Name.Equals(thingName, StringComparison.OrdinalIgnoreCase)); 25 | 26 | if (thing == null) 27 | { 28 | logger.LogInformation("Thing not found. [Thing: {name}][Action: {actionName}]", thingName, actionName); 29 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 30 | return; 31 | } 32 | 33 | if (!thing.ThingContext.Actions.TryGetValue(actionName, out var actionContext)) 34 | { 35 | logger.LogInformation("Action not found. [Thing: {name}][Action: {actionName}]", thingName, actionName); 36 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 37 | return; 38 | } 39 | 40 | logger.LogInformation("Found action found. [Thing: {name}][Action: {actionName}]", thingName, actionName); 41 | 42 | var result = actionContext 43 | .Select(action => new Dictionary 44 | { 45 | [actionName] = action 46 | }) 47 | .ToList(); 48 | 49 | await context.WriteBodyAsync(HttpStatusCode.OK, result) 50 | .ConfigureAwait(false); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/Input/InputJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.Input 5 | { 6 | /// 7 | /// 8 | /// 9 | public class InputJsonSchemaValidation : IJsonSchemaValidation 10 | { 11 | private readonly Dictionary _schemaValidations; 12 | 13 | /// 14 | /// Initialize a new instance of . 15 | /// 16 | /// The >. 17 | public InputJsonSchemaValidation(Dictionary schemaValidations) 18 | { 19 | _schemaValidations = schemaValidations ?? throw new ArgumentNullException(nameof(schemaValidations)); 20 | } 21 | 22 | /// 23 | public bool IsValid(object? value) 24 | { 25 | if (!(value is Dictionary input)) 26 | { 27 | return false; 28 | } 29 | 30 | var properties = new HashSet(); 31 | 32 | foreach (var (propertyName, propertyValue) in input) 33 | { 34 | if (!_schemaValidations.TryGetValue(propertyName, out var schemaValidation)) 35 | { 36 | continue; 37 | } 38 | 39 | if (!schemaValidation.IsValid(propertyValue)) 40 | { 41 | return false; 42 | } 43 | 44 | properties.Add(propertyName); 45 | } 46 | 47 | foreach (var (propertyName, schemaValidation) in _schemaValidations) 48 | { 49 | if (properties.Contains(propertyName)) 50 | { 51 | continue; 52 | } 53 | 54 | if (!schemaValidation.IsValid(null)) 55 | { 56 | return false; 57 | } 58 | 59 | input.Add(propertyName, null!); 60 | } 61 | 62 | 63 | return true; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Integration.Test/Web/Things/ActionThing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.Logging; 6 | using Mozilla.IoT.WebThing.Attributes; 7 | 8 | namespace Mozilla.IoT.WebThing.Integration.Test.Web.Things 9 | { 10 | public class ActionThing : Thing 11 | { 12 | public override string Name => "action-thing"; 13 | 14 | public async Task LongRunning(CancellationToken cancellationToken) 15 | { 16 | await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken) 17 | .ConfigureAwait(false); 18 | } 19 | 20 | public ValueTask MayLongRunning(int? value, [FromServices]ILogger logger, CancellationToken cancellationToken) 21 | { 22 | logger.LogInformation($"Going to execute {nameof(MayLongRunning)}"); 23 | 24 | if (!value.HasValue || value.Value == 0) 25 | { 26 | return new ValueTask(); 27 | } 28 | 29 | return new ValueTask(Task.Delay(value.Value, cancellationToken)); 30 | } 31 | 32 | [ThingAction(Ignore = true)] 33 | public void Nothing() {} 34 | 35 | public void NoRestriction(string value, int level, bool active, Guid id, [FromServices]ILogger logger) 36 | { 37 | logger.LogInformation("Received: [Value: {value}][Level: {level}][Active: {active}][Id: {id}]", 38 | value, level, active, id); 39 | } 40 | 41 | public void WithRestriction([ThingParameter(IsNullable = false)]string value, 42 | [ThingParameter(ExclusiveMinimum = 1, ExclusiveMaximum = 100)]int level, 43 | bool active, 44 | [ThingParameter(Enum = new object[]{"4f12dbd1-ea24-40e6-ac26-620a1b787a25", "a8e3202d-7eaa-4889-a5cf-ec44275414eb"})]Guid id, 45 | [FromServices]ILogger logger) 46 | { 47 | logger.LogInformation("Received: [Value: {value}][Level: {level}][Active: {active}][Id: {id}]", 48 | value, level, active, id); 49 | } 50 | } 51 | 52 | public class WebSocketActionThing : ActionThing 53 | { 54 | public override string Name => "web-socket-action-thing"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Extensions/IEndpointRouteBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Mozilla.IoT.WebThing; 6 | using Mozilla.IoT.WebThing.Endpoints; 7 | using Mozilla.IoT.WebThing.Middlewares; 8 | using Mozilla.IoT.WebThing.WebSockets; 9 | 10 | namespace Microsoft.AspNetCore.Routing 11 | { 12 | /// 13 | /// 14 | /// 15 | public static class IEndpointRouteBuilderExtensions 16 | { 17 | /// 18 | /// Map Things endpoints. 19 | /// 20 | /// 21 | /// 22 | public static void MapThings(this IEndpointRouteBuilder endpoint) 23 | { 24 | if (endpoint == null) 25 | { 26 | throw new ArgumentNullException(nameof(endpoint)); 27 | } 28 | 29 | endpoint.MapGet("/things", GetAllThings.InvokeAsync); 30 | endpoint.MapGet("/things/{name}", context => context.WebSockets.IsWebSocketRequest 31 | ? WebSocket.InvokeAsync(context) : GetThing.InvokeAsync(context)); 32 | endpoint.MapGet("/things/{name}/properties", GetProperties.InvokeAsync); 33 | endpoint.MapGet("/things/{name}/properties/{property}", GetProperty.InvokeAsync); 34 | endpoint.MapPut("/things/{name}/properties/{property}", PutProperty.InvokeAsync); 35 | endpoint.MapGet("/things/{name}/events", GetEvents.InvokeAsync); 36 | endpoint.MapGet("/things/{name}/events/{event}", GetEvent.InvokeAsync); 37 | endpoint.MapPost("/things/{name}/actions", PostActions.InvokeAsync); 38 | endpoint.MapGet("/things/{name}/actions", GetActions.InvokeAsync); 39 | endpoint.MapPost("/things/{name}/actions/{action}", PostAction.InvokeAsync); 40 | endpoint.MapGet("/things/{name}/actions/{action}", GetAction.InvokeAsync); 41 | endpoint.MapGet("/things/{name}/actions/{action}/{id}", GetActionById.InvokeAsync); 42 | endpoint.MapDelete("/things/{name}/actions/{action}/{id}", DeleteAction.InvokeAsync); 43 | 44 | //To Force bind 45 | endpoint.ServiceProvider.GetService>(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/String/StringJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations.String 5 | { 6 | /// 7 | /// Represent string json schema validation. 8 | /// 9 | public class StringJsonSchemaValidation : IJsonSchemaValidation 10 | { 11 | private readonly bool _isNullable; 12 | 13 | private readonly int? _minimum; 14 | private readonly int? _maximum; 15 | private readonly HashSet? _enums; 16 | 17 | private readonly string? _pattern; 18 | 19 | /// 20 | /// Initialize a new instance of . 21 | /// 22 | /// Accepted null value. 23 | /// The minimum string length. 24 | /// The maximum string length. 25 | /// The accepted values. 26 | /// The string patterns. 27 | public StringJsonSchemaValidation(bool isNullable, int? minimum, int? maximum, 28 | string? pattern, HashSet? enums) 29 | { 30 | _isNullable = isNullable; 31 | _minimum = minimum; 32 | _maximum = maximum; 33 | _enums = enums; 34 | _pattern = pattern; 35 | } 36 | 37 | /// 38 | public bool IsValid(object? value) 39 | { 40 | if (value == null) 41 | { 42 | return _isNullable; 43 | } 44 | 45 | var comparable = value.ToString()!; 46 | 47 | if (_minimum.HasValue && comparable.Length < _minimum.Value) 48 | { 49 | return false; 50 | } 51 | 52 | if (_maximum.HasValue && comparable.Length > _maximum.Value) 53 | { 54 | return false; 55 | } 56 | 57 | if (!string.IsNullOrEmpty(_pattern) && !Regex.IsMatch(comparable, _pattern)) 58 | { 59 | return false; 60 | } 61 | 62 | return _enums == null || _enums.Contains(comparable); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Factories/Imp/SystemTexJsonConvertibleFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Mozilla.IoT.WebThing.Json.Convertibles; 4 | using Mozilla.IoT.WebThing.Json.Convertibles.Array; 5 | using Mozilla.IoT.WebThing.Json.Convertibles.String; 6 | 7 | namespace Mozilla.IoT.WebThing.Factories 8 | { 9 | /// 10 | public class SystemTexJsonConvertibleFactory : IJsonConvertibleFactory 11 | { 12 | /// 13 | public IJsonConvertible Create(TypeCode typeCode, Type type) 14 | { 15 | switch (typeCode) 16 | { 17 | case TypeCode.Boolean: 18 | return SystemTexJsonBooleanConvertible.Instance; 19 | case TypeCode.String: 20 | return SystemTexJsonStringConvertible.Instance; 21 | case TypeCode.Char: 22 | return SystemTexJsonCharConvertible.Instance; 23 | case TypeCode.DateTime: 24 | return SystemTexJsonDateTimeConvertible.Instance; 25 | case TypeCode.DateTimeOffset: 26 | return SystemTexJsonDateTimeOffsetConvertible.Instance; 27 | case TypeCode.Guid: 28 | return SystemTexJsonGuidConvertible.Instance; 29 | case TypeCode.TimeSpan: 30 | return SystemTexJsonTimeSpanConvertible.Instance; 31 | case TypeCode.Enum: 32 | return new SystemTexJsonEnumConvertible(@type); 33 | case TypeCode.SByte: 34 | case TypeCode.Byte: 35 | case TypeCode.Int16: 36 | case TypeCode.UInt16: 37 | case TypeCode.Int32: 38 | case TypeCode.UInt32: 39 | case TypeCode.Int64: 40 | case TypeCode.UInt64: 41 | case TypeCode.Float: 42 | case TypeCode.Double: 43 | case TypeCode.Decimal: 44 | return SystemTexJsonNumberConvertible.Instance; 45 | case TypeCode.Array: 46 | var arrayType = type.GetCollectionType(); 47 | return new SystemTexJsonArrayConvertible(Create(arrayType.ToTypeCode(), arrayType)); 48 | default: 49 | throw new ArgumentOutOfRangeException(nameof(typeCode), typeCode, null); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sample/SingleThing/Things/LampThing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging; 5 | using Mozilla.IoT.WebThing; 6 | using Mozilla.IoT.WebThing.Attributes; 7 | 8 | namespace SingleThing.Things 9 | { 10 | public class LampThing : Thing 11 | { 12 | public override string Name => "my-lamp-1234"; 13 | public override string? Title => "My Lamp"; 14 | public override string? Description => "A web connected lamp"; 15 | public override string[]? Type { get; } = new[] { "Light", "OnOffSwitch" }; 16 | 17 | private bool _on = true; 18 | [ThingProperty(Type = new[] {"OnOffProperty"}, Title = "On/Off", Description = "Whether the lamp is turned on")] 19 | public bool On 20 | { 21 | get => _on; 22 | set 23 | { 24 | _on = value; 25 | OnPropertyChanged(); 26 | } 27 | } 28 | 29 | private int _brightness = 50; 30 | [ThingProperty(Type = new[] {"BrightnessProperty"}, Title = "Brightness", Description = "The level of light from 0-100", Unit = "percent", Minimum = 0, Maximum = 100)] 31 | public int Brightness 32 | { 33 | get => _brightness; 34 | set 35 | { 36 | _brightness = value; 37 | OnPropertyChanged(); 38 | } 39 | } 40 | 41 | [ThingEvent(Title = "Overheated", Unit = "degree celsius", Type = new [] {"OverheatedEvent"}, Description = "The lamp has exceeded its safe operating temperature")] 42 | public event EventHandler? Overheated; 43 | 44 | 45 | [ThingAction(Title = "Fade", Type = new []{"FadeAction"}, Description = "Fade the lamp to a given level")] 46 | public async Task Fade( 47 | [ThingParameter(Minimum = 0, Maximum = 100, Unit = "percent")]int brightness, 48 | [ThingParameter(Minimum = 1, Unit = "milliseconds")]int duration, 49 | [FromServices]ILogger logger) 50 | { 51 | await Task.Delay(duration); 52 | 53 | logger.LogInformation("Going to set Brightness to {brightness}", brightness); 54 | Brightness = brightness; 55 | 56 | logger.LogInformation("Going to send event Overheated"); 57 | Overheated?.Invoke(this, 102); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Extensions/FixtureExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AutoFixture; 3 | 4 | namespace Mozilla.IoT.WebThing.Test.Extensions 5 | { 6 | public static class FixtureExtensions 7 | { 8 | public static object GetValue(this Fixture fixture, Type type) 9 | { 10 | if (type == typeof(bool)) 11 | { 12 | return fixture.Create(); 13 | } 14 | 15 | if (type == typeof(byte)) 16 | { 17 | return fixture.Create(); 18 | } 19 | 20 | if (type == typeof(sbyte)) 21 | { 22 | return fixture.Create(); 23 | } 24 | 25 | if (type == typeof(short)) 26 | { 27 | return fixture.Create(); 28 | } 29 | 30 | if (type == typeof(ushort)) 31 | { 32 | return fixture.Create(); 33 | } 34 | 35 | if (type == typeof(int)) 36 | { 37 | return fixture.Create(); 38 | } 39 | 40 | if (type == typeof(uint)) 41 | { 42 | return fixture.Create(); 43 | } 44 | 45 | if (type == typeof(long)) 46 | { 47 | return fixture.Create(); 48 | } 49 | 50 | if (type == typeof(ulong)) 51 | { 52 | return fixture.Create(); 53 | } 54 | 55 | if (type == typeof(float)) 56 | { 57 | return fixture.Create(); 58 | } 59 | 60 | if (type == typeof(double)) 61 | { 62 | return fixture.Create(); 63 | } 64 | 65 | if (type == typeof(decimal)) 66 | { 67 | return fixture.Create(); 68 | } 69 | 70 | if (type == typeof(DateTime)) 71 | { 72 | return fixture.Create(); 73 | } 74 | 75 | if (type == typeof(DateTimeOffset)) 76 | { 77 | return fixture.Create(); 78 | } 79 | 80 | return fixture.Create(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing.Newtonsoft/Extensions/ThingOptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json; 3 | using Mozilla.IoT.WebThing.Extensions; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Serialization; 6 | 7 | namespace Mozilla.IoT.WebThing.Newtonsoft 8 | { 9 | /// 10 | /// The extension to 11 | /// 12 | public static class ThingOptionExtensions 13 | { 14 | private static readonly object s_locker = new object(); 15 | private static JsonSerializerSettings? s_serializer = null; 16 | 17 | internal static JsonSerializerSettings ToJsonSerializerSettings(this ThingOption option) 18 | { 19 | if (s_serializer == null) 20 | { 21 | lock (s_locker) 22 | { 23 | if (s_serializer == null) 24 | { 25 | s_serializer = new JsonSerializerSettings 26 | { 27 | Formatting = Formatting.None, 28 | ContractResolver = option.PropertyNamingPolicy == JsonNamingPolicy.CamelCase 29 | ? new CamelCasePropertyNamesContractResolver() 30 | : new DefaultContractResolver() 31 | }; 32 | 33 | foreach (var converter in s_converters) 34 | { 35 | s_serializer.Converters.Add(converter); 36 | } 37 | 38 | s_converters.Clear(); 39 | } 40 | } 41 | } 42 | 43 | return s_serializer!; 44 | } 45 | 46 | private static readonly LinkedList s_converters = new LinkedList(); 47 | 48 | /// 49 | /// Add 50 | /// 51 | /// The . 52 | /// The to be added. 53 | /// The same instance passed in option. 54 | public static ThingOption AddJsonConvert(this ThingOption option, JsonConverter convert) 55 | { 56 | s_converters.AddLast(convert); 57 | return option; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/WebSockets/AddEventSubscription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Sockets; 6 | using System.Net.WebSockets; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Logging; 12 | using Mozilla.IoT.WebThing.Json; 13 | 14 | namespace Mozilla.IoT.WebThing.WebSockets 15 | { 16 | /// 17 | /// Add event subscription. 18 | /// 19 | public class AddEventSubscription : IWebSocketAction 20 | { 21 | private static readonly ArraySegment s_errorActionNotFound = new ArraySegment(Encoding.UTF8.GetBytes(@"{""messageType"": ""error"",""data"": {""status"": ""404 Not Found"",""message"": ""Event not found""}}")); 22 | 23 | 24 | /// 25 | public string Action => "addEventSubscription"; 26 | 27 | /// 28 | public async ValueTask ExecuteAsync(System.Net.WebSockets.WebSocket socket, Thing thing, object data, 29 | IServiceProvider provider, CancellationToken cancellationToken) 30 | { 31 | var logger = provider.GetRequiredService>(); 32 | 33 | var convert = provider.GetRequiredService(); 34 | foreach (var (eventName, _) in convert.ToEnumerable(data)) 35 | { 36 | if (thing.ThingContext.Events.TryGetValue(eventName, out _)) 37 | { 38 | logger.LogInformation("Going to add subscribes socket to event. [Thing: {thing}][Event: {eventName}]", 39 | thing.Name, eventName); 40 | 41 | thing.ThingContext.EventsSubscribes[eventName] 42 | .TryAdd( 43 | thing.ThingContext.Sockets.First(x => x.Value == socket).Key, 44 | socket); 45 | } 46 | else 47 | { 48 | logger.LogInformation("Event not found. [Thing: {thing}][Event: {eventName}]", thing.Name, eventName); 49 | await socket.SendAsync(s_errorActionNotFound, WebSocketMessageType.Text, true, cancellationToken) 50 | .ConfigureAwait(false); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Endpoints/GetEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text.Json; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | using Mozilla.IoT.WebThing.Extensions; 11 | 12 | namespace Mozilla.IoT.WebThing.Endpoints 13 | { 14 | internal class GetEvent 15 | { 16 | public static async Task InvokeAsync(HttpContext context) 17 | { 18 | var service = context.RequestServices; 19 | var logger = service.GetRequiredService>(); 20 | var things = service.GetRequiredService>(); 21 | 22 | var name = context.GetRouteData("name"); 23 | var @event = context.GetRouteData("event"); 24 | 25 | logger.LogInformation("Requesting Thing event. [Thing: {name}][Event: {eventName}]", name, @event); 26 | 27 | var thing = things.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); 28 | 29 | if (thing == null) 30 | { 31 | logger.LogInformation("Thing not found. [Thing: {name}][Event: {eventName}]", name, @event); 32 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 33 | return; 34 | } 35 | 36 | if (!thing.ThingContext.Events.TryGetValue(@event, out var events)) 37 | { 38 | logger.LogInformation("Event not found.[Thing: {thingName}][Event: {eventName}]", thing.Name, @event); 39 | context.Response.StatusCode = (int)HttpStatusCode.NotFound; 40 | return; 41 | } 42 | 43 | var option = service.GetRequiredService(); 44 | var result = new LinkedList>(); 45 | 46 | foreach (var e in events.ToArray()) 47 | { 48 | result.AddLast(new Dictionary 49 | { 50 | [option.PropertyNamingPolicy.ConvertName(@event)] = e 51 | }); 52 | } 53 | 54 | logger.LogInformation("Found {counter} events. [Thing: {name}][Event: {eventName}]", result.Count, thing.Name, @event); 55 | await context.WriteBodyAsync(HttpStatusCode.OK, result); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Json/SchemaValidations/Number/NumberJsonSchemaValidation.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Mozilla.IoT.WebThing.Json.SchemaValidations 4 | { 5 | /// 6 | /// Represent number json schema validation. 7 | /// 8 | public class NumberJsonSchemaValidation : IJsonSchemaValidation 9 | { 10 | private readonly bool _isNullable; 11 | private readonly decimal? _minimum; 12 | private readonly decimal? _maximum; 13 | private readonly decimal? _multipleOf; 14 | private readonly HashSet? _enums; 15 | 16 | /// 17 | /// Initialize a new instance of . 18 | /// 19 | /// Accepted null value. 20 | /// The minimum value. 21 | /// The maximum value. 22 | /// The multiple of value 23 | /// The accepted values. 24 | public NumberJsonSchemaValidation(bool isNullable, decimal? minimum, decimal? maximum, decimal? multipleOf, 25 | HashSet? enums) 26 | { 27 | _isNullable = isNullable; 28 | _minimum = minimum; 29 | _maximum = maximum; 30 | _multipleOf = multipleOf; 31 | _enums = enums; 32 | } 33 | 34 | /// 35 | public bool IsValid(object? value) 36 | { 37 | if (value == null) 38 | { 39 | return _isNullable; 40 | } 41 | 42 | if (!(value is decimal comparable)) 43 | { 44 | return false; 45 | } 46 | 47 | if (_minimum.HasValue && comparable.CompareTo(_minimum.Value) == -1) 48 | { 49 | return false; 50 | } 51 | 52 | if (_maximum.HasValue && comparable.CompareTo(_maximum.Value) == 1) 53 | { 54 | return false; 55 | } 56 | 57 | if (_multipleOf.HasValue && comparable % _multipleOf.Value != 0) 58 | { 59 | return false; 60 | } 61 | 62 | if (_enums != null && !_enums.Contains(comparable)) 63 | { 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tests/Mozilla.IoT.WebThing.Test/Extensions/IServiceExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.AspNetCore.Hosting.Server; 4 | using Microsoft.AspNetCore.WebSockets; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Mozilla.IoT.WebThing.Builders; 7 | using Mozilla.IoT.WebThing.Factories; 8 | using Mozilla.IoT.WebThing.Json; 9 | using Mozilla.IoT.WebThing.WebSockets; 10 | using NSubstitute; 11 | using Xunit; 12 | 13 | namespace Mozilla.IoT.WebThing.Test.Extensions 14 | { 15 | public class IServiceExtensionsTest 16 | { 17 | [Fact] 18 | public void CheckIfTheDependencyInjectionIsCorrect() 19 | { 20 | var service = new ServiceCollection(); 21 | 22 | service.AddThings() 23 | .AddThing(); 24 | 25 | service.AddLogging(); 26 | service.AddWebSockets(opt => { }); 27 | 28 | service.AddSingleton(Substitute.For()); 29 | 30 | var provider = service.BuildServiceProvider(new ServiceProviderOptions 31 | { 32 | ValidateOnBuild = true, 33 | ValidateScopes = true 34 | }); 35 | 36 | var scope = provider.CreateScope().ServiceProvider; 37 | _ = scope.GetRequiredService(); 38 | 39 | _ = scope.GetRequiredService(); 40 | _ = scope.GetRequiredService(); 41 | _ = scope.GetRequiredService(); 42 | _ = scope.GetRequiredService(); 43 | _ = scope.GetRequiredService(); 44 | _ = scope.GetRequiredService(); 45 | _ = scope.GetRequiredService(); 46 | _ = scope.GetRequiredService(); 47 | _ = scope.GetRequiredService(); 48 | _ = scope.GetRequiredService(); 49 | _ = scope.GetRequiredService>(); 50 | } 51 | 52 | public class FakeThing : Thing 53 | { 54 | public override string Name => "test"; 55 | 56 | public int Value { get; set; } 57 | 58 | public event EventHandler SomeThing; 59 | 60 | public void Emit() 61 | { 62 | SomeThing?.Invoke(this, 1); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/TestThing/Things/LampThing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging; 5 | using Mozilla.IoT.WebThing; 6 | using Mozilla.IoT.WebThing.Attributes; 7 | 8 | namespace TestThing.Things 9 | { 10 | public class LampThing : Thing 11 | { 12 | public override string Id => "urn:dev:ops:my-lamp-1234"; 13 | public override string Name => "my-lamp-1234"; 14 | public override string Title => "My Lamp"; 15 | public override string Description => "A web connected lamp"; 16 | public override string[] Type { get; } = { "Light", "OnOffSwitch" }; 17 | 18 | private bool _on = true; 19 | [ThingProperty(Type = new[] {"OnOffProperty"}, Title = "On/Off", Description = "Whether the lamp is turned on")] 20 | public bool On 21 | { 22 | get => _on; 23 | set 24 | { 25 | _on = value; 26 | OnPropertyChanged(); 27 | } 28 | } 29 | 30 | private int _brightness = 50; 31 | [ThingProperty(Type = new[] {"BrightnessProperty"}, Title = "Brightness", Description = "The level of light from 0-100", 32 | Unit = "percent", Minimum = 0, Maximum = 100)] 33 | public int Brightness 34 | { 35 | get => _brightness; 36 | set 37 | { 38 | _brightness = value; 39 | OnPropertyChanged(); 40 | } 41 | } 42 | 43 | [ThingEvent(Title = "Overheated", Unit = "degree celsius", Type = new [] {"OverheatedEvent"}, Description = "The lamp has exceeded its safe operating temperature")] 44 | public event EventHandler Overheated; 45 | 46 | 47 | [ThingAction(Name = "fade", Title = "Fade", Type = new []{"FadeAction"}, Description = "Fade the lamp to a given level")] 48 | public async Task Fade( 49 | [ThingParameter(Minimum = 0, Maximum = 100, Unit = "percent")]int brightness, 50 | [ThingParameter(Minimum = 1, Unit = "milliseconds")]int duration, 51 | [FromServices]ILogger logger) 52 | { 53 | await Task.Delay(duration); 54 | 55 | logger.LogInformation("Going to set Brightness to {brightness}", brightness); 56 | Brightness = brightness; 57 | 58 | logger.LogInformation("Going to send event Overheated"); 59 | Overheated?.Invoke(this, 102); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Mozilla.IoT.WebThing/Builders/IThingResponseBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using Mozilla.IoT.WebThing.Attributes; 4 | using Mozilla.IoT.WebThing.Extensions; 5 | 6 | namespace Mozilla.IoT.WebThing.Builders 7 | { 8 | /// 9 | /// Create . 10 | /// 11 | public interface IThingResponseBuilder 12 | { 13 | /// 14 | /// Set . 15 | /// 16 | /// The to be set. 17 | /// 18 | IThingResponseBuilder SetThing(Thing thing); 19 | 20 | /// 21 | /// Set 22 | /// 23 | /// The to be set. 24 | /// 25 | IThingResponseBuilder SetThingOption(ThingOption option); 26 | 27 | /// 28 | /// Add event. 29 | /// 30 | /// The event. 31 | /// Extra information about event 32 | void Add(EventInfo @event, ThingEventAttribute? eventInfo); 33 | 34 | /// 35 | /// Add property. 36 | /// 37 | /// The property. 38 | /// 39 | /// The about property 40 | void Add(PropertyInfo property, ThingPropertyAttribute? attribute, JsonSchema jsonSchema); 41 | 42 | /// 43 | /// Add action. 44 | /// 45 | /// The action. 46 | /// 47 | void Add(MethodInfo action, ThingActionAttribute? attribute); 48 | 49 | /// 50 | /// Add property. 51 | /// 52 | /// The parameter. 53 | /// 54 | /// The about parameter 55 | void Add(ParameterInfo parameter, ThingParameterAttribute? attribute, JsonSchema jsonSchema); 56 | 57 | /// 58 | /// Build the . 59 | /// 60 | /// New . 61 | Dictionary Build(); 62 | } 63 | } 64 | --------------------------------------------------------------------------------