From f41092a51459fc6ed25fec466670a411047ca8b0 Mon Sep 17 00:00:00 2001 From: Vishrut Shah Date: Mon, 2 May 2016 15:21:51 -0700 Subject: [PATCH] Implement x-ms-parameterized-host extension for ruby (#992) * Implement x-ms-parameterized-host extension for ruby * Default value of method parameters should not be double escaped when string * Check for x-ms-skip-url-encoding should be on extension + the values (true/false) --- .../RspecTests/custom_base_uri_more_spec.rb | 39 ++ .../RspecTests/custom_base_uri_spec.rb | 33 ++ .../Generators/Ruby/Ruby/RubyCodeGenerator.cs | 17 +- .../TemplateModels/MethodTemplateModel.cs | 387 ++++++++++-------- .../ServiceClientTemplateModel.cs | 6 + .../Ruby/Ruby/Templates/MethodTemplate.cshtml | 8 +- .../Templates/ServiceClientTemplate.cshtml | 25 +- gulpfile.js | 2 + 8 files changed, 331 insertions(+), 186 deletions(-) create mode 100644 AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_more_spec.rb create mode 100644 AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_spec.rb diff --git a/AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_more_spec.rb b/AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_more_spec.rb new file mode 100644 index 000000000000..29a5dff237a7 --- /dev/null +++ b/AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_more_spec.rb @@ -0,0 +1,39 @@ +# encoding: utf-8 + +$: << 'RspecTests/Generated/custom_base_uri_more' + +require 'custom_base_url_more_options' +require 'uri' + +module CustomBaseUriMoreModule + describe 'Custom base uri more options' do + before(:all) do + url = URI(ENV['StubServerURI']) + @vault = "http://#{url.host}" + @key_name = "key1" + + dummyToken = 'dummy12321343423' + @credentials = MsRest::TokenCredentials.new(dummyToken) + + client = CustomBaseUriMoreModule::AutoRestParameterizedCustomHostTestClient.new(@credentials) + client.subscription_id = 'test12' + client.instance_variable_set("@dns_suffix", ":#{url.port.to_s}") + @custom_base_url_client = CustomBaseUriMoreModule::Paths.new(client) + end + + it 'should get empty' do + result = @custom_base_url_client.get_empty_async(@vault, '', @key_name).value! + expect(result.response.status).to eq(200) + end + + it 'should throw on nil vault or secret' do + expect { + @custom_base_url_client.get_empty_async(nil, nil, @key_name).value! + }.to raise_error(ArgumentError) + + expect { + @custom_base_url_client.get_empty_async(@vault, nil, @key_name).value! + }.to raise_error(ArgumentError) + end + end +end diff --git a/AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_spec.rb b/AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_spec.rb new file mode 100644 index 000000000000..c3eb81c719d5 --- /dev/null +++ b/AutoRest/Generators/Ruby/Azure.Ruby.Tests/RspecTests/custom_base_uri_spec.rb @@ -0,0 +1,33 @@ +# encoding: utf-8 + +$: << 'RspecTests/Generated/custom_base_uri' + +require 'custom_base_url' +require 'uri' + +module CustomBaseUriModule + describe 'Custom base uri' do + before(:all) do + url = URI(ENV['StubServerURI']) + @account_name = url.host + + dummyToken = 'dummy12321343423' + @credentials = MsRest::TokenCredentials.new(dummyToken) + + client = CustomBaseUriModule::AutoRestParameterizedHostTestClient.new(@credentials) + client.instance_variable_set("@host", ":#{url.port.to_s}") + @custom_base_url_client = CustomBaseUriModule::Paths.new(client) + end + + it 'should get empty' do + result = @custom_base_url_client.get_empty_async(@account_name).value! + expect(result.response.status).to eq(200) + end + + it 'should throw on nil account name' do + expect { + @custom_base_url_client.get_empty_async(nil).value! + }.to raise_error(ArgumentError) + end + end +end diff --git a/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs b/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs index 5b875665dfa9..368888b08265 100644 --- a/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs +++ b/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs @@ -111,24 +111,25 @@ public override string ImplementationFileExtension /// /// Normalizes client model by updating names and types to be language specific. /// - /// - public override void NormalizeClientModel(ServiceClient serviceClientModel) + /// + public override void NormalizeClientModel(ServiceClient serviceClient) { - PopulateAdditionalProperties(serviceClientModel); - CodeNamer.NormalizeClientModel(serviceClientModel); - CodeNamer.ResolveNameCollisions(serviceClientModel, Settings.Namespace, + Extensions.ProcessParameterizedHost(serviceClient, Settings); + PopulateAdditionalProperties(serviceClient); + CodeNamer.NormalizeClientModel(serviceClient); + CodeNamer.ResolveNameCollisions(serviceClient, Settings.Namespace, Settings.Namespace + "::Models"); } /// /// Adds special properties to the service client (e.g. credentials). /// - /// The service client. - private void PopulateAdditionalProperties(ServiceClient serviceClientModel) + /// The service client. + private void PopulateAdditionalProperties(ServiceClient serviceClient) { if (Settings.AddCredentials) { - serviceClientModel.Properties.Add(new Property + serviceClient.Properties.Add(new Property { Name = "Credentials", Type = new PrimaryType(KnownPrimaryType.Credentials), diff --git a/AutoRest/Generators/Ruby/Ruby/TemplateModels/MethodTemplateModel.cs b/AutoRest/Generators/Ruby/Ruby/TemplateModels/MethodTemplateModel.cs index d612a9cefa00..e51e53727c91 100644 --- a/AutoRest/Generators/Ruby/Ruby/TemplateModels/MethodTemplateModel.cs +++ b/AutoRest/Generators/Ruby/Ruby/TemplateModels/MethodTemplateModel.cs @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; +using System.Text.RegularExpressions; using Microsoft.Rest.Generator.ClientModel; using Microsoft.Rest.Generator.Ruby.TemplateModels; using Microsoft.Rest.Generator.Utilities; -using System.Text.RegularExpressions; namespace Microsoft.Rest.Generator.Ruby { @@ -30,6 +31,173 @@ public MethodTemplateModel(Method source, ServiceClient serviceClient) ServiceClient = serviceClient; } + /// + /// Gets the return type name for the underlying interface method + /// + public virtual string OperationResponseReturnTypeString + { + get + { + return "MsRest::HttpOperationResponse"; + } + } + + /// + /// Gets the type for operation exception + /// + public virtual string OperationExceptionTypeString + { + get + { + return "MsRest::HttpOperationError"; + } + } + + /// + /// Gets the code required to initialize response body. + /// + public virtual string InitializeResponseBody + { + get { return string.Empty; } + } + + /// + /// Gets the list of namespaces where we look for classes that need to + /// be instantiated dynamically due to polymorphism. + /// + public virtual List ClassNamespaces + { + get + { + return new List { }; + } + } + + /// + /// Gets the path parameters as a Ruby dictionary string + /// + public virtual string PathParamsRbDict + { + get + { + return ParamsToRubyDict(EncodingPathParams); + } + } + + /// + /// Gets the skip encoding path parameters as a Ruby dictionary string + /// + public virtual string SkipEncodingPathParamsRbDict + { + get + { + return ParamsToRubyDict(SkipEncodingPathParams); + } + } + + /// + /// Gets the query parameters as a Ruby dictionary string + /// + public virtual string QueryParamsRbDict + { + get + { + return ParamsToRubyDict(EncodingQueryParams); + } + } + + /// + /// Gets the skip encoding query parameters as a Ruby dictionary string + /// + public virtual string SkipEncodingQueryParamsRbDict + { + get + { + return ParamsToRubyDict(SkipEncodingQueryParams); + } + } + + /// + /// Gets the path parameters not including the params that skip encoding + /// + public virtual IEnumerable EncodingPathParams + { + get { return AllPathParams.Where(p => !(p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension) && + String.Equals(p.Extensions[Generator.Extensions.SkipUrlEncodingExtension].ToString(), "true", StringComparison.OrdinalIgnoreCase))); } + } + + /// + /// Gets the skip encoding path parameters + /// + public virtual IEnumerable SkipEncodingPathParams + { + get + { + return AllPathParams.Where(p => + (p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension) && + String.Equals(p.Extensions[Generator.Extensions.SkipUrlEncodingExtension].ToString(), "true", StringComparison.OrdinalIgnoreCase) && + !p.Extensions.ContainsKey("hostParameter"))); + } + } + + /// + /// Gets all path parameters + /// + public virtual IEnumerable AllPathParams + { + get { return ParameterTemplateModels.Where(p => p.Location == ParameterLocation.Path); } + } + + /// + /// Gets the skip encoding query parameters + /// + public virtual IEnumerable SkipEncodingQueryParams + { + get { return AllQueryParams.Where(p => p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension)); } + } + + /// + /// Gets the query parameters not including the params that skip encoding + /// + public virtual IEnumerable EncodingQueryParams + { + get { return AllQueryParams.Where(p => !p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension)); } + } + + /// + /// Gets all of the query parameters + /// + public virtual IEnumerable AllQueryParams + { + get { return ParameterTemplateModels.Where(p => p.Location == ParameterLocation.Query); } + } + + /// + /// Gets the list of middelwares required for HTTP requests. + /// + public virtual IList FaradayMiddlewares + { + get + { + return new List() + { + "[MsRest::RetryPolicyMiddleware, times: 3, retry: 0.02]", + "[:cookie_jar]" + }; + } + } + + /// + /// Gets the expression for default header setting. + /// + public virtual string SetDefaultHeaders + { + get + { + return string.Empty; + } + } + /// /// Gets the reference to the service client object. /// @@ -39,7 +207,7 @@ public MethodTemplateModel(Method source, ServiceClient serviceClient) /// Gets the list of method paramater templates. /// public List ParameterTemplateModels { get; private set; } - + /// /// Gets the list of parameter which need to be included into HTTP header. /// @@ -103,14 +271,11 @@ public string MethodParameterDeclaration PrimaryType type = parameter.Type as PrimaryType; if (type != null) { - if (type.Type == KnownPrimaryType.Boolean || type.Type == KnownPrimaryType.Double || type.Type == KnownPrimaryType.Int || type.Type == KnownPrimaryType.Long) + if (type.Type == KnownPrimaryType.Boolean || type.Type == KnownPrimaryType.Double || + type.Type == KnownPrimaryType.Int || type.Type == KnownPrimaryType.Long || type.Type == KnownPrimaryType.String) { format = "{0} = " + parameter.DefaultValue; } - else if (type.Type == KnownPrimaryType.String) - { - format = "{0} = \"" + parameter.DefaultValue + "\""; - } } } } @@ -153,48 +318,6 @@ public IEnumerable LocalParameters } } - /// - /// Gets the return type name for the underlying interface method - /// - public virtual string OperationResponseReturnTypeString - { - get - { - return "MsRest::HttpOperationResponse"; - } - } - - /// - /// Gets the type for operation exception - /// - public virtual string OperationExceptionTypeString - { - get - { - return "MsRest::HttpOperationError"; - } - } - - /// - /// Gets the code required to initialize response body. - /// - public virtual string InitializeResponseBody - { - get { return string.Empty; } - } - - /// - /// Gets the list of namespaces where we look for classes that need to - /// be instantiated dynamically due to polymorphism. - /// - public virtual List ClassNamespaces - { - get - { - return new List { }; - } - } - /// /// Get the method's request body (or null if there is no request body) /// @@ -230,16 +353,6 @@ public bool UrlWithPath } } - /// - /// Gets the formatted status code. - /// - /// The status code. - /// Formatted status code. - public string GetStatusCodeReference(HttpStatusCode code) - { - return string.Format("{0}", (int)code); - } - /// /// Creates a code in form of string which deserializes given input variable of given type. /// @@ -325,99 +438,52 @@ public virtual string RemoveDuplicateForwardSlashes(string urlVariableName) return builder.ToString(); } - + /// - /// Gets the path parameters as a Ruby dictionary string + /// Generate code to build the URL from a url expression and method parameters /// - public virtual string PathParamsRbDict + /// The variable to store the url in. + /// + public virtual string BuildUrl(string variableName) { - get - { - return ParamsToRubyDict(EncodingPathParams); - } + var builder = new IndentedStringBuilder(" "); + BuildPathParameters(variableName, builder); + + return builder.ToString(); } - + /// - /// Gets the skip encoding path parameters as a Ruby dictionary string + /// Gets the formatted status code. /// - public virtual string SkipEncodingPathParamsRbDict + /// The status code. + /// Formatted status code. + public string GetStatusCodeReference(HttpStatusCode code) { - get - { - return ParamsToRubyDict(SkipEncodingPathParams); - } + return string.Format("{0}", (int)code); } - + /// - /// Gets the query parameters as a Ruby dictionary string + /// Generate code to replace path parameters in the url template with the appropriate values /// - public virtual string QueryParamsRbDict + /// The variable name for the url to be constructed + /// The string builder for url construction + protected virtual void BuildPathParameters(string variableName, IndentedStringBuilder builder) { - get + if (builder == null) { - return ParamsToRubyDict(EncodingQueryParams); + throw new ArgumentNullException("builder"); } - } - - /// - /// Gets the skip encoding query parameters as a Ruby dictionary string - /// - public virtual string SkipEncodingQueryParamsRbDict - { - get + + IEnumerable pathParameters = LogicalParameters.Where(p => p.Extensions.ContainsKey("hostParameter") && p.Location == ParameterLocation.Path); + + foreach (var pathParameter in pathParameters) { - return ParamsToRubyDict(SkipEncodingQueryParams); + var pathReplaceFormat = "{0} = {0}.gsub('{{{1}}}', {2})"; + var urlPathName = UrlPathNameFromPathPattern(pathParameter.SerializedName); + builder.AppendLine(pathReplaceFormat, variableName, urlPathName, pathParameter.GetFormattedReferenceValue()); } } - - /// - /// Gets the skip encoding path parameters - /// - public virtual IEnumerable SkipEncodingPathParams - { - get { return AllPathParams.Where(p => p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension)); } - } - - /// - /// Gets the path parameters not including the params that skip encoding - /// - public virtual IEnumerable EncodingPathParams - { - get { return AllPathParams.Where(p => !p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension)); } - } - - /// - /// Gets all path parameters - /// - public virtual IEnumerable AllPathParams - { - get { return ParameterTemplateModels.Where(p => p.Location == ParameterLocation.Path); } - } - - /// - /// Gets the skip encoding query parameters - /// - public virtual IEnumerable SkipEncodingQueryParams - { - get { return AllQueryParams.Where(p => p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension)); } - } - - /// - /// Gets the query parameters not including the params that skip encoding - /// - public virtual IEnumerable EncodingQueryParams - { - get { return AllQueryParams.Where(p => !p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension)); } - } - - /// - /// Gets all of the query parameters - /// - public virtual IEnumerable AllQueryParams - { - get { return ParameterTemplateModels.Where(p => p.Location == ParameterLocation.Query); } - } - + /// /// Builds the parameters as a Ruby dictionary string /// @@ -429,50 +495,27 @@ protected string ParamsToRubyDict(IEnumerable parameters foreach (var param in parameters) { string variableName = param.Name; - string urlPathName = param.SerializedName; - string pat = @".*\{" + urlPathName + @"(\:\w+)\}"; - Regex r = new Regex(pat); - Match m = r.Match(Url); - if (m.Success) - { - urlPathName += m.Groups[1].Value; - } - if (param.Type is SequenceType) - { - encodedParameters.Add(string.Format("'{0}' => {1}", urlPathName, param.GetFormattedReferenceValue())); - } - else - { - encodedParameters.Add(string.Format("'{0}' => {1}", urlPathName, variableName)); - } + string urlPathName = UrlPathNameFromPathPattern(param.SerializedName); + encodedParameters.Add(string.Format("'{0}' => {1}", urlPathName, param.GetFormattedReferenceValue())); } return string.Format(CultureInfo.InvariantCulture, "{{{0}}}", string.Join(",", encodedParameters)); } /// - /// Gets the list of middelwares required for HTTP requests. - /// - public virtual IList FaradayMiddlewares - { - get - { - return new List() - { - "[MsRest::RetryPolicyMiddleware, times: 3, retry: 0.02]", - "[:cookie_jar]" - }; - } - } - - /// - /// Gets the expression for default header setting. + /// Builds the url path parameter from the pattern if exists /// - public virtual string SetDefaultHeaders + /// Name of the path parameter to match. + /// url path parameter as a string + private string UrlPathNameFromPathPattern(string urlPathParamName) { - get + string pat = @".*\{" + urlPathParamName + @"(\:\w+)\}"; + Regex r = new Regex(pat); + Match m = r.Match(Url); + if (m.Success) { - return string.Empty; + urlPathParamName += m.Groups[1].Value; } + return urlPathParamName; } } -} +} \ No newline at end of file diff --git a/AutoRest/Generators/Ruby/Ruby/TemplateModels/ServiceClientTemplateModel.cs b/AutoRest/Generators/Ruby/Ruby/TemplateModels/ServiceClientTemplateModel.cs index d7d3bb2b3cec..cc0fb64a65cf 100644 --- a/AutoRest/Generators/Ruby/Ruby/TemplateModels/ServiceClientTemplateModel.cs +++ b/AutoRest/Generators/Ruby/Ruby/TemplateModels/ServiceClientTemplateModel.cs @@ -25,6 +25,7 @@ public ServiceClientTemplateModel(ServiceClient serviceClient) MethodTemplateModels = new List(); Methods.Where(m => m.Group == null) .ForEach(m => MethodTemplateModels.Add(new MethodTemplateModel(m, serviceClient))); + this.IsCustomBaseUri = serviceClient.Extensions.ContainsKey(Microsoft.Rest.Generator.Extensions.ParameterizedHostExtension); } /// @@ -37,6 +38,11 @@ public ServiceClientTemplateModel(ServiceClient serviceClient) /// public List MethodTemplateModels { get; set; } + /// + /// Gets the flag indicating whether url is from x-ms-parameterized-host extension. + /// + public bool IsCustomBaseUri { get; private set; } + /// /// Gets the list of modules/classes which need to be included. /// diff --git a/AutoRest/Generators/Ruby/Ruby/Templates/MethodTemplate.cshtml b/AutoRest/Generators/Ruby/Ruby/Templates/MethodTemplate.cshtml index 6b085d99c9ab..b8d70fabe5b9 100644 --- a/AutoRest/Generators/Ruby/Ruby/Templates/MethodTemplate.cshtml +++ b/AutoRest/Generators/Ruby/Ruby/Templates/MethodTemplate.cshtml @@ -101,7 +101,6 @@ def @(Model.Name)_async(@(Model.MethodParameterDeclaration)) @:@(parameter.Type.ValidateType(Model.Scope, parameter.Name)) } } - request_headers = {} @if (Model.Parameters.Any(p => p.Location == ParameterLocation.Header)) { @@ -152,7 +151,12 @@ def @(Model.Name)_async(@(Model.MethodParameterDeclaration)) headers: request_headers.merge(custom_headers || {}) } - request = MsRest::HttpOperationRequest.new(@@base_url || @(Model.ClientReference).base_url, path_template, :@Model.HttpMethod.ToString().ToLower(), options) +@EmptyLine + request_url = @@base_url || @(Model.ClientReference).base_url + @(Model.BuildUrl("request_url")) +@EmptyLine + + request = MsRest::HttpOperationRequest.new(request_url, path_template, :@Model.HttpMethod.ToString().ToLower(), options) promise = request.run_promise do |req| @(Model.ClientReference).credentials.sign_request(req) unless @(Model.ClientReference).credentials.nil? end diff --git a/AutoRest/Generators/Ruby/Ruby/Templates/ServiceClientTemplate.cshtml b/AutoRest/Generators/Ruby/Ruby/Templates/ServiceClientTemplate.cshtml index da18fb0e5a68..1a049baf2386 100644 --- a/AutoRest/Generators/Ruby/Ruby/Templates/ServiceClientTemplate.cshtml +++ b/AutoRest/Generators/Ruby/Ruby/Templates/ServiceClientTemplate.cshtml @@ -23,7 +23,7 @@ module @Settings.Namespace @EmptyLine # @@return [String] the base URI of the service. - attr_accessor :base_url + @(Model.IsCustomBaseUri ? "attr_reader" : "attr_accessor") :base_url @EmptyLine @foreach (var property in Model.Properties) @@ -43,12 +43,29 @@ module @Settings.Namespace # # Creates initializes a new instance of the @Model.Name class. # @@param credentials [MsRest::ServiceClientCredentials] credentials to authorize HTTP requests made by the service client. - # @@param base_url [String] the base URI of the service. + @if (!Model.IsCustomBaseUri) + { + @:# @@param base_url [String] the base URI of the service. + } # @@param options [Array] filters to be applied to the HTTP requests. # - def initialize(credentials, base_url = nil, options = nil) + @if (!Model.IsCustomBaseUri) + { + @:def initialize(credentials, base_url = nil, options = nil) + } + else + { + @:def initialize(credentials, options = nil) + } super(credentials, options) - @@base_url = base_url || '@Model.BaseUrl' + @if (!Model.IsCustomBaseUri) + { + @:@@base_url = base_url || '@Model.BaseUrl' + } + else + { + @:@@base_url = '@Model.BaseUrl' + } @EmptyLine fail ArgumentError, 'credentials is nil' if credentials.nil? fail ArgumentError, 'invalid type of credentials input parameter' unless credentials.is_a?(MsRest::ServiceClientCredentials) diff --git a/gulpfile.js b/gulpfile.js index e7bc6be0adf4..350c79b60c74 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -128,6 +128,8 @@ var rubyAzureMappings = { 'azure_url':['../../../TestServer/swagger/subscriptionId-apiVersion.json', 'AzureUrlModule'], 'azure_special_properties': ['../../../TestServer/swagger/azure-special-properties.json', 'AzureSpecialPropertiesModule'], 'azure_report':['../../../TestServer/swagger/azure-report.json', 'AzureReportModule'], + 'custom_base_uri':['../../../TestServer/swagger/custom-baseUrl.json', 'CustomBaseUriModule'], + 'custom_base_uri_more':['../../../TestServer/swagger/custom-baseUrl-more-options.json', 'CustomBaseUriMoreModule'], }; gulp.task('regenerate:expected', function(cb){