Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2839566
fix(ruby): autoload namespace barrels so a subclass loads regardless …
andreaTP Sep 25, 2026
d6a9686
style(ruby): indent generated code with two spaces
andreaTP Sep 28, 2026
f02f9d7
style(ruby): write YARD doc comments without trailing whitespace
andreaTP Sep 28, 2026
67c32ec
fix(ruby): expose model properties with attr_accessor instead of bare…
andreaTP Sep 28, 2026
6ab5384
style(ruby): freeze generated enum hashes
andreaTP Sep 28, 2026
23a3ec4
test(ruby): lint the layout cops generated code already satisfies
andreaTP Sep 28, 2026
d97b800
fix(ruby): write the client barrel where files require it for a multi…
andreaTP Sep 28, 2026
c61b19c
fix(ruby): stop writing and requiring barrels that have nothing to au…
andreaTP Sep 28, 2026
b750a75
style(ruby): write lambdas, hashes and mixins the idiomatic way
andreaTP Sep 28, 2026
ee9f410
style(ruby): drop empty parentheses from method definitions and calls
andreaTP Sep 28, 2026
c944986
style(ruby): double quote every generated string literal
andreaTP Sep 28, 2026
cb6adb1
style(ruby): separate class members with one blank line
andreaTP Sep 28, 2026
299961e
style(ruby): end methods on their value instead of an explicit return
andreaTP Sep 28, 2026
d322fdb
style(ruby): start every generated file with the frozen string litera…
andreaTP Sep 28, 2026
733be57
style(ruby): let a subclass inherit initialize and the deserializer w…
andreaTP Sep 28, 2026
d141792
feat(ruby): generate eager_load! on the client root module
andreaTP Sep 28, 2026
e6fe299
docs(ruby): give every cop the generated code still skips a reason
andreaTP Sep 28, 2026
dd78a37
test: cover a three-level hierarchy entered through its base class
andreaTP Sep 28, 2026
75e7485
test(ruby): port the discriminator and query parameter integration tests
andreaTP Sep 28, 2026
d9fc9df
test(ruby): run the default spec alongside the mock server specs
andreaTP Sep 28, 2026
cef7536
test(ruby): run the integration tests against the 0.22 runtime gems
andreaTP Sep 28, 2026
7352377
test(ruby): check that every constant the generated code names resolves
andreaTP Sep 28, 2026
6c67ec9
docs(ruby): correct and complete the changelog entries
andreaTP Sep 28, 2026
760efc2
test(ruby): stop excluding GitHub's contents endpoint, which now loads
andreaTP Sep 29, 2026
b77d120
fix(ruby): repeat the eager_load! walk until a pass loads nothing new
andreaTP Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/idempotency-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ jobs:
- dart
description:
- "./tests/Kiota.Builder.IntegrationTests/InheritingErrors.yaml"
- "./tests/Kiota.Builder.IntegrationTests/InheritingThreeLevels.yaml"
- "./tests/Kiota.Builder.IntegrationTests/NoUnderscoresInModel.yaml"
- "oas::petstore"
- "https://api.twitter.com/2/openapi.json"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ jobs:
- dart
description:
- "./tests/Kiota.Builder.IntegrationTests/InheritingErrors.yaml"
- "./tests/Kiota.Builder.IntegrationTests/InheritingThreeLevels.yaml"
- "./tests/Kiota.Builder.IntegrationTests/EnumHandling.yaml"
- "./tests/Kiota.Builder.IntegrationTests/NoUnderscoresInModel.yaml"
- "./tests/Kiota.Builder.IntegrationTests/ToDoApi.yaml"
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Preserve models for multipart properties with default content types when other properties specify an encoding. [#7342](https://github.com/microsoft/kiota/issues/7342)
- Dart: escape enum options named value or values to avoid conflicts with generated and built-in enum members. [#7807](https://github.com/microsoft/kiota/issues/7807)
- Ruby: the client's root module has an `eager_load!` method that loads every autoloaded model, for servers that preload before forking.
- Ruby: generated files start with `# frozen_string_literal: true`, so string default values handed to user code are frozen.
- Ruby: generated string literals are double quoted, the StandardRB convention.
- Ruby: namespaces with nothing to autoload no longer get an empty barrel file, and generated files no longer require them (561 fewer files in a GitHub client).
- Ruby: a client generated with a multi-segment `--namespace-name`, such as `integration_test.client`, failed to load because its root file was written one directory too deep.
- Ruby: namespace barrels register their models and enums with `autoload` instead of requiring them, so a subclass no longer fails to load when its base class file is required first. Other files require the barrel rather than an individual model file, and models now load on first use. A type no barrel autoloads, such as a model named after its namespace, is now required directly: 1,315 constants referenced by a Meraki client never loaded, and a client generated from the current GitHub description failed to load because it required a file that is never written. [#7956](https://github.com/microsoft/kiota/issues/7956)
- Ruby: model properties are declared with `attr_accessor`, which removes the thousands of "possibly useless use of a variable" warnings `ruby -w` printed while loading a client.
- Ruby: enum hashes are frozen. [kiota-ruby#68](https://github.com/microsoft/kiota-ruby/issues/68)
- C#: escape Unicode line and paragraph separators in generated string literals so generated clients remain valid C# source.
- External reference allowlist wildcards no longer cross the URI authority boundary, so a wildcard in the scheme or host cannot match text in the path, the user information or the port.
- Initialize inherited Dart model fields in constructor initializer lists.
Expand Down
8 changes: 3 additions & 5 deletions it/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"./tests/Kiota.Builder.IntegrationTests/InheritingErrors.yaml": {
"MockServerITFolder": "basic"
},
"./tests/Kiota.Builder.IntegrationTests/InheritingThreeLevels.yaml": {
"MockServerITFolder": "inheritance"
},
"./tests/Kiota.Builder.IntegrationTests/GeneratesUritemplateHints.yaml": {
"MockServerITFolder": "query-params"
},
Expand All @@ -24,11 +27,6 @@
"Language": "typescript",
"Pattern": "/repos/{owner}/{repo}/contents/{path}#GET",
"Rationale": "Kiota fails for typescript with api version 1.1.4 dated 2026-06-17 - https://github.com/microsoft/kiota/issues/7803"
},
{
"Language": "ruby",
"Pattern": "/repos/{owner}/{repo}/contents/{path}#GET",
"Rationale": "The oneOf response leaves a model detached from the DOM while a using still references it, so the composed type wrapper requires a file that is never generated. Same endpoint excluded for Java and TypeScript - https://github.com/microsoft/kiota/issues/7829"
}
]
},
Expand Down
7 changes: 2 additions & 5 deletions it/exec-cmd.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,12 @@ elseif ($language -eq "typescript") {
} -ErrorAction Stop
}
elseif ($language -eq "ruby") {
$clientRb = Join-Path -Path $testPath -ChildPath "client.rb"
$dest = Join-Path -Path $testPath -ChildPath "lib" -AdditionalChildPath "integration_test", "client"
Copy-Item -Path $clientRb -Destination $dest
# Mock server tests are places in a subdir in "spec". So execute them.
# Mock server tests are places in a subdir in "spec". So execute them, with the default test.
if ($mockServerTest) {
Invoke-call -ScriptBlock {
git init
bundle install
bundle exec rake SPEC=spec/$mockSeverITFolder/*.rb
bundle exec rake "SPEC=spec/{$mockSeverITFolder/*,integration_test_spec}.rb"
}
}
else {
Expand Down
105 changes: 16 additions & 89 deletions it/ruby/.rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,120 +2,49 @@ AllCops:
TargetRubyVersion: 3.2

Style/StringLiterals:
Enabled: false
EnforcedStyle: double_quotes

Style/StringLiteralsInInterpolation:
Enabled: true
EnforcedStyle: double_quotes

# generated lines embed URL templates, qualified constants and API descriptions
Layout/LineLength:
Enabled: false

Layout/TrailingWhitespace:
Enabled: false

Layout/FirstHashElementIndentation:
Enabled: false

Layout/IndentationWidth:
Enabled: false

Layout/SpaceInsideBlockBraces:
Enabled: false

Style/Lambda:
Enabled: false

Style/DefWithParentheses:
Enabled: false

Style/RedundantReturn:
Enabled: false

Style/IfUnlessModifier:
Enabled: false

Layout/EmptyLineBetweenDefs:
Enabled: false

Style/MutableConstant:
Enabled: false

Lint/Void:
Enabled: false

# class docs come from optional API descriptions; filler comments would be noise
Style/Documentation:
Enabled: false

Style/FrozenStringLiteralComment:
Enabled: false

Style/EmptyLiteral:
Enabled: false

Layout/EmptyLineAfterGuardClause:
Enabled: false

Style/MethodCallWithoutArgsParentheses:
Enabled: false

Style/TrailingCommaInHashLiteral:
Enabled: false

Style/TrivialAccessors:
Enabled: false

# module and class names come from the namespace name and API names; renaming them is breaking
Naming/ClassAndModuleCamelCase:
Enabled: false

# the runtime calls get_field_deserializers and get_query_parameter by name
Naming/AccessorMethodName:
Enabled: false

Layout/IndentationStyle:
Enabled: false

Layout/SpaceAroundEqualsInParameterDefault:
Enabled: false

Style/RedundantSelf:
Enabled: false

Layout/FirstArgumentIndentation:
Enabled: false

Layout/ClosingParenthesisIndentation:
Enabled: false

Lint/EmptyFile:
Enabled: false

Style/MixinGrouping:
Enabled: false

Style/AccessorGrouping:
Enabled: false

Layout/SpaceInsideHashLiteralBraces:
Enabled: false

Layout/TrailingEmptyLines:
Enabled: false

# generated method size tracks the API surface, one line per property, mapping or error code
Metrics/MethodLength:
Enabled: false

# generated method size tracks the API surface, one line per property, mapping or error code
Metrics/AbcSize:
Enabled: false

# enums are CamelCase hash constants that users reference; renaming them is breaking
Naming/ConstantName:
Enabled: false

# is_ and has_ names mirror the API's property names, as in every other Kiota language
Naming/PredicatePrefix:
Enabled: false

# generated class size tracks the API surface
Metrics/ClassLength:
Enabled: false

# generated module size tracks the API surface
Metrics/ModuleLength:
Enabled: false

Expand All @@ -136,12 +65,10 @@ Metrics/CyclomaticComplexity:
Metrics/PerceivedComplexity:
Enabled: false

# Generated factory methods may just delegate to super
Lint/UselessMethodDefinition:
Enabled: false
# one accessor per property, each with its own doc comment
Style/AccessorGrouping:
EnforcedStyle: separated

# The cop matches by method name, so it flags the accessor for an API property called `inherited`
# (the GitHub description has one). Ruby's inheritance hook is a class method, and an instance
# method of the same name does not shadow it, so renaming the property would be the worse trade
Lint/MissingSuper:
# descriptions come from the API and may start with prose such as "Note:"
Style/CommentAnnotation:
Enabled: false
8 changes: 4 additions & 4 deletions it/ruby/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ gem "rspec", "~> 3.0"

gem "rubocop", "~> 1.21"

gem "microsoft_kiota_abstractions", "~> 0.19.0"
gem "microsoft_kiota_abstractions", "~> 0.22.0"

gem "microsoft_kiota_faraday", "~> 0.19.0"
gem "microsoft_kiota_faraday", "~> 0.22.0"

gem "microsoft_kiota_serialization_json", "~> 0.19.0"
gem "microsoft_kiota_serialization_json", "~> 0.22.0"

gem "microsoft_kiota_authentication_oauth", "~> 0.19.0"
gem "microsoft_kiota_authentication_oauth", "~> 0.22.0"
6 changes: 0 additions & 6 deletions it/ruby/client.rb

This file was deleted.

2 changes: 1 addition & 1 deletion it/ruby/lib/integration_test.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

require_relative "integration_test/version"
require_relative "integration_test/client/client"
require_relative "integration_test/client/api_client"

module Integration_test
class Error < StandardError; end
Expand Down
6 changes: 0 additions & 6 deletions it/ruby/lib/integration_test/client/client.rb

This file was deleted.

6 changes: 3 additions & 3 deletions it/ruby/spec/basic/integration_test_basic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

RSpec.describe Integration_test do
it "does something useful" do
auth_provider = MicrosoftKiotaAbstractions::AnonymousAuthenticationProvider.new()
auth_provider = MicrosoftKiotaAbstractions::AnonymousAuthenticationProvider.new
request_adapter = MicrosoftKiotaFaraday::FaradayRequestAdapter.new(auth_provider)
request_adapter.set_base_url('http://127.0.0.1:1080')
request_adapter.set_base_url("http://127.0.0.1:1080")
client = Integration_test::Client::ApiClient.new(request_adapter)
expect(client).to_not be nil

# Error: uninitialized constant Integration_test::Client::Api::V1::Topics::TopicsRequestBuilder::Binary
# response = client.api().v1().topics().get().resume
# pp response
Expand Down
50 changes: 25 additions & 25 deletions it/ruby/spec/defaultvalues/integration_test_defaultvalues.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,42 @@

RSpec.describe Integration_test do
it "test default value initialization of model class" do
auth_provider = MicrosoftKiotaAbstractions::AnonymousAuthenticationProvider.new()
auth_provider = MicrosoftKiotaAbstractions::AnonymousAuthenticationProvider.new
request_adapter = MicrosoftKiotaFaraday::FaradayRequestAdapter.new(auth_provider)
request_adapter.set_base_url('http://127.0.0.1:1080')
request_adapter.set_base_url("http://127.0.0.1:1080")
client = Integration_test::Client::ApiClient.new(request_adapter)
service_response = client.api().v1().weather_forecast().get().resume

service_response = client.api.v1.weather_forecast.get.resume
# Here, a single object is returned instead of a list. So we just check for null
expect(service_response).to_not be nil

# pp service_response
# pp service_response.enum_value().class

# Now the real test: create a model class and verify that all properties have the default values.
model = Integration_test::Client::Models::WeatherForecast.new()
expect(model.bool_value()).to be true
model = Integration_test::Client::Models::WeatherForecast.new

expect(model.bool_value).to be true

expect(model.date_only_value).to_not be nil
expect(model.date_only_value.to_s()).to eq("1900-01-01")
expect(model.date_only_value.to_s).to eq("1900-01-01")

expect(model.date_value).to_not be nil
expect(model.date_value.to_s()).to eq("1900-01-01T00:00:00+00:00")
expect(model.decimal_value()).to be 25.5
expect(model.double_value()).to be 25.5
expect(model.enum_value()).to be Integration_test::Client::Models::WeatherForecastEnumValue[:One]
expect(model.float_value()).to be 25.5
expect(model.date_value.to_s).to eq("1900-01-01T00:00:00+00:00")

expect(model.decimal_value).to be 25.5
expect(model.double_value).to be 25.5
expect(model.enum_value).to be Integration_test::Client::Models::WeatherForecastEnumValue[:One]
expect(model.float_value).to be 25.5

expect(model.guid_value).to_not be nil
expect(model.guid_value().to_s()).to eq("00000000-0000-0000-0000-000000000000")
expect(model.long_value()).to be 255
expect(model.summary()).to eq("Test")
expect(model.temperature_c()).to be 15
expect(model.time_value()).to_not be nil
expect(model.time_value().strftime("%H:%M:%S")).to eq("00:00:00")
expect(model.guid_value.to_s).to eq("00000000-0000-0000-0000-000000000000")

expect(model.long_value).to be 255
expect(model.summary).to eq("Test")
expect(model.temperature_c).to be 15

expect(model.time_value).to_not be nil
expect(model.time_value.strftime("%H:%M:%S")).to eq("00:00:00")
end
end
48 changes: 48 additions & 0 deletions it/ruby/spec/discriminator/integration_test_discriminator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

RSpec.describe Integration_test do
def deserialize(hash)
MicrosoftKiotaSerializationJson::JsonParseNodeFactory.new.get_parse_node("application/json", hash.to_json)
.get_object_value(Integration_test::Client::Discriminateme::Component.method(:create_from_discriminator_value))
end

def serialize(value)
writer = MicrosoftKiotaSerializationJson::JsonSerializationWriterFactory.new.get_serialization_writer("application/json")
writer.write_object_value(nil, value)
JSON.parse(writer.get_serialized_content)
end

it "deserializes the first member of the union" do
result = deserialize("objectType" => "obj1", "one" => "foo")

expect(result.component2).to be_nil
expect(result.component1).to be_a(Integration_test::Client::Models::Component1)
expect([result.component1.object_type, result.component1.one]).to eq(%w[obj1 foo])
end

it "deserializes the second member of the union" do
result = deserialize("objectType" => "obj2", "two" => "bar")

expect(result.component1).to be_nil
expect(result.component2).to be_a(Integration_test::Client::Models::Component2)
expect([result.component2.object_type, result.component2.two]).to eq(%w[obj2 bar])
end

it "serializes the first member of the union" do
component = Integration_test::Client::Discriminateme::Component.new
component.component1 = Integration_test::Client::Models::Component1.new
component.component1.object_type = "obj1"
component.component1.one = "foo"

expect(serialize(component)).to eq("objectType" => "obj1", "one" => "foo")
end

it "serializes the second member of the union" do
component = Integration_test::Client::Discriminateme::Component.new
component.component2 = Integration_test::Client::Models::Component2.new
component.component2.object_type = "obj2"
component.component2.two = "bar"

expect(serialize(component)).to eq("objectType" => "obj2", "two" => "bar")
end
end
Loading
Loading