public void Compile(IReadOnlyCollection <AbstractFunctionDefinition> functionDefinitions, OpenApiOutputModel openApiOutputModel, string outputBinaryFolder, string outputNamespaceName) { HandlebarsHelperRegistration.RegisterHelpers(); foreach (AbstractFunctionDefinition functionDefinition in functionDefinitions) { string templateSource = _templateProvider.GetJsonTemplate(functionDefinition); Func <object, string> template = Handlebars.Compile(templateSource); functionDefinition.AssemblyName = $"{outputNamespaceName}.dll"; functionDefinition.FunctionClassTypeName = $"{functionDefinition.Namespace}.{functionDefinition.Name}"; string json = template(functionDefinition); WriteFunctionTemplate(outputBinaryFolder, functionDefinition.Name, json); } if (openApiOutputModel != null && openApiOutputModel.IsConfiguredForUserInterface) { string templateSource = _templateProvider.GetTemplate("swaggerui", "json"); Func <object, string> template = Handlebars.Compile(templateSource); string json = template(new { AssemblyName = $"{outputNamespaceName}.dll", Namespace = outputNamespaceName }); WriteFunctionTemplate(outputBinaryFolder, "OpenApiProvider", json); } }
public void Compile() { IFunctionAppConfiguration configuration = ConfigurationLocator.FindConfiguration(_configurationSourceAssembly); if (configuration == null) { throw new ConfigurationException($"The assembly {_configurationSourceAssembly.GetName().Name} does not contain a public class implementing the IFunctionAppConfiguration interface"); } string newAssemblyNamespace = $"{_configurationSourceAssembly.GetName().Name}.Functions"; FunctionHostBuilder builder = new FunctionHostBuilder(_serviceCollection, _commandRegistry, false); configuration.Build(builder); new PostBuildPatcher().Patch(builder, newAssemblyNamespace); VerifyCommandAndResponseTypes(builder); IReadOnlyCollection <string> externalAssemblies = GetExternalAssemblyLocations(builder.FunctionDefinitions); OpenApiOutputModel openApi = _openApiCompiler.Compile(builder.OpenApiConfiguration, builder.FunctionDefinitions, _outputBinaryFolder); _jsonCompiler.Compile(builder.FunctionDefinitions, openApi, _outputBinaryFolder, newAssemblyNamespace); if (_outputProxiesJson && builder.AreProxiesEnabled) { _proxiesJsonCompiler.Compile(builder.FunctionDefinitions, builder.OpenApiConfiguration, openApi, _outputBinaryFolder); } _assemblyCompiler.Compile(builder.FunctionDefinitions, configuration.GetType(), newAssemblyNamespace, externalAssemblies, _outputBinaryFolder, $"{newAssemblyNamespace}.dll", openApi, _target, builder.OutputAuthoredSourceFolder); }
public void Compile(IReadOnlyCollection <AbstractFunctionDefinition> functionDefinitions, OpenApiConfiguration openApiConfiguration, OpenApiOutputModel openApiOutputModel, string outputBinaryFolder) { HandlebarsHelperRegistration.RegisterHelpers(); HttpFunctionDefinition[] httpFunctionDefinitions = functionDefinitions .OfType <HttpFunctionDefinition>() .Where(x => !string.IsNullOrWhiteSpace(x.Route)) .ToArray(); if (!httpFunctionDefinitions.Any()) { return; } List <object> proxyDefinitions = new List <object>(httpFunctionDefinitions); var isYamlOutput = openApiConfiguration.ApiOutputFormat == ApiOutputFormat.Yaml; if (openApiConfiguration.IsOpenApiOutputEnabled) { proxyDefinitions.Add(new { Name = "OpenApiDefinitionRoute", Route = "/openapi." + (isYamlOutput ? "yaml" : "json"), IsOpenApiYaml = isYamlOutput, IsOpenApiJson = !isYamlOutput }); Debug.Assert(openApiOutputModel != null); if (openApiOutputModel.IsConfiguredForUserInterface) { proxyDefinitions.Add(new { Name = "OpenApiProvider", Route = openApiConfiguration.UserInterfaceRoute + "/{name}", IsOpenApiUi = true }); } } string templateSource = _templateProvider.GetProxiesJsonTemplate(); Func <object, string> template = Handlebars.Compile(templateSource); string json = template(proxyDefinitions); DirectoryInfo folder = Directory.CreateDirectory(Path.Combine(outputBinaryFolder, "..")); string filename = Path.Combine(folder.FullName, "proxies.json"); using (Stream stream = new FileStream(filename, FileMode.Create)) using (StreamWriter writer = new StreamWriter(stream)) { writer.Write(json); } }
public void Compile(IReadOnlyCollection <AbstractFunctionDefinition> functionDefinitions, OpenApiOutputModel openApiOutputModel, string outputBinaryFolder, string outputNamespaceName) { HandlebarsHelperRegistration.RegisterHelpers(); foreach (AbstractFunctionDefinition functionDefinition in functionDefinitions) { string templateSource = _templateProvider.GetJsonTemplate(functionDefinition); Func <object, string> template = Handlebars.Compile(templateSource); functionDefinition.AssemblyName = $"{outputNamespaceName}.dll"; functionDefinition.FunctionClassTypeName = $"{functionDefinition.Namespace}.{functionDefinition.Name}"; string json = template(functionDefinition); WriteFunctionTemplate(outputBinaryFolder, functionDefinition.Name, json); if (functionDefinition is CosmosDbFunctionDefinition cosmosDbFunctionDefinition) { if (cosmosDbFunctionDefinition.TrackRemainingWork) { TimerFunctionDefinition cosmosMonitorDefinition = new TimerFunctionDefinition(functionDefinition.CommandType) { AssemblyName = cosmosDbFunctionDefinition.AssemblyName, CommandDeserializerType = null, CommandType = null, CronExpression = cosmosDbFunctionDefinition.RemainingWorkCronExpression, FunctionClassTypeName = $"{functionDefinition.Namespace}.Monitor{functionDefinition.Name}" }; string timerTemplateSource = _templateProvider.GetJsonTemplate(cosmosMonitorDefinition); Func <object, string> timerTemplate = Handlebars.Compile(timerTemplateSource); string timerJson = timerTemplate(cosmosMonitorDefinition); WriteFunctionTemplate(outputBinaryFolder, $"Monitor{functionDefinition.Name}", timerJson); } } } if (openApiOutputModel != null && openApiOutputModel.IsConfiguredForUserInterface) { string templateSource = _templateProvider.GetTemplate("swaggerui", "json"); Func <object, string> template = Handlebars.Compile(templateSource); string json = template(new { AssemblyName = $"{outputNamespaceName}.dll", Namespace = outputNamespaceName }); WriteFunctionTemplate(outputBinaryFolder, "OpenApiProvider", json); } }
private List <SyntaxTree> CompileSource(IReadOnlyCollection <AbstractFunctionDefinition> functionDefinitions, OpenApiOutputModel openApiOutputModel, Type functionAppConfigurationType, string newAssemblyNamespace, string outputAuthoredSourceFolder) { List <SyntaxTree> syntaxTrees = new List <SyntaxTree>(); DirectoryInfo directoryInfo = outputAuthoredSourceFolder != null ? new DirectoryInfo(outputAuthoredSourceFolder) : null; if (directoryInfo != null && !directoryInfo.Exists) { directoryInfo = null; } foreach (AbstractFunctionDefinition functionDefinition in functionDefinitions) { string templateSource = _templateProvider.GetCSharpTemplate(functionDefinition); AddSyntaxTreeFromHandlebarsTemplate(templateSource, functionDefinition.Name, functionDefinition, directoryInfo, syntaxTrees); } if (openApiOutputModel != null && openApiOutputModel.IsConfiguredForUserInterface) { string templateSource = _templateProvider.GetTemplate("swaggerui", "csharp"); AddSyntaxTreeFromHandlebarsTemplate(templateSource, "SwaggerUi", new { Namespace = newAssemblyNamespace }, directoryInfo, syntaxTrees); } // Now we need to create a class that references the assembly with the configuration builder // otherwise the reference will be optimised away by Roslyn and it will then never get loaded // by the function host - and so at runtime the builder with the runtime info in won't be located string linkBackTemplateSource = _templateProvider.GetCSharpLinkBackTemplate(); Func <object, string> linkBackTemplate = Handlebars.Compile(linkBackTemplateSource); LinkBackModel linkBackModel = new LinkBackModel { ConfigurationTypeName = functionAppConfigurationType.EvaluateType(), Namespace = newAssemblyNamespace }; string outputLinkBackCode = linkBackTemplate(linkBackModel); OutputDiagnosticCode(directoryInfo, "ReferenceLinkBack", outputLinkBackCode); SyntaxTree linkBackSyntaxTree = CSharpSyntaxTree.ParseText(outputLinkBackCode); syntaxTrees.Add(linkBackSyntaxTree); return(syntaxTrees); }
public void Compile(IReadOnlyCollection <AbstractFunctionDefinition> functionDefinitions, Type functionAppConfigurationType, string newAssemblyNamespace, IReadOnlyCollection <string> externalAssemblyLocations, string outputBinaryFolder, string assemblyName, OpenApiOutputModel openApiOutputModel, string outputAuthoredSourceFolder) { HandlebarsHelperRegistration.RegisterHelpers(); IReadOnlyCollection <SyntaxTree> syntaxTrees = CompileSource(functionDefinitions, openApiOutputModel, functionAppConfigurationType, newAssemblyNamespace, outputAuthoredSourceFolder); CompileAssembly(syntaxTrees, externalAssemblyLocations, openApiOutputModel, outputBinaryFolder, assemblyName, newAssemblyNamespace); }
public OpenApiOutputModel Compile(OpenApiConfiguration configuration, IReadOnlyCollection <AbstractFunctionDefinition> abstractFunctionDefinitions, string outputBinaryFolder) { string apiPrefix = GetApiPrefix(outputBinaryFolder); if (!configuration.IsValid) { throw new ConfigurationException("Open API implementation is partially complete, a title and a version must be specified"); } if (!configuration.IsOpenApiOutputEnabled) { return(null); } HttpFunctionDefinition[] functionDefinitions = abstractFunctionDefinitions.OfType <HttpFunctionDefinition>().ToArray(); if (functionDefinitions.Length == 0) { return(null); } OpenApiDocument openApiDocument = new OpenApiDocument { Info = new OpenApiInfo { Version = configuration.Version, Title = configuration.Title }, Servers = configuration.Servers?.Select(x => new OpenApiServer { Url = x }).ToArray(), Paths = new OpenApiPaths(), Components = new OpenApiComponents { Schemas = new Dictionary <string, OpenApiSchema>() } }; SchemaReferenceRegistry registry = new SchemaReferenceRegistry(); CreateTags(functionDefinitions, openApiDocument); CreateSchemas(functionDefinitions, openApiDocument, registry); CreateOperationsFromRoutes(functionDefinitions, openApiDocument, registry, apiPrefix); if (openApiDocument.Paths.Count == 0) { return(null); } string yaml = openApiDocument.Serialize(OpenApiSpecVersion.OpenApi3_0, OpenApiFormat.Yaml); OpenApiOutputModel result = new OpenApiOutputModel { OpenApiSpecification = new OpenApiFileReference { Content = yaml, Filename = "openapi.yaml" } }; if (!string.IsNullOrWhiteSpace(configuration.UserInterfaceRoute)) { result.SwaggerUserInterface = CopySwaggerUserInterfaceFilesToWebFolder(); } return(result); }
private void CompileAssembly(IReadOnlyCollection <SyntaxTree> syntaxTrees, IReadOnlyCollection <string> externalAssemblyLocations, OpenApiOutputModel openApiOutputModel, string outputBinaryFolder, string outputAssemblyName, string assemblyNamespace) { IReadOnlyCollection <string> locations = BuildCandidateReferenceList(externalAssemblyLocations); const string manifestResourcePrefix = "FunctionMonkey.Compiler.references.netstandard2._0."; // For each assembly we've found we need to check and see if it is already included in the output binary folder // If it is then its referenced already by the function host and so we add a reference to that version. List <string> resolvedLocations = ResolveLocationsWithExistingReferences(outputBinaryFolder, locations); string[] manifestResoureNames = GetType().Assembly.GetManifestResourceNames() .Where(x => x.StartsWith(manifestResourcePrefix)) .Select(x => x.Substring(manifestResourcePrefix.Length)) .ToArray(); List <PortableExecutableReference> references = BuildReferenceSet(resolvedLocations, manifestResoureNames, manifestResourcePrefix); CSharpCompilation compilation = CSharpCompilation.Create(outputAssemblyName, syntaxTrees, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); List <ResourceDescription> resources = null; if (openApiOutputModel != null) { resources = new List <ResourceDescription>(); Debug.Assert(openApiOutputModel.OpenApiSpecification != null); resources.Add(new ResourceDescription($"{assemblyNamespace}.OpenApi.{openApiOutputModel.OpenApiSpecification.Filename}", () => new MemoryStream(Encoding.UTF8.GetBytes(openApiOutputModel.OpenApiSpecification.Content)), true)); if (openApiOutputModel.SwaggerUserInterface != null) { foreach (OpenApiFileReference fileReference in openApiOutputModel.SwaggerUserInterface) { OpenApiFileReference closureCapturedFileReference = fileReference; resources.Add(new ResourceDescription($"{assemblyNamespace}.OpenApi.{closureCapturedFileReference.Filename}", () => new MemoryStream(Encoding.UTF8.GetBytes(closureCapturedFileReference.Content)), true)); } } } using (Stream stream = new FileStream(Path.Combine(outputBinaryFolder, outputAssemblyName), FileMode.Create)) { EmitResult result = compilation.Emit(stream, manifestResources: resources); if (!result.Success) { IEnumerable <Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); StringBuilder messageBuilder = new StringBuilder(); foreach (Diagnostic diagnostic in failures) { messageBuilder.AppendFormat("{0}:{1} {2}", diagnostic.Id, diagnostic.GetMessage(), diagnostic.Location.ToString()); } throw new ConfigurationException(messageBuilder.ToString()); } } }
public void Compile(IReadOnlyCollection <AbstractFunctionDefinition> functionDefinitions, OpenApiConfiguration openApiConfiguration, OpenApiOutputModel openApiOutputModel, string outputBinaryFolder) { HandlebarsHelperRegistration.RegisterHelpers(); HttpFunctionDefinition[] httpFunctionDefinitions = functionDefinitions .OfType <HttpFunctionDefinition>() .Where(x => !string.IsNullOrWhiteSpace(x.Route)) .ToArray(); if (!httpFunctionDefinitions.Any()) { return; } List <object> proxyDefinitions = new List <object>(httpFunctionDefinitions); if (openApiConfiguration.IsOpenApiOutputEnabled) { proxyDefinitions.Add(new { Name = "OpenApiYaml", Route = "/openapi.yaml", IsOpenApiYaml = true, }); Debug.Assert(openApiOutputModel != null); if (openApiOutputModel.IsConfiguredForUserInterface) { // The commented out code will do an explicit proxy per Swagger file // It goes wit the proxies.explicit.json.handlebars template /*StringBuilder proxyBuilder = new StringBuilder(); * int index = 0; * foreach (OpenApiFileReference reference in openApiOutputModel.SwaggerUserInterface) * { * proxyBuilder.AppendLine( * $",\"OpenAPIUI{index}\": {{\"matchCondition\":{{\"route\":\"/openapi/{reference.Filename}\",\"methods\":[\"GET\"]}},\"backendUri\":\"https://localhost/api/OpenApiProvider?name={reference.Filename}\"}}"); * * index++; * } * * proxyDefinitions.Add(new * { * IsOpenApiUi = true, * OpenApiUiProxies = proxyBuilder.ToString() * });*/ proxyDefinitions.Add(new { Name = "OpenApiProvider", Route = openApiConfiguration.UserInterfaceRoute + "/{name}", IsOpenApiUi = true }); } } string templateSource = _templateProvider.GetProxiesJsonTemplate(); Func <object, string> template = Handlebars.Compile(templateSource); string json = template(proxyDefinitions); DirectoryInfo folder = Directory.CreateDirectory(Path.Combine(outputBinaryFolder, "..")); string filename = Path.Combine(folder.FullName, "proxies.json"); using (Stream stream = new FileStream(filename, FileMode.Create)) using (StreamWriter writer = new StreamWriter(stream)) { writer.Write(json); } }