public HostJsonFile(string filePath) { var fileText = Helpers.SafelyReadFromFile(filePath); Data = JsonConvert.DeserializeObject <HostJson>(fileText, _settings); }
public FunctionsCSharpFile(string file, HostJson hostData) { Name = Path.GetFileNameWithoutExtension(file); FileName = file; var fileText = Helpers.SafelyReadFromFile(file); Syntax = CSharpSyntaxTree.ParseText(fileText, new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.None, SourceCodeKind.Regular)); Context = new GenerationContext(); Root = Syntax.GetRoot() as CompilationUnitSyntax; var usingStatements = Root.DescendantNodes().OfType <UsingDirectiveSyntax>().ToList(); Regex allowedUsings; Regex unallowedUsings; if (Settings.AllowedNamespaces?.Any() ?? false) { allowedUsings = new Regex($"({string.Join("|", Settings.AllowedNamespaces)})"); } else { allowedUsings = new Regex($"(.+)"); } if (Settings.ExcludedNamespaces?.Any() ?? false) { unallowedUsings = new Regex($"({string.Join("|", Settings.ExcludedNamespaces)})"); } else { unallowedUsings = new Regex($"(^[.]+)"); } Context.UsingStatements = usingStatements.Select(x => x.WithoutLeadingTrivia().WithoutTrailingTrivia().ToFullString()) .Where(x => allowedUsings.IsMatch(x) && !unallowedUsings.IsMatch(x)) .ToList(); var namespaceDeclarations = Root.DescendantNodes().OfType <NamespaceDeclarationSyntax>().ToList(); foreach (var nsd in namespaceDeclarations) { var classDeclarations = nsd.DescendantNodes().OfType <ClassDeclarationSyntax>().ToList(); foreach (var cd in classDeclarations) { var methods = cd.DescendantNodes().OfType <MethodDeclarationSyntax>() .Where(x => x.Modifiers.Any(y => y.Text == "public") && x.Modifiers.Any(y => y.Text == "static")) .ToList(); foreach (var method in methods) { Context.Functions.Add(ClassParser.ReadMethodAsFunction(method, hostData)); } } } }
public static FunctionEndpoint ReadMethodAsFunction(MethodDeclarationSyntax syntax, HostJson hostData) { var attributes = syntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList(); var endpoint = new FunctionEndpoint(); try { var endpointName = attributes.GetAttribute <FunctionNameAttribute>(); if (endpointName == null) { endpoint.Ignored = true; return(endpoint); } endpoint.Name = endpointName.GetAttributeValue(); //Ignore generator attribute endpoint.Ignored = attributes.HasAttribute <NotGeneratedAttribute>(); //Obsolete Attribute var obsoleteAttribute = attributes.GetAttribute <ObsoleteAttribute>(); if (obsoleteAttribute != null) { endpoint.Obsolete = true; endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); } //Response types var responseTypes = attributes.GetAttributes <ProducesResponseTypeAttribute>(); var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); responses.Add(new ResponseTypeDefinition(true)); endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse <HttpStatusCode>(x.StatusValue))).ToList(); var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList(); if (duplicateResponseTypes.Any()) { throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}"); } //Add after so we don't get duplicate error from the null Status endpoint.ResponseTypes.Add(new ExceptionResponseType()); //Need to check if the function has a HttpTrigger var httpTriggerAttribute = syntax.ParameterList.Parameters.SingleOrDefault(x => x.AttributeLists.SelectMany(y => y.Attributes).HasAttribute <HttpTriggerAttribute>()); if (httpTriggerAttribute == null) { endpoint.Ignored = true; return(endpoint); } var triggerAttribute = new HttpTriggerParameter(httpTriggerAttribute); endpoint.SupportedMethods = triggerAttribute.Methods; var routePrefix = hostData?.http?.routePrefix ?? "api"; if (triggerAttribute.Route != null) { var route = triggerAttribute.Route.TrimStart('/'); if (!string.IsNullOrEmpty(routePrefix)) { if (!route.StartsWith(routePrefix)) { route = $"{routePrefix}/" + route; } route = "/" + route; } endpoint.Route = new HttpRoute(route); } else { if (!string.IsNullOrEmpty(routePrefix)) { endpoint.Route = new HttpRoute($"{routePrefix}/{endpoint.Name}"); } else { endpoint.Route = new HttpRoute($"{endpoint.Name}"); } } var expectedBodyParameters = attributes.GetAttributes <ExpectedBodyParameterAttribute>() .Select(x => new ExpectedBodyParamterDefinition(x)) .GroupBy(x => x.Method) .ToDictionary(x => x.Key, y => y.Select(z => (IParameter) new BodyParameter("body", z.Type, null))); var expectedQueryParameters = attributes.GetAttributes <ExpectedQueryParameterAttribute>() .Select(x => new ExpectedQueryParamterDefinition(x)) .GroupBy(x => x.Method) .ToDictionary(x => x.Key, y => y.Select(z => (IParameter) new QueryParameter(z.Name, z.Type, null, z.IsQueryObject))); endpoint.HttpParameters = expectedBodyParameters.Union(expectedQueryParameters).ToDictionary(); var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute())).ToList(); var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList(); endpoint.Parameters = routeParams.Cast <IParameter>().NotNull().ToList(); endpoint.Parameters.Add(new CancellationTokenModifier()); endpoint.Parameters.Add(new CookieModifier()); endpoint.Parameters.Add(new HeadersModifier()); endpoint.Parameters.Add(new TimeoutModifier()); if (triggerAttribute.AuthLevel == AuthorizationLevel.User) { if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized)) { endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized)); } endpoint.Parameters.Add(new SecurityModifier()); } else if (triggerAttribute.AuthLevel == AuthorizationLevel.Anonymous) { } else { if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized)) { endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized)); } endpoint.Parameters.Add(new FunctionAuthModifier()); } var parameterHeaders = attributes.GetAttributes <HeaderParameterAttribute>() .Select(x => new ParameterHeaderDefinition(x)) .ToList(); endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); var headers = attributes.GetAttributes <IncludeHeaderAttribute>() .Select(x => new HeaderDefinition(x)) .ToList(); endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); var rawReturnType = syntax.ReturnType?.ToFullString(); var returnType = Helpers.GetTypeFromString(rawReturnType.Trim()); while (returnType.IsContainerReturnType()) { returnType = returnType.Arguments.SingleOrDefault(); } if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name)) { returnType = null; } if (returnType?.Name == "void" || (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false))) { returnType = null; } if (returnType.IsFileReturnType()) { returnType = new Helpers.TypeString(typeof(Stream).FullName); endpoint.ReturnsStream = true; } rawReturnType = returnType?.ToString(); endpoint.ReturnType = rawReturnType?.Trim(); foreach (var method in endpoint.SupportedMethods) { var duplicateParameters = endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); if (duplicateParameters.Any()) { throw new NotSupportedException($"Function has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); } var invalidParameters = endpoint.GetParametersForHttpMethod(method).Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); if (invalidParameters.Any()) { throw new NotSupportedException($"Function {endpoint.Name} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); } } } catch (NotSupportedException nse) { if (endpoint.Ignored) { return(endpoint); } endpoint.Failed = true; endpoint.Error = nse.Message; } #if !DEBUG catch (Exception ex) { endpoint.Failed = true; endpoint.UnexpectedFailure = true; endpoint.Error = ex.ToString(); } #endif return(endpoint); }
public HostJsonFile(string filePath) { var fileText = Helpers.SafelyReadFromFile(filePath); Data = Helpers.DeserializeFromJson <HostJson>(fileText); }