/// <summary>
        /// Generates an URI-friendly Id for the <see cref="ApIdescription"/>. E.g. "Get-Values-Id_name" instead of "GetValues/{Id}?name={name}"
        /// </summary>
        /// <param name="description">The <see cref="ApIdescription"/>.</param>
        /// <returns>The Id as a string.</returns>
        public static string GetFriendlyId(this ApIdescription description)
        {
            string path = description.RelativePath;

            string[] urlParts       = path.Split('?');
            string   localPath      = urlParts[0];
            string   queryKeyString = null;

            if (urlParts.Length > 1)
            {
                string   query     = urlParts[1];
                string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys;
                queryKeyString = String.Join("_", queryKeys);
            }

            StringBuilder friendlyPath = new StringBuilder();

            friendlyPath.AppendFormat("{0}-{1}",
                                      description.HttpMethod.Method,
                                      localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty));
            if (queryKeyString != null)
            {
                friendlyPath.AppendFormat("_{0}", queryKeyString.Replace('.', '-'));
            }
            return(friendlyPath.ToString());
        }
        private static bool TryGetResourceParameter(ApIdescription apIdescription, HttpConfiguration config, out ApiParameterDescription parameterDescription, out Type resourceType)
        {
            parameterDescription = apIdescription.ParameterDescriptions.FirstOrDefault(
                p => p.Source == ApiParameterSource.FromBody ||
                (p.ParameterDescriptor != null && p.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage)));

            if (parameterDescription == null)
            {
                resourceType = null;
                return(false);
            }

            resourceType = parameterDescription.ParameterDescriptor.ParameterType;

            if (resourceType == typeof(HttpRequestMessage))
            {
                HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
                resourceType = sampleGenerator.ResolveHttpRequestMessageType(apIdescription);
            }

            if (resourceType == null)
            {
                parameterDescription = null;
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Resolves the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> passed to the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
        /// </summary>
        /// <param name="api">The <see cref="ApIdescription"/>.</param>
        /// <returns>The type.</returns>
        public virtual Type ResolveHttpRequestMessageType(ApIdescription api)
        {
            string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName;
            string actionName     = api.ActionDescriptor.ActionName;
            IEnumerable <string>            parameterNames = api.ParameterDescriptions.Select(p => p.Name);
            Collection <MediaTypeFormatter> formatters;

            return(ResolveType(api, controllerName, actionName, parameterNames, SampleDirection.Request, out formatters));
        }
        /// <summary>
        /// Gets the request or response body samples.
        /// </summary>
        /// <param name="api">The <see cref="ApIdescription"/>.</param>
        /// <param name="sampleDirection">The value indicating whether the sample is for a request or for a response.</param>
        /// <returns>The samples keyed by media type.</returns>
        public virtual Idictionary <MediaTypeHeaderValue, object> GetSample(ApIdescription api, SampleDirection sampleDirection)
        {
            if (api == null)
            {
                throw new ArgumentNullException("api");
            }
            string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName;
            string actionName     = api.ActionDescriptor.ActionName;
            IEnumerable <string>            parameterNames = api.ParameterDescriptions.Select(p => p.Name);
            Collection <MediaTypeFormatter> formatters;
            Type type    = ResolveType(api, controllerName, actionName, parameterNames, sampleDirection, out formatters);
            var  samples = new Dictionary <MediaTypeHeaderValue, object>();

            // Use the samples provIded directly for actions
            var actionSamples = GetAllActionSamples(controllerName, actionName, parameterNames, sampleDirection);

            foreach (var actionSample in actionSamples)
            {
                samples.Add(actionSample.Key.MediaType, WrapSampleIfString(actionSample.Value));
            }

            // Do the sample generation based on formatters only if an action doesn't return an HttpResponseMessage.
            // Here we cannot rely on formatters because we don't know what's in the HttpResponseMessage, it might not even use formatters.
            if (type != null && !typeof(HttpResponseMessage).IsAssignableFrom(type))
            {
                object sampleObject = GetSampleObject(type);
                foreach (var formatter in formatters)
                {
                    foreach (MediaTypeHeaderValue mediaType in formatter.SupportedMediaTypes)
                    {
                        if (!samples.ContainsKey(mediaType))
                        {
                            object sample = GetActionSample(controllerName, actionName, parameterNames, type, formatter, mediaType, sampleDirection);

                            // If no sample found, try generate sample using formatter and sample object
                            if (sample == null && sampleObject != null)
                            {
                                sample = WriteSampleObjectUsingFormatter(formatter, sampleObject, type, mediaType);
                            }

                            samples.Add(mediaType, WrapSampleIfString(sample));
                        }
                    }
                }
            }

            return(samples);
        }
        public virtual Type ResolveType(ApIdescription api, string controllerName, string actionName, IEnumerable <string> parameterNames, SampleDirection sampleDirection, out Collection <MediaTypeFormatter> formatters)
        {
            if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection))
            {
                throw new InvalIdEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection));
            }
            if (api == null)
            {
                throw new ArgumentNullException("api");
            }
            Type type;

            if (ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, parameterNames), out type) ||
                ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, new[] { "*" }), out type))
            {
                // Re-compute the supported formatters based on type
                Collection <MediaTypeFormatter> newFormatters = new Collection <MediaTypeFormatter>();
                foreach (var formatter in api.ActionDescriptor.Configuration.Formatters)
                {
                    if (IsFormatSupported(sampleDirection, formatter, type))
                    {
                        newFormatters.Add(formatter);
                    }
                }
                formatters = newFormatters;
            }
            else
            {
                switch (sampleDirection)
                {
                case SampleDirection.Request:
                    ApiParameterDescription requestBodyParameter = api.ParameterDescriptions.FirstOrDefault(p => p.Source == ApiParameterSource.FromBody);
                    type       = requestBodyParameter == null ? null : requestBodyParameter.ParameterDescriptor.ParameterType;
                    formatters = api.SupportedRequestBodyFormatters;
                    break;

                case SampleDirection.Response:
                default:
                    type       = api.ResponseDescription.ResponseType ?? api.ResponseDescription.DeclaredType;
                    formatters = api.SupportedResponseFormatters;
                    break;
                }
            }

            return(type);
        }
        private static HelpPageApiModel GenerateApiModel(ApIdescription apIdescription, HttpConfiguration config)
        {
            HelpPageApiModel apiModel = new HelpPageApiModel()
            {
                ApIdescription = apIdescription,
            };

            ModelDescriptionGenerator modelGenerator  = config.GetModelDescriptionGenerator();
            HelpPageSampleGenerator   sampleGenerator = config.GetHelpPageSampleGenerator();

            GenerateUriParameters(apiModel, modelGenerator);
            GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
            GenerateResourceDescription(apiModel, modelGenerator);
            GenerateSamples(apiModel, sampleGenerator);

            return(apiModel);
        }
        /// <summary>
        /// Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls.
        /// </summary>
        /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
        /// <param name="apIdescriptionId">The <see cref="ApIdescription"/> Id.</param>
        /// <returns>
        /// An <see cref="HelpPageApiModel"/>
        /// </returns>
        public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apIdescriptionId)
        {
            object model;
            string modelId = ApiModelPrefix + apIdescriptionId;

            if (!config.Properties.TryGetValue(modelId, out model))
            {
                Collection <ApIdescription> apIdescriptions = config.Services.GetApiExplorer().ApIdescriptions;
                ApIdescription apIdescription = apIdescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apIdescriptionId, StringComparison.OrdinalIgnoreCase));
                if (apIdescription != null)
                {
                    model = GenerateApiModel(apIdescription, config);
                    config.Properties.TryAdd(modelId, model);
                }
            }

            return((HelpPageApiModel)model);
        }
        private static voId GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator)
        {
            ApIdescription apIdescription = apiModel.ApIdescription;

            foreach (ApiParameterDescription apiParameter in apIdescription.ParameterDescriptions)
            {
                if (apiParameter.Source == ApiParameterSource.FromBody)
                {
                    Type parameterType = apiParameter.ParameterDescriptor.ParameterType;
                    apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
                    apiModel.RequestDocumentation    = apiParameter.Documentation;
                }
                else if (apiParameter.ParameterDescriptor != null &&
                         apiParameter.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage))
                {
                    Type parameterType = sampleGenerator.ResolveHttpRequestMessageType(apIdescription);

                    if (parameterType != null)
                    {
                        apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
                    }
                }
            }
        }
 /// <summary>
 /// Gets the response body samples for a given <see cref="ApIdescription"/>.
 /// </summary>
 /// <param name="api">The <see cref="ApIdescription"/>.</param>
 /// <returns>The samples keyed by media type.</returns>
 public Idictionary <MediaTypeHeaderValue, object> GetSampleResponses(ApIdescription api)
 {
     return(GetSample(api, SampleDirection.Response));
 }
        private static voId GenerateUriParameters(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator)
        {
            ApIdescription apIdescription = apiModel.ApIdescription;

            foreach (ApiParameterDescription apiParameter in apIdescription.ParameterDescriptions)
            {
                if (apiParameter.Source == ApiParameterSource.FromUri)
                {
                    HttpParameterDescriptor parameterDescriptor        = apiParameter.ParameterDescriptor;
                    Type                        parameterType          = null;
                    ModelDescription            typeDescription        = null;
                    ComplexTypeModelDescription complexTypeDescription = null;
                    if (parameterDescriptor != null)
                    {
                        parameterType          = parameterDescriptor.ParameterType;
                        typeDescription        = modelGenerator.GetOrCreateModelDescription(parameterType);
                        complexTypeDescription = typeDescription as ComplexTypeModelDescription;
                    }

                    // Example:
                    // [TypeConverter(typeof(PointConverter))]
                    // public class Point
                    // {
                    //     public Point(int x, int y)
                    //     {
                    //         X = x;
                    //         Y = y;
                    //     }
                    //     public int X { get; set; }
                    //     public int Y { get; set; }
                    // }
                    // Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection.
                    //
                    // public class Point
                    // {
                    //     public int X { get; set; }
                    //     public int Y { get; set; }
                    // }
                    // Regular complex class Point will have properties X and Y added to UriParameters collection.
                    if (complexTypeDescription != null &&
                        !IsBindableWithTypeConverter(parameterType))
                    {
                        foreach (ParameterDescription uriParameter in complexTypeDescription.Properties)
                        {
                            apiModel.UriParameters.Add(uriParameter);
                        }
                    }
                    else if (parameterDescriptor != null)
                    {
                        ParameterDescription uriParameter =
                            AddParameterDescription(apiModel, apiParameter, typeDescription);

                        if (!parameterDescriptor.IsOptional)
                        {
                            uriParameter.Annotations.Add(new ParameterAnnotation()
                            {
                                Documentation = "Required"
                            });
                        }

                        object defaultValue = parameterDescriptor.DefaultValue;
                        if (defaultValue != null)
                        {
                            uriParameter.Annotations.Add(new ParameterAnnotation()
                            {
                                Documentation = "Default value is " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture)
                            });
                        }
                    }
                    else
                    {
                        Debug.Assert(parameterDescriptor == null);

                        // If parameterDescriptor is null, this is an undeclared route parameter which only occurs
                        // when source is FromUri. Ignored in request model and among resource parameters but listed
                        // as a simple string here.
                        ModelDescription modelDescription = modelGenerator.GetOrCreateModelDescription(typeof(string));
                        AddParameterDescription(apiModel, apiParameter, modelDescription);
                    }
                }
            }
        }