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);
            }
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
                }
        }
Exemple #4
0
        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);
            }
        }
Exemple #5
0
        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);
        }
Exemple #6
0
        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);
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        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);
                }
        }