public static ApiVersion CreateApiVersionFromDefinition(ApiVersionDefinition definition, string organizationId, string projectName) { return(new ApiVersion { ApiVersionValue = definition.ApiVersion, Description = definition.Description, Deprecated = definition.Deprecated, ProjectId = projectName, OrganizationId = organizationId }); }
public static AspNetCoreHttpController ReadClassAsHttpController(ClassDeclarationSyntax syntax) { var attributes = syntax.AttributeLists.SelectMany(x => x.Attributes).ToList(); var controller = new AspNetCoreHttpController(); try { controller.Name = $@"{syntax.Identifier.ValueText.Trim().Replace("Controller", "")}"; controller.Abstract = syntax.Modifiers.Any(x => x.Text == "abstract"); if (syntax.BaseList == null) { controller.Ignored = true; return(controller); } controller.BaseClass = syntax.BaseList.Types.Where(x => x.ToFullString().Trim().EndsWith("Controller")).SingleOrDefault()?.ToFullString().Trim().Replace("Controller", ""); controller.Ignored = attributes.HasAttribute <NotGeneratedAttribute>(); var namespaceAttribute = attributes.GetAttribute <NamespaceSuffixAttribute>(); if (namespaceAttribute != null) { controller.NamespaceSuffix = namespaceAttribute.GetAttributeValue(); } var routeAttribute = attributes.GetAttribute <RouteAttribute>(); if (routeAttribute != null) //Fetch route from RouteAttribute { controller.Route = new HttpRoute(routeAttribute.GetAttributeValue()); } if (controller.Route == null && !controller.Abstract && !controller.Ignored) //No Route, invalid controller { controller.Ignored = true; throw new NotSupportedException("Controller must have a route to be valid for generation."); } if (controller.Route != null) { var match = RouteVersionRegex.Match(controller.Route.Value); if (match.Success) { var group = match.Groups[1]; controller.NamespaceVersion = group.Value.ToUpper(); } } else { controller.Route = new HttpRoute(string.Empty); } var versionAttribute = attributes.GetAttribute <ApiVersionAttribute>(); if (versionAttribute != null) { var version = new ApiVersionDefinition(versionAttribute); controller.NamespaceVersion = $"V{version.Version.Replace(".", "_")}"; var versionConstraint = controller.Route.Constraints.OfType <ApiVersionContraint>().SingleOrDefault(); if (versionConstraint != null) { controller.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, false); } else { controller.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, true); } } //Response types var responseTypes = attributes.GetAttributes <ProducesResponseTypeAttribute>(); var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); controller.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse <HttpStatusCode>(x.StatusValue))).ToList(); var parameterHeaders = attributes.GetAttributes <HeaderParameterAttribute>() .Select(x => new ParameterHeaderDefinition(x)) .ToList(); controller.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); var headers = attributes.GetAttributes <IncludeHeaderAttribute>() .Select(x => new HeaderDefinition(x)) .ToList(); controller.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); //Authorize Attribute controller.IsSecured = attributes.HasAttribute <AuthorizeAttribute>(); //Obsolete Attribute var obsoleteAttribute = attributes.GetAttribute <ObsoleteAttribute>(); if (obsoleteAttribute != null) { controller.Obsolete = true; controller.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); } //Only public endpoints can be hit anyways var methods = syntax.DescendantNodes().OfType <MethodDeclarationSyntax>() .Where(x => x.Modifiers.Any(y => y.Text == "public")) .ToList(); controller.Endpoints = methods.Select(x => ReadMethodAsHttpEndpoint(controller, x)).ToList(); if (!controller.Endpoints.Any(x => !x.Ignored) && controller.Endpoints.Count > 0 && !controller.Abstract) { controller.Ignored = true; } } catch (NotSupportedException nse) { if (controller.Ignored) { return(controller); } controller.Failed = true; controller.Error = nse.Message; } #if !DEBUG catch (Exception ex) { controller.Failed = true; controller.UnexpectedFailure = true; controller.Error = ex.ToString(); } #endif return(controller); }
private static AspNetCoreHttpEndpoint ReadMethodAsHttpEndpoint(AspNetCoreHttpController parent, MethodDeclarationSyntax syntax) { var attributes = syntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList(); var endpoint = new AspNetCoreHttpEndpoint(parent); endpoint.Name = syntax.Identifier.ValueText.Trim(); endpoint.FormattedName = syntax.Identifier.ValueText.CleanMethodName(); endpoint.Virtual = syntax.Modifiers.Any(x => x.Text == "virtual"); endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override"); endpoint.New = syntax.Modifiers.Any(x => x.Text == "new"); //Ignore generator attribute endpoint.Ignored = attributes.HasAttribute <NotGeneratedAttribute>(); //Route Attribute var routeAttribute = attributes.GetAttribute <RouteAttribute>(); if (routeAttribute != null) //Fetch route from RouteAttribute { endpoint.Route = new HttpRoute(routeAttribute.GetAttributeValue()); } //HTTP Attribute var knownHttpAttributes = new List <string> { $"{Constants.Http}{HttpAttributeType.Delete}", $"{Constants.Http}{HttpAttributeType.Get}", $"{Constants.Http}{HttpAttributeType.Patch}", $"{Constants.Http}{HttpAttributeType.Post}", $"{Constants.Http}{HttpAttributeType.Put}", }; var httpAttribute = attributes.SingleOrDefault(x => knownHttpAttributes.Any(y => x.Name.ToFullString().MatchesAttribute(y))); if (httpAttribute == null) { endpoint.Ignored = true; } else { var httpType = (HttpAttributeType)Enum.Parse(typeof(HttpAttributeType), httpAttribute.Name .ToFullString() .Replace(Constants.Http, "") .Replace(Constants.Attribute, "")); endpoint.HttpType = Helpers.HttpMethodFromEnum(httpType); } if (endpoint.Route == null && httpAttribute?.ArgumentList != null) //If Route was never fetched from RouteAttribute or if they used the Http(template) override { endpoint.Route = new HttpRoute(httpAttribute.GetAttributeValue()); } //Ignore method if it doesn't have a route or http attribute if (endpoint.Route == null && httpAttribute == null) { endpoint.Ignored = true; return(endpoint); } if (endpoint.Route == null) { endpoint.Route = new HttpRoute(string.Empty); } var versionAttribute = attributes.GetAttribute <ApiVersionAttribute>(); if (versionAttribute != null) { var version = new ApiVersionDefinition(versionAttribute); var versionConstraint = endpoint.Route.Constraints.OfType <ApiVersionContraint>().SingleOrDefault(); if (versionConstraint != null) { endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, false); } else { endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, true); } } if (endpoint.Route.Version != null && parent.Route.Version != null) { throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has {nameof(ApiVersionAttribute)} on both it's method and class"); } //Obsolete Attribute var obsoleteAttribute = attributes.GetAttribute <ObsoleteAttribute>(); if (obsoleteAttribute != null) { endpoint.Obsolete = true; endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); } //Authorize Attribute endpoint.IsSecured = attributes.HasAttribute <AuthorizeAttribute>(); //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()); var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute(parent))).ToList(); var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList(); var queryParams = parameters.Where(x => x.Options.FromQuery).Select(x => new QueryParameter(x.Options.QueryName, x.Type, x.Default, x.Options.QueryObject)).ToList(); var bodyParam = parameters.Where(x => x.Options.FromBody).Select(x => new BodyParameter(x.Name, x.Type, x.Default)).SingleOrDefault(); endpoint.Parameters = routeParams.Cast <IParameter>().Union(queryParams).Union(new List <IParameter> { bodyParam }).NotNull().ToList(); endpoint.Parameters.Add(new CancellationTokenModifier()); endpoint.Parameters.Add(new CookieModifier()); endpoint.Parameters.Add(new HeadersModifier()); endpoint.Parameters.Add(new TimeoutModifier()); if (endpoint.IsSecured) { endpoint.Parameters.Add(new SecurityModifier()); } 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(); var okStatus = endpoint.ResponseTypes.SingleOrDefault(x => x.Status == HttpStatusCode.OK); if (okStatus != null && endpoint.ReturnType != null && Helpers.IsType(okStatus.ActionType, endpoint.ReturnType)) { //Remove the OkStatus since it is the same as the method return endpoint.ResponseTypes.Remove(okStatus); } else if (okStatus != null && endpoint.ReturnType != null && !Helpers.IsType(okStatus.ActionType, endpoint.ReturnType)) { throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has a OK response type of {okStatus.ActionType}, but the method return {endpoint.ReturnType}"); } var duplicateParameters = endpoint.GetParametersWithoutResponseTypes().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); if (duplicateParameters.Any()) { throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); } var invalidParameters = endpoint.GetParameters().Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); if (invalidParameters.Any()) { throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); } var fullRoute = endpoint.GetFullRoute(parent); if (fullRoute?.Version?.Query ?? false) { endpoint.Parameters.Add(new QueryParameter($"api-version={fullRoute?.Version}")); } return(endpoint); }