Exemple #1
0
 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);
        }