This commit is contained in:
Spyros Garyfallos 2018-10-10 15:51:16 -07:00
Родитель 5a75604987
Коммит 623585192f
25 изменённых файлов: 1009 добавлений и 80 удалений

Просмотреть файл

@ -0,0 +1,8 @@
{
"profiles": {
"TemperatureSensor": {
"commandName": "Project",
"commandLineArgs": "metadata=true"
}
}
}

Просмотреть файл

@ -26,7 +26,8 @@ namespace ThermostatApplication
//register the modules
host.RegisterModule<ITemperatureSensor, TemperatureSensor>();
//var description = ServiceDescriptor.Describe<TemperatureSensor>(e => JsonSchema4.FromTypeAsync(e).Result.ToJson());
//var description = Microsoft.Azure.TypeEdge.Description.ServiceDescriptor.Describe<TemperatureSensor>();
//Microsoft.Azure.TypeEdge.Host.Service.ServiceGenerator.CreateFiles(description);
//host.RegisterExternalModule(new TypeEdge.Host.Docker.DockerModule("tempSensor",
// new HostingSettings("mcr.microsoft.com/azureiotedge-simulated-temperature-sensor:1.0", null),

Просмотреть файл

@ -111,10 +111,12 @@ namespace Microsoft.Azure.TypeEdge.Host.Docker
try
{
var containers =
await _dockerClient.Containers.ListContainersAsync(new ContainersListParameters {All = true}, cancellationToken);
await _dockerClient.Containers.ListContainersAsync(new ContainersListParameters {All = true},
cancellationToken);
if (containers
.SingleOrDefault(e => e.Image == (_moduleWithIdentity.Module as Devices.Edge.Agent.Docker.DockerModule)?.Config
.Image) != null)
.SingleOrDefault(e =>
e.Image == (_moduleWithIdentity.Module as Devices.Edge.Agent.Docker.DockerModule)?.Config
.Image) != null)
{
Console.WriteLine($"Removing {_moduleWithIdentity.Module.Name}...");
await (await _dockerFactory.RemoveAsync(_moduleWithIdentity.Module))

Просмотреть файл

@ -42,6 +42,38 @@
<ItemGroup>
<PackageReference Include="Docker.DotNet" Version="3.125.2" />
<PackageReference Include="NJsonSchema" Version="9.11.0" />
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.10" />
<PackageReference Include="System.CodeDom" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="mscorlib">
<HintPath>mscorlib</HintPath>
</Reference>
<Reference Include="System">
<HintPath>System</HintPath>
</Reference>
<Reference Include="System.Core">
<HintPath>System.Core</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<ItemGroup>
<Compile Update="Service\Service.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Service.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Service\Service.tt">
<Generator>TextTemplatingFilePreprocessor</Generator>
<LastGenOutput>Service.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,192 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Azure.TypeEdge.Description;
using Newtonsoft.Json.Schema;
namespace Microsoft.Azure.TypeEdge.Host.Service
{
//copied from https://gist.github.com/balexandre/e8cd6a965cdc532cb6ae#file-jsonschematopocos-cs
public class CodeGenerator
{
private const string Cyrillic = "Cyrillic";
private const string Nullable = "?";
private const string PocoClassPrefix = "MailChimp_";
public string Generate(TypeDescription typeDescription)
{
var schema = JSchema.Parse(typeDescription.Description);
var name = typeDescription.Name;
if (schema.Type == null)
throw new Exception("Schema does not specify a type.");
var sb = new StringBuilder();
switch (schema.Type)
{
case JSchemaType.Object:
sb.Append(ConvertJsonSchemaObjectToPoco(name, schema));
break;
case JSchemaType.Array:
foreach (var item in schema.Items.Where(x => x.Type.HasValue && x.Type == JSchemaType.Object))
sb.Append(ConvertJsonSchemaObjectToPoco(null, item));
break;
}
return sb.ToString();
}
private StringBuilder ConvertJsonSchemaObjectToPoco(string classname, JSchema schema)
{
return ConvertJsonSchemaObjectToPoco(classname, schema, out var newClassName);
}
private StringBuilder ConvertJsonSchemaObjectToPoco(string className, JSchema schema, out string newClassName)
{
var sb = new StringBuilder();
var isEnum = schema.Enum != null && schema.Enum.Any();
sb.AppendLine(GenerateObjectSummary(schema));
sb.AppendFormat("public {0} ", isEnum ? "enum" : "class");
if (string.IsNullOrEmpty(className)) className = GetClassName(schema);
newClassName = className;
sb.Append(className);
sb.AppendLine(" {");
if (isEnum)
sb.AppendLine(string.Join(",", schema.Enum));
else
foreach (var item in schema.Properties)
{
// Property Summary
sb.AppendLine(GenerateObjectSummary(item.Value));
sb.Append("public ");
sb.Append(GetClrType(item.Value, sb));
sb.Append(" ");
sb.Append(item.Key.Trim());
sb.AppendLine(" { get; set; }");
}
sb.AppendLine("}");
return sb;
}
private string GenerateObjectSummary(JSchema schema)
{
var sb = new StringBuilder();
// summary
sb.AppendLine("\n/// <summary>");
if (!string.IsNullOrWhiteSpace(schema.Title))
sb.AppendFormat("/// {0}\n", schema.Title);
if (!string.IsNullOrWhiteSpace(schema.Description))
sb.AppendFormat("/// {0}\n", schema.Description);
sb.AppendLine("/// </summary>");
// extra data
foreach (var ed in schema.ExtensionData) sb.AppendFormat("/// <{0}>{1}</{0}>\n", ed.Key, ed.Value);
return sb.ToString();
}
private string GetClassName(JSchema schema)
{
if (schema.Title != null)
return string.Format("{0}{1}", PocoClassPrefix, GenerateSlug(schema.Title));
return string.Format("{0}{1}", PocoClassPrefix, Guid.NewGuid().ToString("N"));
}
private string GenerateSlug(string phrase)
{
var str = RemoveAccent(phrase);
str = Regex.Replace(str, @"[^a-zA-Z\s-]", ""); // invalid chars
str = Regex.Replace(str, @"\s+", " ").Trim(); // convert multiple spaces into one space, trim
str = Regex.Replace(str, @"\s", "_"); // convert spaces to underscores
return str;
}
private string RemoveAccent(string txt)
{
var bytes = Encoding.GetEncoding(Cyrillic).GetBytes(txt);
return Encoding.ASCII.GetString(bytes);
}
private string GetClrType(JSchema jsonSchema, StringBuilder sb)
{
string className = null;
switch (jsonSchema.Type)
{
case JSchemaType.Array:
if (jsonSchema.Items.Count == 0)
return "IEnumerable<object>";
if (jsonSchema.Items.Count == 1)
return string.Format("IEnumerable<{0}>", GetClrType(jsonSchema.Items.First(), sb));
throw new Exception("Not sure what type this will be.");
case JSchemaType.Boolean:
return string.Format("bool{0}",
jsonSchema.Required == null || !jsonSchema.Required.Any() ? string.Empty : Nullable);
case JSchemaType.Number:
return string.Format("float{0}",
jsonSchema.Required == null || !jsonSchema.Required.Any() ? string.Empty : Nullable);
case JSchemaType.Integer:
return string.Format("int{0}",
jsonSchema.Required == null || !jsonSchema.Required.Any() ? string.Empty : Nullable);
case JSchemaType.String:
if (jsonSchema.Enum != null && jsonSchema.Enum.Any())
{
// it's an enumeration
sb.Insert(0, ConvertJsonSchemaObjectToPoco(null, jsonSchema, out className));
return className;
}
return "string";
case JSchemaType.Object:
sb.Insert(0, ConvertJsonSchemaObjectToPoco(null, jsonSchema, out className));
return className;
case JSchemaType.None:
case JSchemaType.Null:
default:
return "object";
}
}
}
// To be used when $ref has URL's instead Id's
public class UrlResolver : JSchemaResolver
{
public override Stream GetSchemaResource(ResolveSchemaContext context, SchemaReference reference)
{
return GetStreamFromUrl(reference.BaseUri);
}
// http://stackoverflow.com/a/19051936/28004
private Stream GetStreamFromUrl(Uri url)
{
byte[] refData = null;
using (var wc = new WebClient())
{
refData = wc.DownloadData(url);
}
return new MemoryStream(refData);
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
namespace Microsoft.Azure.TypeEdge.Host.Service
{
public class CodeGeneratorSettings
{
public static CodeGeneratorSettings Default { get; } = new CodeGeneratorSettings
{Namespace = "TypeEdge.Proxy", OutputPath = "./TypeEdge.Proxy"};
public string Namespace { get; set; }
public string OutputPath { get; set; }
public Language Language { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,8 @@
namespace Microsoft.Azure.TypeEdge.Host.Service
{
public enum Language
{
CSharp = 0,
TypeScript = 1
}
}

Просмотреть файл

@ -0,0 +1,435 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 15.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Microsoft.Azure.TypeEdge.Host.Service
{
/// <summary>
/// Class to produce the template output
/// </summary>
#line 1 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
[GeneratedCode("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")]
public partial class Service : ServiceBase
{
/// <summary>
/// Create the template output
/// </summary>
public virtual string TransformText()
{
Write("\r\nusing Microsoft.Azure.TypeEdge.Attributes;\r\nusing Microsoft.Azure.TypeEdge.Modu" +
"les.Endpoints;\r\nusing Microsoft.Azure.TypeEdge.Twins;\r\n \r\nnamespace ");
#line 11 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(_codeGeneratorSettings.Namespace));
#line default
#line hidden
Write("\r\n{\r\n [TypeModule]\r\n public interface I");
#line 14 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(_serviceDescription.Name));
#line default
#line hidden
Write(" \r\n {\r\n ");
#line 16 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
if (_serviceDescription.InputDescriptions != null)
foreach (var endpoint in _serviceDescription.InputDescriptions)
{
#line default
#line hidden
Write(" Input<");
#line 20 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(endpoint.TypeDescription.Name));
#line default
#line hidden
Write("> ");
#line 20 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(endpoint.Name));
#line default
#line hidden
Write(" { get; set; }\r\n\t");
#line 21 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
}
#line default
#line hidden
Write("\r\n\t");
#line 25 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
if (_serviceDescription.OutputDescriptions != null)
foreach (var endpoint in _serviceDescription.OutputDescriptions)
{
#line default
#line hidden
Write(" Output<");
#line 29 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(endpoint.TypeDescription.Name));
#line default
#line hidden
Write("> ");
#line 29 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(endpoint.Name));
#line default
#line hidden
Write(" { get; set; }\r\n\t");
#line 30 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
}
#line default
#line hidden
Write("\r\n\t");
#line 34 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
if (_serviceDescription.TwinDescriptions != null)
foreach (var twin in _serviceDescription.TwinDescriptions)
{
#line default
#line hidden
Write(" ModuleTwin<");
#line 38 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(twin.TypeDescription.Name));
#line default
#line hidden
Write("> ");
#line 38 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(twin.Name));
#line default
#line hidden
Write(" { get; set; }\r\n\t");
#line 39 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
}
#line default
#line hidden
Write("\r\n\r\n\t");
#line 44 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
if (_serviceDescription.DirectMethodDescriptions != null)
foreach (var method in _serviceDescription.DirectMethodDescriptions)
{
var returnType = "void";
var arguments = "";
if (method.ReturnTypeDescription != null)
returnType = method.ReturnTypeDescription.Name;
if (method.ArgumentsTypeDescription != null)
arguments = string.Join(",",
method.ArgumentsTypeDescription.Select(e => $"{e.TypeDescription.Name} {e.Name}")
.ToArray());
#line default
#line hidden
Write(" ");
#line 54 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(returnType));
#line default
#line hidden
Write(" ");
#line 54 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(method.Name));
#line default
#line hidden
Write("(");
#line 54 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
Write(ToStringHelper.ToStringWithCulture(arguments));
#line default
#line hidden
Write(");\r\n\t");
#line 55 "C:\work\oss\me\TypeEdge\Microsoft.Azure.TypeEdge.Host\Service\Service.tt"
}
#line default
#line hidden
Write("\r\n }\r\n}");
return GenerationEnvironment.ToString();
}
}
#line default
#line hidden
#region Base class
/// <summary>
/// Base class for this transformation
/// </summary>
[GeneratedCode("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")]
public class ServiceBase
{
#region Fields
private StringBuilder generationEnvironmentField;
private CompilerErrorCollection errorsField;
private List<int> indentLengthsField;
private bool endsWithNewline;
#endregion
#region Properties
/// <summary>
/// The string builder that generation-time code is using to assemble generated output
/// </summary>
protected StringBuilder GenerationEnvironment
{
get
{
if (generationEnvironmentField == null) generationEnvironmentField = new StringBuilder();
return generationEnvironmentField;
}
set => generationEnvironmentField = value;
}
/// <summary>
/// The error collection for the generation process
/// </summary>
public CompilerErrorCollection Errors
{
get
{
if (errorsField == null) errorsField = new CompilerErrorCollection();
return errorsField;
}
}
/// <summary>
/// A list of the lengths of each indent that was added with PushIndent
/// </summary>
private List<int> indentLengths
{
get
{
if (indentLengthsField == null) indentLengthsField = new List<int>();
return indentLengthsField;
}
}
/// <summary>
/// Gets the current indent we use when adding lines to the output
/// </summary>
public string CurrentIndent { get; private set; } = "";
/// <summary>
/// Current transformation session
/// </summary>
public virtual IDictionary<string, object> Session { get; set; }
#endregion
#region Transform-time helpers
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void Write(string textToAppend)
{
if (string.IsNullOrEmpty(textToAppend)) return;
// If we're starting off, or if the previous text ended with a newline,
// we have to append the current indent first.
if (GenerationEnvironment.Length == 0
|| endsWithNewline)
{
GenerationEnvironment.Append(CurrentIndent);
endsWithNewline = false;
}
// Check if the current text ends with a newline
if (textToAppend.EndsWith(Environment.NewLine, StringComparison.CurrentCulture)) endsWithNewline = true;
// This is an optimization. If the current indent is "", then we don't have to do any
// of the more complex stuff further down.
if (CurrentIndent.Length == 0)
{
GenerationEnvironment.Append(textToAppend);
return;
}
// Everywhere there is a newline in the text, add an indent after it
textToAppend = textToAppend.Replace(Environment.NewLine, Environment.NewLine + CurrentIndent);
// If the text ends with a newline, then we should strip off the indent added at the very end
// because the appropriate indent will be added when the next time Write() is called
if (endsWithNewline)
GenerationEnvironment.Append(textToAppend, 0, textToAppend.Length - CurrentIndent.Length);
else
GenerationEnvironment.Append(textToAppend);
}
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void WriteLine(string textToAppend)
{
Write(textToAppend);
GenerationEnvironment.AppendLine();
endsWithNewline = true;
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void Write(string format, params object[] args)
{
Write(string.Format(CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void WriteLine(string format, params object[] args)
{
WriteLine(string.Format(CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Raise an error
/// </summary>
public void Error(string message)
{
var error = new CompilerError();
error.ErrorText = message;
Errors.Add(error);
}
/// <summary>
/// Raise a warning
/// </summary>
public void Warning(string message)
{
var error = new CompilerError();
error.ErrorText = message;
error.IsWarning = true;
Errors.Add(error);
}
/// <summary>
/// Increase the indent
/// </summary>
public void PushIndent(string indent)
{
if (indent == null) throw new ArgumentNullException("indent");
CurrentIndent = CurrentIndent + indent;
indentLengths.Add(indent.Length);
}
/// <summary>
/// Remove the last indent that was added with PushIndent
/// </summary>
public string PopIndent()
{
var returnValue = "";
if (indentLengths.Count > 0)
{
var indentLength = indentLengths[indentLengths.Count - 1];
indentLengths.RemoveAt(indentLengths.Count - 1);
if (indentLength > 0)
{
returnValue = CurrentIndent.Substring(CurrentIndent.Length - indentLength);
CurrentIndent = CurrentIndent.Remove(CurrentIndent.Length - indentLength);
}
}
return returnValue;
}
/// <summary>
/// Remove any indentation
/// </summary>
public void ClearIndent()
{
indentLengths.Clear();
CurrentIndent = "";
}
#endregion
#region ToString Helpers
/// <summary>
/// Utility class to produce culture-oriented representation of an object as a string.
/// </summary>
public class ToStringInstanceHelper
{
private IFormatProvider formatProviderField = CultureInfo.InvariantCulture;
/// <summary>
/// Gets or sets format provider to be used by ToStringWithCulture method.
/// </summary>
public IFormatProvider FormatProvider
{
get => formatProviderField;
set
{
if (value != null) formatProviderField = value;
}
}
/// <summary>
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
/// </summary>
public string ToStringWithCulture(object objectToConvert)
{
if (objectToConvert == null) throw new ArgumentNullException("objectToConvert");
var t = objectToConvert.GetType();
var method = t.GetMethod("ToString", new[]
{
typeof(IFormatProvider)
});
if (method == null)
return objectToConvert.ToString();
return (string) method.Invoke(objectToConvert, new object[]
{
formatProviderField
});
}
}
/// <summary>
/// Helper to produce culture-oriented representation of an object as a string
/// </summary>
public ToStringInstanceHelper ToStringHelper { get; } = new ToStringInstanceHelper();
#endregion
}
#endregion
}

Просмотреть файл

@ -0,0 +1,16 @@
using Microsoft.Azure.TypeEdge.Description;
namespace Microsoft.Azure.TypeEdge.Host.Service
{
partial class Service
{
private readonly CodeGeneratorSettings _codeGeneratorSettings;
private readonly ServiceDescription _serviceDescription;
public Service(ServiceDescription serviceDescription, CodeGeneratorSettings codeGeneratorSettings)
{
_serviceDescription = serviceDescription;
_codeGeneratorSettings = codeGeneratorSettings;
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
using Microsoft.Azure.TypeEdge.Attributes;
using Microsoft.Azure.TypeEdge.Modules.Endpoints;
using Microsoft.Azure.TypeEdge.Twins;
namespace <#= _codeGeneratorSettings.Namespace #>
{
[TypeModule]
public interface I<#= _serviceDescription.Name #>
{
<# if(_serviceDescription.InputDescriptions!=null)
foreach (var endpoint in _serviceDescription.InputDescriptions)
{
#>
Input<<#= endpoint.TypeDescription.Name #>> <#= endpoint.Name #> { get; set; }
<#
}
#>
<# if(_serviceDescription.OutputDescriptions!=null)
foreach (var endpoint in _serviceDescription.OutputDescriptions)
{
#>
Output<<#= endpoint.TypeDescription.Name #>> <#= endpoint.Name #> { get; set; }
<#
}
#>
<# if(_serviceDescription.TwinDescriptions!=null)
foreach (var twin in _serviceDescription.TwinDescriptions)
{
#>
ModuleTwin<<#= twin.TypeDescription.Name #>> <#= twin.Name #> { get; set; }
<#
}
#>
<# if(_serviceDescription.DirectMethodDescriptions!=null)
foreach (var method in _serviceDescription.DirectMethodDescriptions)
{
string returnType = "void";
string arguments = "";
if(method.ReturnTypeDescription!=null)
returnType = method.ReturnTypeDescription.Name;
if(method.ArgumentsTypeDescription!=null)
arguments = string.Join( ",", method.ArgumentsTypeDescription.Select(e => $"{e.TypeDescription.Name} {e.Name}").ToArray());
#>
<#= returnType #> <#= method.Name #>(<#= arguments #>);
<#
}
#>
}
}

Просмотреть файл

@ -0,0 +1,81 @@
using System;
using System.IO;
using Microsoft.Azure.TypeEdge.Description;
namespace Microsoft.Azure.TypeEdge.Host.Service
{
public class ServiceGenerator
{
public static bool CreateFiles(ServiceDescription service)
{
return CreateFiles(service, new CodeGenerator().Generate);
}
public static bool CreateFiles(ServiceDescription service, Func<TypeDescription, string> codeGenerator)
{
var settings = CodeGeneratorSettings.Default;
settings.Namespace = service.Name + ".Proxy";
settings.OutputPath = "./" + settings.Namespace;
return CreateFiles(service, codeGenerator, settings);
}
public static bool CreateFiles(ServiceDescription serviceDescription,
Func<TypeDescription, string> codeGenerator,
CodeGeneratorSettings settings)
{
var service = new Service(serviceDescription, settings);
var serviceCode = service.TransformText();
if (!Directory.Exists(settings.OutputPath))
Directory.CreateDirectory(settings.OutputPath);
foreach (var endpoint in serviceDescription.InputDescriptions)
{
var code = codeGenerator(endpoint.TypeDescription);
if (!string.IsNullOrEmpty(code))
File.WriteAllText(Path.Combine(settings.OutputPath, endpoint.TypeDescription.Name + ".cs"), code);
}
foreach (var endpoint in serviceDescription.OutputDescriptions)
{
var code = codeGenerator(endpoint.TypeDescription);
if (!string.IsNullOrEmpty(code))
File.WriteAllText(Path.Combine(settings.OutputPath, endpoint.TypeDescription.Name + ".cs"), code);
}
foreach (var twin in serviceDescription.TwinDescriptions)
{
var code = codeGenerator(twin.TypeDescription);
if (!string.IsNullOrEmpty(code))
File.WriteAllText(Path.Combine(settings.OutputPath, twin.TypeDescription.Name + ".cs"), code);
}
foreach (var method in serviceDescription.DirectMethodDescriptions)
{
if (method.ReturnTypeDescription != null)
{
var code = codeGenerator(method.ReturnTypeDescription);
if (!string.IsNullOrEmpty(code))
File.WriteAllText(Path.Combine(settings.OutputPath, method.ReturnTypeDescription.Name + ".cs"),
code);
}
if (method.ArgumentsTypeDescription != null)
foreach (var arg in method.ArgumentsTypeDescription)
{
var code = codeGenerator(arg.TypeDescription);
if (!string.IsNullOrEmpty(code))
File.WriteAllText(Path.Combine(settings.OutputPath, arg.TypeDescription.Name + ".cs"),
code);
}
}
File.WriteAllText(Path.Combine(settings.OutputPath, $"I{serviceDescription.Name}.cs"), serviceCode);
return true;
}
}
}

Просмотреть файл

@ -53,6 +53,7 @@ namespace Microsoft.Azure.TypeEdge.Host
_deviceId = configuration.GetValue<string>("DeviceId");
_iotHubConnectionString = configuration.GetValue<string>("IotHubConnectionString");
if (string.IsNullOrEmpty(_iotHubConnectionString))
throw new Exception("Missing \"IotHubConnectionString\" value in configuration");
@ -449,7 +450,7 @@ namespace Microsoft.Azure.TypeEdge.Host
var registryManager = RegistryManager.CreateFromConnectionString(_iotHubConnectionString);
var device = await registryManager.GetDeviceAsync(_deviceId) ?? await registryManager.AddDeviceAsync(
new Device(_deviceId) { Capabilities = new DeviceCapabilities { IotEdge = true } });
new Device(_deviceId) {Capabilities = new DeviceCapabilities {IotEdge = true}});
var sasKey = device.Authentication.SymmetricKey.PrimaryKey;
try

Просмотреть файл

@ -0,0 +1,16 @@
using System;
namespace Microsoft.Azure.TypeEdge.Description
{
public class ArgumentDescription
{
public ArgumentDescription(string argumentName, Type type, Func<Type, string> schemaGenerator)
{
Name = argumentName;
TypeDescription = new TypeDescription(type, schemaGenerator);
}
public string Name { get; set; }
public TypeDescription TypeDescription { get; set; }
}
}

Просмотреть файл

@ -6,17 +6,17 @@ namespace Microsoft.Azure.TypeEdge.Description
{
public class DirectMethodDescription
{
public string Name { get; set; }
public string[] ArgumentsTypeDescription { get; set; }
public string ReturnTypeDescription { get; set; }
public DirectMethodDescription(MethodInfo mi, Func<Type, string> schemaGenerator)
{
Name = mi.Name;
if (mi.ReturnType != typeof(void))
ReturnTypeDescription = schemaGenerator(mi.ReturnType);
ArgumentsTypeDescription = mi.GetParameters().Select(p => schemaGenerator(p.ParameterType)).ToArray();
ReturnTypeDescription = new TypeDescription(mi.ReturnType, schemaGenerator);
ArgumentsTypeDescription = mi.GetParameters()
.Select(p => new ArgumentDescription(p.Name, p.ParameterType, schemaGenerator)).ToArray();
}
public string Name { get; set; }
public ArgumentDescription[] ArgumentsTypeDescription { get; set; }
public TypeDescription ReturnTypeDescription { get; set; }
}
}

Просмотреть файл

@ -4,14 +4,14 @@ namespace Microsoft.Azure.TypeEdge.Description
{
public class EndpointDescription
{
public string Name { get; }
public string TypeDescription { get; }
public EndpointDescription(string name, Type type, Func<Type, string> schemaGenerator)
{
Name = name;
TypeDescription = schemaGenerator(type);
TypeDescription = new TypeDescription(type, schemaGenerator);
}
public string Name { get; }
public TypeDescription TypeDescription { get; }
}
}

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using Newtonsoft.Json.Schema.Generation;
namespace Microsoft.Azure.TypeEdge.Description
{
public class SchemaGenerator
{
private readonly JSchemaGenerator JSchemaGenerator;
public SchemaGenerator()
{
JSchemaGenerator = new JSchemaGenerator();
}
public string Generate(Type type)
{
return JSchemaGenerator.Generate(type).ToString();
}
}
}

Просмотреть файл

@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Azure.TypeEdge.Description
namespace Microsoft.Azure.TypeEdge.Description
{
public class ServiceDescription
{
public string Name { get; set; }
public EndpointDescription[] InputDescriptions { get; set; }
public EndpointDescription[] OutputDescriptions { get; set; }
public TwinDescription TwinDescription { get; set; }
public TwinDescription[] TwinDescriptions { get; set; }
public DirectMethodDescription[] DirectMethodDescriptions { get; set; }
}
}
}

Просмотреть файл

@ -10,39 +10,63 @@ namespace Microsoft.Azure.TypeEdge.Description
{
public static class ServiceDescriptor
{
public static ServiceDescription Describe(Type type)
{
return Describe(type, new SchemaGenerator().Generate);
}
public static ServiceDescription Describe<T>()
where T : TypeModule
{
return Describe(typeof(T));
}
public static ServiceDescription Describe<T>(Func<Type, string> schemaGenerator)
where T : TypeModule
where T : TypeModule
{
return Describe(typeof(T), schemaGenerator);
}
public static ServiceDescription Describe(Type type, Func<Type, string> schemaGenerator)
{
return new ServiceDescription
{
InputDescriptions = GetEndpointDescription<T>(typeof(Input<>), schemaGenerator),
OutputDescriptions = GetEndpointDescription<T>(typeof(Output<>), schemaGenerator),
TwinDescription = GetTwinDescription<T>(typeof(ModuleTwin<>), schemaGenerator),
DirectMethodDescriptions = GetDirectMethodDescriptions<T>(schemaGenerator)
Name = type.Name,
InputDescriptions = GetEndpointDescription(type, typeof(Input<>), schemaGenerator),
OutputDescriptions = GetEndpointDescription(type, typeof(Output<>), schemaGenerator),
TwinDescriptions = GetTwinDescription(type, typeof(ModuleTwin<>), schemaGenerator),
DirectMethodDescriptions = GetDirectMethodDescriptions(type, schemaGenerator)
};
}
private static DirectMethodDescription[] GetDirectMethodDescriptions<T>(Func<Type, string> schemaGenerator)
private static DirectMethodDescription[] GetDirectMethodDescriptions(Type type,
Func<Type, string> schemaGenerator)
{
return typeof(T).GetProxyInterface().GetMethods().Where(m => !m.IsSpecialName).Select(e => new DirectMethodDescription(e, schemaGenerator)).ToArray();
return type.GetProxyInterface().GetMethods().Where(m => !m.IsSpecialName)
.Select(e => new DirectMethodDescription(e, schemaGenerator)).ToArray();
}
private static EndpointDescription[] GetEndpointDescription<T>(Type propertyType, Func<Type, string> schemaGenerator)
private static EndpointDescription[] GetEndpointDescription(Type type, Type propertyType,
Func<Type, string> schemaGenerator)
{
return GetPropertyInfos<T>(propertyType)
.Select(e => new EndpointDescription(e.Name, e.PropertyType.GenericTypeArguments[0], schemaGenerator)).ToArray();
return GetPropertyInfos(type, propertyType)
.Select(e => new EndpointDescription(e.Name, e.PropertyType.GenericTypeArguments[0], schemaGenerator))
.ToArray();
}
private static TwinDescription GetTwinDescription<T>(Type propertyType, Func<Type, string> schemaGenerator)
private static TwinDescription[] GetTwinDescription(Type type, Type propertyType,
Func<Type, string> schemaGenerator)
{
return new TwinDescription(GetPropertyInfos<T>(propertyType).Select(e => e.PropertyType.GenericTypeArguments[0]), schemaGenerator);
return GetPropertyInfos(type, propertyType)
.Select(e => new TwinDescription(e.Name, e.PropertyType.GenericTypeArguments[0], schemaGenerator))
.ToArray();
}
private static IEnumerable<PropertyInfo> GetPropertyInfos<T>(Type propertyType)
private static IEnumerable<PropertyInfo> GetPropertyInfos(Type type, Type propertyType)
{
return typeof(T).GetProperties().Where(e =>
return type.GetProperties().Where(e =>
e.PropertyType.IsGenericType &&
e.PropertyType.GetGenericTypeDefinition().IsAssignableFrom(propertyType));
}
}
}
}

Просмотреть файл

@ -1,32 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Azure.TypeEdge.Description
{
public class TwinDescription
{
public string TypeDescription { get; }
public TwinDescription(IEnumerable<Type> types, Func<Type, string> schemaGenerator)
public TwinDescription(string name, Type type, Func<Type, string> schemaGenerator)
{
if (types == null || !types.Any())
return;
var mergeSettings = new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
};
var typeJObjects = types.Select(e => JObject.Parse(schemaGenerator(e))).ToArray();
var union = typeJObjects[0];
for (var i = 0; i < typeJObjects.Length-1; i++)
union.Merge(typeJObjects[i + 1], mergeSettings);
TypeDescription = union.ToString(Formatting.None);
Name = name;
TypeDescription = new TypeDescription(type, schemaGenerator);
}
public string Name { get; }
public TypeDescription TypeDescription { get; }
//public TwinDescription(IEnumerable<Type> types, Func<Type, string> schemaGenerator)
//{
// if (types == null || !types.Any())
// return;
// var mergeSettings = new JsonMergeSettings
// {
// MergeArrayHandling = MergeArrayHandling.Union
// };
// var typeJObjects = types.Select(e => JObject.Parse(schemaGenerator(e))).ToArray();
// var union = typeJObjects[0];
// for (var i = 0; i < typeJObjects.Length-1; i++)
// union.Merge(typeJObjects[i + 1], mergeSettings);
// TypeDescription = union.ToString(Formatting.None);
//}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
using System;
namespace Microsoft.Azure.TypeEdge.Description
{
public class TypeDescription
{
public TypeDescription(Type type, Func<Type, string> schemaGenerator)
{
Name = type.Name;
Description = schemaGenerator(type);
}
public string Name { get; set; }
public string Description { get; set; }
}
}

Просмотреть файл

@ -33,13 +33,10 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.10" />
<PackageReference Include="Serilog" Version="2.7.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="2.1.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="NewFolder\" />
</ItemGroup>
</Project>

Просмотреть файл

@ -38,7 +38,6 @@ namespace Microsoft.Azure.TypeEdge.Modules.Endpoints
_volume.Delete(fileName);
return res;
});
Module.SubscribeRoute(output.Name,

Просмотреть файл

@ -6,7 +6,7 @@ namespace Microsoft.Azure.TypeEdge.Modules.Messages
{
public abstract class EdgeMessage : IEdgeMessage
{
public IDictionary<string, string> Properties { get; set; }
[JsonIgnore] public IDictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
public byte[] GetBytes()
{

Просмотреть файл

@ -138,7 +138,8 @@ namespace Microsoft.Azure.TypeEdge.Modules
Logger.LogWarning($"Missing {Constants.EdgeHubConnectionStringKey} variable.");
var settings = new AmqpTransportSettings(TransportType.Amqp_Tcp_Only);
var disableSslCertificateValidationKey = configuration.GetValue($"{Constants.DisableSslCertificateValidationKey}", false);
var disableSslCertificateValidationKey =
configuration.GetValue($"{Constants.DisableSslCertificateValidationKey}", false);
if (disableSslCertificateValidationKey)
settings.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
@ -211,10 +212,7 @@ namespace Microsoft.Azure.TypeEdge.Modules
var volumePath = volumeName.ToLower();
if (!Directory.Exists(volumePath))
{
Directory.CreateDirectory(volumePath);
}
if (!Directory.Exists(volumePath)) Directory.CreateDirectory(volumePath);
Volumes[volumeName] = volumePath;
}
@ -416,6 +414,9 @@ namespace Microsoft.Azure.TypeEdge.Modules
input.SetBytes(messageBytes);
foreach (var messageProperty in message.Properties)
input.Properties.Add(messageProperty.Key, messageProperty.Value);
var invocationResult = callback.Handler.DynamicInvoke(input);
var result = await (Task<MessageResult>) invocationResult;

Просмотреть файл

@ -14,7 +14,9 @@ using Microsoft.Azure.TypeEdge.Proxy;
using Microsoft.Azure.TypeEdge.Volumes;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using static System.String;
using ServiceDescriptor = Microsoft.Azure.TypeEdge.Description.ServiceDescriptor;
namespace Microsoft.Azure.TypeEdge
{
@ -43,14 +45,12 @@ namespace Microsoft.Azure.TypeEdge
if (IsNullOrEmpty(moduleName))
{
Console.WriteLine($"WARN:No {Constants.ModuleNameConfigName} in configuration. ");
moduleName = DiscoverModuleName();
if (IsNullOrEmpty(moduleName))
{
Console.WriteLine($"WARN:No {Constants.ModuleNameConfigName} in configuration. ");
Console.WriteLine("Exiting...");
return;
//throw new ArgumentException($"No moduleName in arguments");
}
}
@ -66,6 +66,13 @@ namespace Microsoft.Azure.TypeEdge
if (moduleType == null)
throw new ArgumentException($"No module called {moduleName} in calling assembly");
if (configuration.GetValue("metadata", false))
{
var description = ServiceDescriptor.Describe(moduleType);
Console.WriteLine(JsonConvert.SerializeObject(description, Formatting.Indented));
return;
}
var connectionString = configuration.GetValue<string>($"{Constants.EdgeHubConnectionStringKey}");
if (IsNullOrEmpty(connectionString))
@ -105,9 +112,9 @@ namespace Microsoft.Azure.TypeEdge
var moduleDependencies = moduleType.GetConstructors().First().GetParameters();
var moduleDependencyTypes = moduleDependencies.Where(i => i.ParameterType.IsInterface &&
i.ParameterType.GetCustomAttribute(
typeof(TypeModuleAttribute),
true) != null).Select(e => e.ParameterType);
i.ParameterType.GetCustomAttribute(
typeof(TypeModuleAttribute),
true) != null).Select(e => e.ParameterType);
var proxyGenerator = new ProxyGenerator();
foreach (var dependency in moduleDependencyTypes)
@ -171,6 +178,6 @@ namespace Microsoft.Azure.TypeEdge
var moduleInterfaceType = moduleType.GetProxyInterface();
moduleTypes = (moduleType, moduleInterfaceType);
return true;
}
}
}
}