private void Initialize(Assembly functionsAssembly, string routePrefix) { var methods = functionsAssembly.GetTypes() .SelectMany(t => t.GetMethods()) .Where(m => m.GetCustomAttributes(typeof(FunctionNameAttribute), false).Any()) .ToList(); IList <ApiDescriptionGroup> apiDescrGroup = new List <ApiDescriptionGroup>(); foreach (MethodInfo methodInfo in methods) { if (!TryGetHttpTrigger(methodInfo, out var triggerAttribute)) { continue; } var functionAttr = (FunctionNameAttribute)methodInfo.GetCustomAttribute(typeof(FunctionNameAttribute), false); var prefix = string.IsNullOrWhiteSpace(routePrefix) ? "" : $"{routePrefix.TrimEnd('/')}/"; var route = $"{prefix}{(!string.IsNullOrWhiteSpace(triggerAttribute.Route) ? triggerAttribute.Route : functionAttr.Name)}"; var verbs = triggerAttribute.Methods ?? new[] { "get", "post", "delete", "head", "patch", "put", "options" }; var items = new List <ApiDescription>(); foreach (string verb in verbs) { var description = CreateDescription(methodInfo, route, functionAttr, verb); items.Add(description); } var group = new ApiDescriptionGroup(functionAttr.Name, items); apiDescrGroup.Add(group); } // TODO: is Version necessery? ApiDescriptionGroups = new ApiDescriptionGroupCollection(new ReadOnlyCollection <ApiDescriptionGroup>(apiDescrGroup), 1); }
private void AddActionScript(StringBuilder script, ApiDescriptionGroup controller, ApiDescription action) { script.AppendLine($" //action {action.ActionDescriptor.DisplayName}"); AddCallParameters(script, controller, action); script.AppendLine(" }"); }
private IApiDescriptionGroupCollectionProvider CreateApiDescriptionProvider(MethodInfo methodInfo) { var descriptionGroup = new ApiDescriptionGroup( "MyGroup", new List <ApiDescription>() { new ApiDescription() { HttpMethod = MvcUrlTemplate.HttpMethod, ActionDescriptor = new ControllerActionDescriptor() { MethodInfo = methodInfo, AttributeRouteInfo = new AttributeRouteInfo() { Template = MvcUrlTemplate.Url } } } }); var groupProvider = new Mock <IApiDescriptionGroupCollectionProvider>(MockBehavior.Strict); groupProvider .SetupGet(p => p.ApiDescriptionGroups) .Returns(new ApiDescriptionGroupCollection(new[] { descriptionGroup }, 1)); return(groupProvider.Object); }
private void AddControllerScript(StringBuilder script, ApiDescriptionGroup controller) { foreach (var action in controller.Items) { script.AppendLine(); AddActionScript(script, controller, action); } }
IEnumerable <Item> CreateGroupItem(ApiDescriptionGroup group, XmlDocument documentation) { foreach (IGrouping <string, ApiDescription> controller in group.Items.GroupBy(Settings.GroupExtractor)) { yield return(new Folder { Name = controller.Key, Item = controller.Select(i => CreateItem(i, documentation)).ToArray() }); } }
private static bool TryGetControllerType(ApiDescriptionGroup apiDescriptionGroup, out Type controllerType) { controllerType = null; var controllerActionDescriptor = apiDescriptionGroup.Items.Select(o => o.ActionDescriptor).OfType <ControllerActionDescriptor>().FirstOrDefault(); if (controllerActionDescriptor != null) { controllerType = controllerActionDescriptor.ControllerTypeInfo.AsType(); } return(controllerType != null); }
private void FindAllModelsFromGetResponses(List <string> models, ApiDescriptionGroup versionGroup) { // items in current API Version foreach (ApiDescription description in GetAllGetMethods(versionGroup)) { var response = description.SupportedResponseTypes.SingleOrDefault(x => x.StatusCode == 200 && x.Type.Namespace.ToLower().Contains("x.pagedlist")); if (response != null) { models.Add(response.Type.GenericTypeArguments.First().Name); } } }
private ApiDescriptionGroupModel CreateApiDescriptionGroupModel(ApiDescriptionGroup apiDescriptionGroup) { var apiDescriptionGroupModel = new ApiDescriptionGroupModel( apiDescriptionGroup.GroupName, apiDescriptionGroup.Items .Select(CreateApiDescriptionModel) .ToList() .AsReadOnly()); Type controllerType; if (TryGetControllerType(apiDescriptionGroup, out controllerType)) { apiDescriptionGroupModel.Description = _documentationProvider.GetDocumentation(controllerType.GetTypeInfo()); } return(apiDescriptionGroupModel); }
private ApiDescriptionGroupCollection BuildGroup() { var apis = new List <ApiDescription>(); foreach (var endpoint in manifest) { var actionDescriptor = new ActionDescriptor { DisplayName = endpoint.Name, RouteValues = GetRouteValues(endpoint) }; apis.AddRange(GetApiDescriptions(endpoint, actionDescriptor)); } var group = new ApiDescriptionGroup(groupName, apis); return(new ApiDescriptionGroupCollection(new[] { group }, 1)); }
/// <summary> /// get a ApiDescriptionGroup from your dependency injection and use it to query all used types /// </summary> /// <param name="apiDescription"></param> /// <param name="mediaTypesToCheck"></param> /// <returns></returns> public DistinctTypeList GetUsedContractTypes(ApiDescriptionGroup apiDescription, string?mediaTypesToCheck = null) { var apiDescriptions = apiDescription.Items .Where(w => w is not null) ; var usedTypes = new DistinctTypeList(); foreach (var description in apiDescriptions !) { var bodyParameter = description.ParameterDescriptions.Where(IsBodyParameter); usedTypes.AddRange(bodyParameter.Select(s => s.Type)); //TODO option to check for default Asp.net classes var responseParameter = description.SupportedResponseTypes.Where(w => w.Type != null && IsCorrectMediaType(mediaTypesToCheck, w)); usedTypes.AddRange(responseParameter.Select(s => s.Type)); } return(usedTypes); }
private RAMLDocument GenerateDocument(ApiDescriptionGroup group) { var doc = new RAMLDocument { Title = group.GroupName }; foreach (var item in group.Items) { var key = "/" + item.RelativePath; var resource = doc.GetOrAddResource(key); SetUriPatameters(item, resource); SetMethod(item, resource); } OtherHandle(doc); return(doc); }
private JToken Handle(ApiDescriptionGroup group) { var items = group.Items.AsEnumerable(); // Remove the obsolete if (_options.IgnoreObsoleteApi) { items = items.Where(item => !item.IsObsolete()); } var json = new JObject(); foreach (var item in items) { json.Add($"{item.HttpMethod} {item.RelativePath}", Handle(item)); } return(json); }
public void ApiDescriptionGroups_ReturnsAllApiDescriptionGroups() { var expectedApiDescriptions = new List <ApiDescriptionGroup>(); A.CallTo(() => _functionProcessor.ProcessHttpFunction(A <MethodInfo> ._)) .ReturnsLazily(() => { var returnValue = new ApiDescriptionGroup("a", new List <ApiDescription>()); expectedApiDescriptions.Add(returnValue); return(returnValue); }); var result = _sut.ApiDescriptionGroups; result.Should().NotBeNull(); A.CallTo(() => _functionProcessor.ProcessHttpFunction(A <MethodInfo> ._)) .MustHaveHappened(_expectedMethodNames.Length, Times.Exactly); result.Items.Should().BeEquivalentTo(expectedApiDescriptions); }
private ControllerDefinition CreateControllerDefinition(ApiDescriptionGroup apiDescriptions) { var controllerDefinition = new ControllerDefinition(); // Group further by http method //var perMethodGrouping = apiDescriptions // .GroupBy(apiDesc => apiDesc.GroupName); var actions = new List <ActionMethodDefinition>(); foreach (var group in apiDescriptions.Items) { var httpMethod = group.HttpMethod; //if (httpMethod == null) // throw new NotSupportedException(string.Format( // "Unbounded HTTP verbs for path '{0}'. Are you missing an HttpMethodAttribute?", // group.First() // //.RelativePathSansQueryString() // )); //if (group.Count() > 1) // throw new NotSupportedException(string.Format( // "Multiple operations with path '{0}' and method '{1}'. Are you overloading action methods?", // group.First() // //.RelativePathSansQueryString() // , httpMethod)); //var apiDescription = group.Single(); actions.Add(CreateActionMethodDefinition(group //, schemaRegistry )); } controllerDefinition.Name = apiDescriptions.GroupName; controllerDefinition.ActionMethods = actions; return(controllerDefinition); }
public ApiDescriptionGroupCollection Generate() { var descriptions = new List <ApiDescription>(); foreach (var method in _functionDataProvider.GetMethods()) { var route = method.TriggerAttribute.Route; var routeParameters = _tokenParser.Parse(route); var apiParameterDescriptions = new List <ApiParameterDescription>(); var apiResponseTypes = new List <ApiResponseType>(); // map body if available var bodyAttributes = method .MethodInfo .GetCustomAttributes(typeof(SwaggerRequestAttribute), false) .Where(x => (x as SwaggerRequestAttribute)?.In == RequestSource.Body) .Select(x => x as SwaggerRequestAttribute); foreach (var parameterMapping in _parameterMappings) { try { apiParameterDescriptions.AddRange(parameterMapping.Map(method.MethodInfo) ?? Enumerable.Empty <ApiParameterDescription>()); } catch (Exception ex) { _logger.LogError(ex, "Error mapping parameters in api explorer"); } } foreach (var responseTypeMapping in _responseMappings) { try { apiResponseTypes.AddRange(responseTypeMapping.Map(method.MethodInfo) ?? Enumerable.Empty <ApiResponseType>()); } catch (Exception ex) { _logger.LogError(ex, "Error mapping parameters in api explorer"); } } foreach (var methodParameter in method.MethodInfo.GetParameters()) { var matchingRouteParameter = routeParameters.SingleOrDefault(param => string.Equals(param, methodParameter.Name, StringComparison.InvariantCultureIgnoreCase)); if (matchingRouteParameter != default && !apiParameterDescriptions.Any(x => string.Equals(x.Name, matchingRouteParameter, StringComparison.InvariantCultureIgnoreCase))) { var isOptional = matchingRouteParameter.EndsWith("?", StringComparison.InvariantCultureIgnoreCase); apiParameterDescriptions.Add(_parameterDescriptionFactory.Create( bindingSource: BindingSource.Path, name: matchingRouteParameter, type: methodParameter.ParameterType, isOptional)); continue; } } foreach (var httpVerb in method.TriggerAttribute.Methods) { var description = new FunctionApiDescription( methodInfo: method.MethodInfo, name: method.Name, parameters: apiParameterDescriptions, verb: httpVerb, route: route, responseTypes: apiResponseTypes); foreach (var bodyAttribute in bodyAttributes) { var requestFormats = GetSupportedFormats(bodyAttribute.Type); foreach (var format in requestFormats) { description.SupportedRequestFormats.Add(format); } } descriptions.Add(description); } } var groups = new ApiDescriptionGroup("default", descriptions); return(new ApiDescriptionGroupCollection(new[] { groups }, version: 1)); }
private IEnumerable <ApiDescription> GetAllGetMethods(ApiDescriptionGroup versionGroup) { return(versionGroup.Items.Where(x => x.HttpMethod.ToLower() == "get")); }
private void AddCallParameters(StringBuilder script, ApiDescriptionGroup controller, ApiDescription action) { var methodParamNames = action.ParameterDescriptions.Select(p => $"{p.Type.Name} {p.Name}").Distinct().ToList(); var parameterList = string.Join(", ", methodParamNames); var url = "/" + BlazorProxyHelper.GenerateUrlWithParameters(action); var httpMethod = action.HttpMethod?.ToUpperInvariant() ?? "POST"; var postPara = ""; var addAbpResult = BlazorProxyHelper.AddAbpResult(action); if (action.SupportedResponseTypes.Any(f => f.Type == typeof(void)) || !action.SupportedResponseTypes.Any()) { script.AppendLine($" public async Task {controller.GroupName}{(action.ActionDescriptor as ControllerActionDescriptor).ActionName}({parameterList})"); script.AppendLine(" {"); } else { script.AppendLine($" public async Task<{BlazorProxyHelper.GetCSharpRepresentation(action.SupportedResponseTypes.First().Type, true)}> {controller.GroupName}{(action.ActionDescriptor as ControllerActionDescriptor).ActionName}({parameterList})"); script.AppendLine(" {"); } if (httpMethod == "POST" || httpMethod == "PUT") { var bodyParameters = action.ParameterDescriptions.Where(f => f.Source == null || f.Source.Id == "Body"); if (bodyParameters.Any() && bodyParameters.Count() <= 1) { postPara = $", {bodyParameters.FirstOrDefault()?.Name}"; } else if (!bodyParameters.Any()) { httpMethod = "GET"; } else if (bodyParameters.Count() > 1) { script.AppendLine(" throw new Exception(\"Can't have more than 1 parameter.\");"); } } var returnType = $"{BlazorProxyHelper.GetCSharpRepresentation(action.SupportedResponseTypes.First().Type, true)}"; var returnHandle = new StringBuilder(); if (!addAbpResult) { returnHandle.AppendLine(" return result;"); } else { returnType = $"ABPResult<{returnType}>"; returnHandle.AppendLine(" if (result.success)"); returnHandle.AppendLine(" {"); returnHandle.AppendLine(" return result.result;"); returnHandle.AppendLine(" }"); returnHandle.AppendLine(" else"); returnHandle.AppendLine(" {"); returnHandle.AppendLine(" if (result.unAuthorizedRequest)"); returnHandle.AppendLine(" {"); returnHandle.AppendLine(" throw new Exception(\"unAuthorizedRequest\");"); returnHandle.AppendLine(" }"); returnHandle.AppendLine(" throw new Exception(result.error.message);"); returnHandle.AppendLine(" }"); if (!_isABPResultAdded) { script.Insert(88, "using ASO.Shared;"); _isABPResultAdded = true; } } if (httpMethod == "DELETE") { script.AppendLine($" var resultResponse = await _httpClient.{BlazorProxyHelper.FirstCharToUpper(httpMethod.ToLower())}Async(\"{url}\");"); if (!action.SupportedResponseTypes.Any(f => f.Type == typeof(void))) { script.AppendLine(" var resultString = await resultResponse.Content.ReadAsStringAsync();"); script.AppendLine($" var result = Newtonsoft.Json.JsonConvert.DeserializeObject<{returnType}>(resultString);"); script.AppendLine(returnHandle.ToString()); } else { script.AppendLine(" return;"); } } else { if (!action.SupportedResponseTypes.Any(f => f.Type == typeof(void))) { script.AppendLine($" var result = await _httpClient.{BlazorProxyHelper.FirstCharToUpper(httpMethod.ToLower())}JsonAsync<{returnType}>(\"{url}\"{postPara});"); script.AppendLine(returnHandle.ToString()); } else { script.AppendLine($" await _httpClient.{BlazorProxyHelper.FirstCharToUpper(httpMethod.ToLower())}JsonAsync(\"{url}\"{postPara});"); script.AppendLine(" return;"); } } ReturnTypes.AddRange(BlazorProxyHelper.GetTypes(action.SupportedResponseTypes.First().Type)); foreach (var item in action.ParameterDescriptions) { ReturnTypes.AddRange(BlazorProxyHelper.GetTypes(item.ParameterDescriptor.ParameterType)); } }
private List <ApiDescriptionGroup> CreateDescriptors() { var groups = new List <ApiDescriptionGroup>(); var grainList = DiscoverGrainTypesToMap(grainInterfaceFeature); foreach (var grainType in grainList) { var typeDescription = grainType.GetCustomAttribute <DescriptionAttribute>(); var groupName = typeDescription?.Description ?? grainType.Name; var apiItems = new List <ApiDescription>(); var methods = grainType.GetMethods().Where(m => m.GetCustomAttributes(true) .Any(attr => attr.GetType() == _routeAttributeType || _methodAttributeTypes.Contains(attr.GetType()))).ToArray(); foreach (var methodInfo in methods) { var methodAttributes = methodInfo.GetCustomAttributes(true) .Where(attr => attr is Orleans.Http.Abstractions.MethodAttribute).SingleOrDefault() as Orleans.Http.Abstractions.MethodAttribute; if (null == methodAttributes) { continue; } var descriptor = new ControllerActionDescriptor() { ControllerName = groupName, ActionName = methodInfo.Name, DisplayName = methodInfo.Name, MethodInfo = methodInfo, ControllerTypeInfo = methodInfo.DeclaringType.GetTypeInfo(), RouteValues = new Dictionary <string, string>() { { "controller", groupName } }, AttributeRouteInfo = new AttributeRouteInfo() { Name = methodInfo.Name, Template = methodAttributes.Pattern }, ActionConstraints = new List <IActionConstraintMetadata>() { new HttpMethodActionConstraint(new[] { methodAttributes.Method }) }, Parameters = new List <ParameterDescriptor>(), BoundProperties = new List <ParameterDescriptor>(), FilterDescriptors = new List <FilterDescriptor>(), Properties = new Dictionary <object, object>() }; var description = new ApiDescription() { ActionDescriptor = descriptor, GroupName = groupName, HttpMethod = methodAttributes.Method, RelativePath = $"grains/{methodAttributes.Pattern}" }; var methodParams = methodInfo.GetParameters(); foreach (var parameter in methodParams) { var bindsource = BindingSource.Path; var attribute = parameter.GetCustomAttributes() .Where(attr => _parameterAttributeTypes.Contains(attr.GetType())).FirstOrDefault(); if (attribute != null) { if (attribute is Orleans.Http.Abstractions.FromBodyAttribute) { bindsource = BindingSource.Body; } else if (attribute is Orleans.Http.Abstractions.FromQueryAttribute) { bindsource = BindingSource.Query; } } var parameterDescriptor = new ControllerParameterDescriptor() { Name = parameter.Name, ParameterType = parameter.ParameterType, /* BindingInfo = new BindingInfo() * { * BinderModelName = parameter.Name, * BindingSource = bindsource, * BinderType = parameter.ParameterType * },*/ ParameterInfo = parameter }; descriptor.Parameters.Add(parameterDescriptor); description.ParameterDescriptions.Add(new ApiParameterDescription() { ModelMetadata = metadataProvider.GetMetadataForType(parameter.ParameterType), Name = parameter.Name, RouteInfo = new ApiParameterRouteInfo(), Type = parameter.ParameterType, Source = bindsource, IsRequired = true }); } //support path route ? description.SupportedRequestFormats.Add(new ApiRequestFormat() { MediaType = "application/json" }); description.SupportedResponseTypes.Add(new ApiResponseType() { ApiResponseFormats = new List <ApiResponseFormat>() { new ApiResponseFormat() { MediaType = "application/json" } }, ModelMetadata = metadataProvider .GetMetadataForType(/*methodInfo.ReturnType*/ typeof(System.Text.Json.JsonElement)), Type = methodInfo.ReturnType, StatusCode = (int)System.Net.HttpStatusCode.OK });; //InferRequestContentTypes(description); apiItems.Add(description); } var group = new ApiDescriptionGroup(groupName, new ReadOnlyCollection <ApiDescription>(apiItems)); groups.Add(group); } return(groups); }
/// <summary> /// /// </summary> /// <param name="apiDescription"></param> /// <param name="attributesToCheck"></param> /// <param name="mediaTypesToCheck"></param> /// <param name="typesToIgnore"></param> /// <returns></returns> public List <AttributeCheckResult> CheckAttributesOfApiContractTypes(ApiDescriptionGroup apiDescription, Type[] attributesToCheck, string?mediaTypesToCheck = null, IEnumerable <Type>?typesToIgnore = null) { var typeList = _apiContractModelsFinder.GetUsedContractTypes(apiDescription, mediaTypesToCheck); return(CheckTypeAttributes(typeList, attributesToCheck, typesToIgnore)); }
public FunctionApiDescriptionProvider( IOptions <Option> functionsOptions, SwashBuckleStartupConfig startupConfig, IModelMetadataProvider modelMetadataProvider, IOutputFormatter outputFormatter, IOptions <HttpOptions> httOptions) { _option = functionsOptions.Value; _modelMetadataProvider = modelMetadataProvider; _outputFormatter = outputFormatter; var methods = startupConfig.Assembly.GetTypes() .SelectMany(t => t.GetMethods()) .Where(m => m.GetCustomAttributes(typeof(FunctionNameAttribute), false).Any()) .ToArray(); IList <ApiDescriptionGroup> apiDescrGroup = new List <ApiDescriptionGroup>(); foreach (var methodInfo in methods) { if (!TryGetHttpTrigger(methodInfo, out var triggerAttribute)) { continue; } var functionAttr = (FunctionNameAttribute)methodInfo.GetCustomAttribute(typeof(FunctionNameAttribute), false); var prefix = string.IsNullOrWhiteSpace(httOptions.Value.RoutePrefix) ? "" : $"{httOptions.Value.RoutePrefix.TrimEnd('/')}/"; var route = $"{prefix}{(!string.IsNullOrWhiteSpace(triggerAttribute.Route) ? triggerAttribute.Route : functionAttr.Name)}"; var routes = new List <(string Route, string RemoveParamName)>(); var regex = new Regex("/\\{(?<paramName>\\w+)\\?\\}$"); var match = regex.Match(route); if (match.Success && match.Captures.Count == 1) { routes.Add((route.Replace(match.Value, "").Replace("//", "/"), match.Groups["paramName"].ToString())); routes.Add((route.Replace(match.Value, match.Value.Replace("?", "")), "")); } else { routes.Add((route, "")); } var verbs = triggerAttribute.Methods ?? new[] { "get", "post", "delete", "head", "patch", "put", "options" }; for (var index = 0; index < routes.Count; index++) { var r = routes[index]; var apiName = functionAttr.Name + (index == 0 ? "" : $"-{index}"); var items = verbs.Select(verb => CreateDescription(methodInfo, r.Route, functionAttr, verb, triggerAttribute.AuthLevel, r.RemoveParamName)).ToArray(); var group = new ApiDescriptionGroup(apiName, items); apiDescrGroup.Add(@group); } } ApiDescriptionGroups = new ApiDescriptionGroupCollection(new ReadOnlyCollection <ApiDescriptionGroup>(apiDescrGroup), 1); }
public FakeApiDescriptionGroupCollectionProvider WithPostsController() { group = new ApiDescriptionGroup("posts", actions); groups.Add(group); return(this); }