Imports System Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.ComponentModel Imports System.Diagnostics Imports System.Diagnostics.CodeAnalysis Imports System.Globalization Imports System.Linq Imports System.Net.Http Imports System.Net.Http.Headers Imports System.Runtime.CompilerServices Imports System.Web.Http Imports System.Web.Http.Controllers Imports System.Web.Http.Description Imports $rootnamespace$.Areas.HelpPage.Models Imports $rootnamespace$.Areas.HelpPage.ModelDescriptions Namespace Areas.HelpPage Public Module HelpPageConfigurationExtensions Private Const ApiModelPrefix As String = "MS_HelpPageApiModel_" ''' ''' Sets the documentation provider for help page. ''' ''' The . ''' The documentation provider. Public Sub SetDocumentationProvider(ByVal config As HttpConfiguration, documentationProvider As IDocumentationProvider) config.Services.Replace(GetType(IDocumentationProvider), documentationProvider) End Sub ''' ''' Sets the objects that will be used by the formatters to produce sample requests/responses. ''' ''' The . ''' The sample objects. Public Sub SetSampleObjects(ByVal config As HttpConfiguration, sampleObjects As IDictionary(Of Type, Object)) config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects End Sub ''' ''' Sets the sample request directly for the specified media type and action. ''' ''' The . ''' The sample request. ''' The media type. ''' Name of the controller. ''' Name of the action. Public Sub SetSampleRequest(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, New String() {"*"}), sample) End Sub ''' ''' Sets the sample request directly for the specified media type and action with parameters. ''' ''' The . ''' The sample request. ''' The media type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetSampleRequest(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample) End Sub ''' ''' Sets the sample request directly for the specified media type of the action. ''' ''' The . ''' The sample response. ''' The media type. ''' Name of the controller. ''' Name of the action. Public Sub SetSampleResponse(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, New String() {"*"}), sample) End Sub ''' ''' Sets the sample response directly for the specified media type of the action with specific parameters. ''' ''' The . ''' The sample response. ''' The media type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetSampleResponse(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample) End Sub ''' ''' Sets the sample directly for all actions with the specified type. ''' ''' The . ''' The sample. ''' The media type. Public Sub SetSampleForMediaType(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType), sample) End Sub ''' ''' Sets the sample directly for all actions with the specified type and media type. ''' ''' The . ''' The sample. ''' The media type. ''' The parameter type or return type of an action. Public Sub SetSampleForType(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, type As Type) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, type), sample) End Sub ''' ''' Specifies the actual type of passed to the in an action. ''' The help page will use this information to produce more accurate request samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. Public Sub SetActualRequestType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, New String() {"*"}), type) End Sub ''' ''' Specifies the actual type of passed to the in an action. ''' The help page will use this information to produce more accurate request samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetActualRequestType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type) End Sub ''' ''' Specifies the actual type of returned as part of the in an action. ''' The help page will use this information to produce more accurate response samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. Public Sub SetActualResponseType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, New String() {"*"}), type) End Sub ''' ''' Specifies the actual type of returned as part of the in an action. ''' The help page will use this information to produce more accurate response samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetActualResponseType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type) End Sub ''' ''' Gets the help page sample generator. ''' ''' The . ''' The help page sample generator. Public Function GetHelpPageSampleGenerator(ByVal config As HttpConfiguration) As HelpPageSampleGenerator Return DirectCast(config.Properties.GetOrAdd( GetType(HelpPageSampleGenerator), Function(k) New HelpPageSampleGenerator()), HelpPageSampleGenerator) End Function ''' ''' Gets the model description generator. ''' ''' The configuration. ''' The Public Function GetModelDescriptionGenerator(config As HttpConfiguration) As ModelDescriptionGenerator Return DirectCast(config.Properties.GetOrAdd(GetType(ModelDescriptionGenerator), Function(k) InitializeModelDescriptionGenerator(config)), ModelDescriptionGenerator) End Function ''' ''' Sets the help page sample generator. ''' ''' The . ''' The help page sample generator. Public Sub SetHelpPageSampleGenerator(ByVal config As HttpConfiguration, sampleGenerator As HelpPageSampleGenerator) config.Properties.AddOrUpdate( GetType(HelpPageSampleGenerator), Function(k) sampleGenerator, Function(k, o) sampleGenerator) End Sub ''' ''' 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. ''' ''' The . ''' The ID. ''' ''' An ''' Public Function GetHelpPageApiModel(ByVal config As HttpConfiguration, apiDescriptionId As String) As HelpPageApiModel Dim model As New Object Dim modelId As String = ApiModelPrefix + apiDescriptionId If (Not config.Properties.TryGetValue(modelId, model)) Then Dim apiDescriptions As Collection(Of ApiDescription) = config.Services.GetApiExplorer().ApiDescriptions Dim ApiDescription As ApiDescription = apiDescriptions.FirstOrDefault(Function(api) String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase)) If (Not ApiDescription Is Nothing) Then model = GenerateApiModel(ApiDescription, config) config.Properties.TryAdd(modelId, model) End If End If Return DirectCast(model, HelpPageApiModel) End Function Public Function GenerateApiModel(apiDescription As ApiDescription, config As HttpConfiguration) As HelpPageApiModel Dim apiModel As New HelpPageApiModel() With { .ApiDescription = apiDescription } Dim sampleGenerator As HelpPageSampleGenerator = config.GetHelpPageSampleGenerator() Dim modelGenerator As ModelDescriptionGenerator = config.GetModelDescriptionGenerator() GenerateUriParameters(apiModel, modelGenerator) GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator) GenerateResourceDescription(apiModel, modelGenerator) GenerateSamples(apiModel, sampleGenerator) Return apiModel End Function Private Sub GenerateUriParameters(apiModel As HelpPageApiModel, modelGenerator As ModelDescriptionGenerator) Dim apiDescription As ApiDescription = apiModel.ApiDescription For Each apiParameter As ApiParameterDescription In apiDescription.ParameterDescriptions If apiParameter.Source = ApiParameterSource.FromUri Then Dim parameterDescriptor As HttpParameterDescriptor = apiParameter.ParameterDescriptor Dim parameterType As Type = Nothing Dim typeDescription As ModelDescription = Nothing Dim complexTypeDescription As ComplexTypeModelDescription = Nothing If parameterDescriptor IsNot Nothing Then parameterType = parameterDescriptor.ParameterType typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType) complexTypeDescription = TryCast(typeDescription, ComplexTypeModelDescription) End If '' Example: '' '' Public Class Point '' Public Sub New(x As Integer, y As Integer) '' x = x '' y = y '' End Sub '' Public X As Integer '' Public Y As Integer '' End Class '' Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection. '' '' Public Class Point '' Public X As Integer '' Public Y As Integer '' End Class '' Regular complex class Point will have properties X and Y added to UriParameters collection. If complexTypeDescription IsNot Nothing AndAlso Not IsBindableWithTypeConverter(parameterType) Then For Each uriParameter As ParameterDescription In complexTypeDescription.Properties apiModel.UriParameters.Add(uriParameter) Next ElseIf parameterDescriptor IsNot Nothing Then Dim uriParameter As ParameterDescription = AddParameterDescription(apiModel, apiParameter, typeDescription) If Not parameterDescriptor.IsOptional Then uriParameter.Annotations.Add(New ParameterAnnotation() With { .Documentation = "Required" }) End If Dim defaultValue As Object = parameterDescriptor.DefaultValue If defaultValue IsNot Nothing Then uriParameter.Annotations.Add(New ParameterAnnotation() With { .Documentation = "Default value is " & Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }) End If Else Debug.Assert(parameterDescriptor Is Nothing) '' If parameterDescriptor is Nothing, 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. Dim modelDescription As ModelDescription = modelGenerator.GetOrCreateModelDescription(GetType(String)) AddParameterDescription(apiModel, apiParameter, modelDescription) End If End If Next End Sub Private Function IsBindableWithTypeConverter(type As Type) As Boolean If type Is Nothing Then Return False End If Return TypeDescriptor.GetConverter(type).CanConvertFrom(GetType(String)) End Function Private Function AddParameterDescription(apiModel As HelpPageApiModel, apiParameter As ApiParameterDescription, typeDescription As ModelDescription) As ParameterDescription Dim parameterDescription As New ParameterDescription() With { .Name = apiParameter.Name, .Documentation = apiParameter.Documentation, .TypeDescription = typeDescription } apiModel.UriParameters.Add(parameterDescription) Return parameterDescription End Function Private Sub GenerateRequestModelDescription(apiModel As HelpPageApiModel, modelGenerator As ModelDescriptionGenerator, sampleGenerator As HelpPageSampleGenerator) Dim apiDescription As ApiDescription = apiModel.ApiDescription For Each apiParameter As ApiParameterDescription In apiDescription.ParameterDescriptions If apiParameter.Source = ApiParameterSource.FromBody Then Dim parameterType As Type = apiParameter.ParameterDescriptor.ParameterType apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType) apiModel.RequestDocumentation = apiParameter.Documentation ElseIf apiParameter.ParameterDescriptor IsNot Nothing AndAlso apiParameter.ParameterDescriptor.ParameterType = GetType(HttpRequestMessage) Then Dim parameterType As Type = sampleGenerator.ResolveHttpRequestMessageType(apiDescription) If parameterType IsNot Nothing Then apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType) End If End If Next End Sub Private Sub GenerateResourceDescription(apiModel As HelpPageApiModel, modelGenerator As ModelDescriptionGenerator) Dim response As ResponseDescription = apiModel.ApiDescription.ResponseDescription Dim responseType As Type = If(response.ResponseType, response.DeclaredType) If responseType IsNot Nothing AndAlso responseType <> GetType(System.Void) Then apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType) End If End Sub Private Sub GenerateSamples(apiModel As HelpPageApiModel, sampleGenerator As HelpPageSampleGenerator) Try For Each item In sampleGenerator.GetSampleRequests(apiModel.ApiDescription) apiModel.SampleRequests.Add(item.Key, item.Value) LogInvalidSampleAsError(apiModel, item.Value) Next For Each item In sampleGenerator.GetSampleResponses(apiModel.ApiDescription) apiModel.SampleResponses.Add(item.Key, item.Value) LogInvalidSampleAsError(apiModel, item.Value) Next Catch e As Exception apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture, "An exception has occurred while generating the sample. Exception message: {0}", HelpPageSampleGenerator.UnwrapException(e).Message)) End Try End Sub Private Function TryGetResourceParameter(apiDescription As ApiDescription, config As HttpConfiguration, ByRef parameterDescription As ApiParameterDescription, ByRef resourceType As Type) As Boolean parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault( Function(p) p.Source = ApiParameterSource.FromBody OrElse (p.ParameterDescriptor IsNot Nothing AndAlso p.ParameterDescriptor.ParameterType = GetType(HttpRequestMessage))) If parameterDescription Is Nothing Then resourceType = Nothing Return False End If resourceType = parameterDescription.ParameterDescriptor.ParameterType If resourceType = GetType(HttpRequestMessage) Then Dim sampleGenerator As HelpPageSampleGenerator = config.GetHelpPageSampleGenerator() resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription) End If If resourceType Is Nothing Then parameterDescription = Nothing Return False End If Return True End Function Private Function InitializeModelDescriptionGenerator(config As HttpConfiguration) As ModelDescriptionGenerator Dim modelGenerator As New ModelDescriptionGenerator(config) Dim apis As Collection(Of ApiDescription) = config.Services.GetApiExplorer().ApiDescriptions For Each api As ApiDescription In apis Dim parameterDescription As ApiParameterDescription = Nothing Dim parameterType As Type = Nothing If TryGetResourceParameter(api, config, parameterDescription, parameterType) Then modelGenerator.GetOrCreateModelDescription(parameterType) End If Next Return modelGenerator End Function Private Sub LogInvalidSampleAsError(apiModel As HelpPageApiModel, sample As Object) Dim invalidSample As InvalidSample = TryCast(sample, InvalidSample) If (Not invalidSample Is Nothing) Then apiModel.ErrorMessages.Add(invalidSample.ErrorMessage) End If End Sub End Module End Namespace