Skip to content

Add shared Rack types#5440

Open
marcotc wants to merge 15 commits intomasterfrom
marcotc/type-appsec-response-to-rack
Open

Add shared Rack types#5440
marcotc wants to merge 15 commits intomasterfrom
marcotc/type-appsec-response-to-rack

Conversation

@marcotc
Copy link
Copy Markdown
Member

@marcotc marcotc commented Mar 12, 2026

What does this PR do?

Defines shared Rack::env, Rack::response, and Rack::app type aliases in vendor/rbs/rack/0/rack.rbs and applies them consistently across all Rack middleware RBS signatures.

Motivation:

Every Rack middleware follows the same contract: call(env) -> [status, headers, body]. Previously, each signature either repeated ::Hash[::String, untyped] inline or used untyped for the env, app, and response. This centralizes the Rack types so all middleware use the same vocabulary.

Change log entry

None.

Additional Notes:

RBS-only changes, no runtime code modified. Several middleware lib files remain in the Steepfile ignore list due to pre-existing issues unrelated to these type changes.

How to test the change?

bundle exec steep check lib/datadog/appsec/response.rb lib/datadog/appsec/contrib/sinatra/request_middleware.rb lib/datadog/appsec/contrib/rack/request_body_middleware.rb lib/datadog/appsec/contrib/rails/request_middleware.rb

Type to_rack return as a precise tuple [Integer, Hash[String, String],
Array[String]] instead of Array[untyped]. The values come directly from
the status, headers, and body attr_readers which are already typed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@marcotc marcotc added the AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos label Mar 12, 2026
@marcotc marcotc requested review from a team as code owners March 12, 2026 07:21
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 12, 2026

Typing analysis

Ignored files

This PR clears 3 ignored files. It increases the percentage of typed files from 45.64% to 45.97% (+0.33%).

Ignored files (+0-3)Cleared:
lib/datadog/tracing/contrib/hanami/action_tracer.rb
lib/datadog/tracing/contrib/hanami/router_tracing.rb
lib/datadog/tracing/contrib/rails/middlewares.rb

Note: Ignored files are excluded from the next sections.

steep:ignore comments

This PR introduces 3 steep:ignore comments.

steep:ignore comments (+3-0)Introduced:
lib/datadog/appsec/contrib/rack/request_body_middleware.rb:16
lib/datadog/appsec/contrib/rails/request_middleware.rb:10
lib/datadog/appsec/contrib/sinatra/request_middleware.rb:10

Untyped methods

This PR introduces 2 untyped methods and 1 partially typed method, and clears 2 untyped methods and 3 partially typed methods. It increases the percentage of typed methods from 61.16% to 61.24% (+0.08%).

Untyped methods (+2-2)Introduced:
sig/datadog/tracing/contrib/hanami/action_tracer.rbs:16
└── def configuration: () -> untyped
sig/datadog/tracing/contrib/hanami/router_tracing.rbs:8
└── def configuration: () -> untyped
Cleared:
sig/datadog/appsec/contrib/rack/request_body_middleware.rbs:10
└── def call: (untyped env) -> untyped
sig/datadog/appsec/contrib/rails/request_middleware.rbs:10
└── def call: (untyped env) -> untyped
Partially typed methods (+1-3)Introduced:
sig/datadog/tracing/contrib/hanami/action_tracer.rbs:10
└── def initialize: (::Rack::app app, untyped action) -> void
Cleared:
sig/datadog/appsec/contrib/rack/request_body_middleware.rbs:8
└── def initialize: (untyped app, ?::Hash[untyped, untyped] opt) -> void
sig/datadog/appsec/contrib/rails/request_middleware.rbs:8
└── def initialize: (untyped app, ?::Hash[untyped, untyped] opt) -> void
sig/datadog/appsec/response.rbs:12
└── def to_rack: () -> ::Array[untyped]

Untyped other declarations

This PR introduces 1 untyped other declaration, and clears 2 untyped other declarations. It increases the percentage of typed other declarations from 77.34% to 77.35% (+0.01%).

Untyped other declarations (+1-2)Introduced:
sig/datadog/tracing/contrib/hanami/action_tracer.rbs:8
└── @action: untyped
Cleared:
sig/datadog/appsec/contrib/rack/request_body_middleware.rbs:6
└── @app: untyped
sig/datadog/appsec/contrib/rails/request_middleware.rbs:6
└── @app: untyped

If you believe a method or an attribute is rightfully untyped or partially typed, you can add # untyped:accept on the line before the definition to remove it from the stats.

Define Rack::env, Rack::response, and Rack::app type aliases in
vendor/rbs/rack/0/rack.rbs and use them consistently across all
Rack middleware signatures: AppSec (Rack, Rails, Sinatra, Devise),
Tracing (Rack, Rails, Sinatra, Hanami).

Replaces inline Hash[String, untyped] env parameters, untyped app
fields, and untyped call return types with the shared aliases.
Removes duplicate local type definitions from Sinatra middleware.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@marcotc marcotc requested review from a team as code owners March 12, 2026 07:39
@marcotc marcotc requested a review from vpellan March 12, 2026 07:39
@marcotc marcotc changed the title Add typing for AppSec::Response#to_rack Add shared Rack types for middleware Mar 12, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@marcotc marcotc changed the title Add shared Rack types for middleware Add shared Rack types Mar 12, 2026
Add type annotations to narrow http_response from Rack::response?
to Rack::response, allowing the RBS signature to be precise without
the nullable workaround.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added integrations Involves tracing integrations appsec Application Security monitoring product labels Mar 12, 2026
marcotc and others added 4 commits March 12, 2026 00:47
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rails ExceptionMiddleware and Hanami ActionTracer now pass steep
checks with the new Rack types. Add missing @action ivar declaration
to ActionTracer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use local variable for active_span so Steep can narrow the type
through the && guard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@datadog-datadog-prod-us1
Copy link
Copy Markdown
Contributor

datadog-datadog-prod-us1 Bot commented Mar 12, 2026

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 95.15% (-0.00%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: dbab7cb | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

@pr-commenter
Copy link
Copy Markdown

pr-commenter Bot commented Mar 12, 2026

Benchmarks

Benchmark execution time: 2026-03-18 00:42:54

Comparing candidate commit dbab7cb in PR branch marcotc/type-appsec-response-to-rack with baseline commit b42addf in branch master.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 46 metrics, 0 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Comment thread vendor/rbs/rack/0/rack.rbs Outdated
Comment thread sig/datadog/appsec/contrib/sinatra/request_middleware.rbs Outdated
Copy link
Copy Markdown
Member

@Strech Strech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how you make it better for everyone! 💟

The only concern I have - it's better to type less, than type it false, as the consequences are - false feeling of being safe.

Comment thread vendor/rbs/rack/0/rack.rbs Outdated
Comment thread vendor/rbs/rack/0/rack.rbs Outdated
marcotc and others added 3 commits March 13, 2026 15:21
Rack env values are intentionally polymorphic (any user data can
appear there), and the middleware options hash is an open config
map — both should use `any`, not `untyped`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use Response::_Body interface for response body instead of
  Array[String]; plain arrays still satisfy the interface but
  body proxy objects are now also accepted
- Restore the _Body interface definition that was dropped
- Fix params to use `any` since request param values are
  intentionally polymorphic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@app: ::Rack::app

def initialize: (untyped app, ?::Hash[untyped, untyped] opt) -> void
def initialize: (::Rack::app app, ?::Hash[::Symbol, any] opt) -> void
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you keep it as untyped here, not sure about any

Suggested change
def initialize: (::Rack::app app, ?::Hash[::Symbol, any] opt) -> void
def initialize: (::Rack::app app, ?::Hash[::Symbol, untyped] opt) -> void

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some research and i added a typing interface for Rack middlewares:

# A Rack middleware with no configuration.
#
# The practical arguments provided to a middleware initializer are `new(app, *args, &block)`:
# 1. Rack application
# 2. Your choice of positional arguments
# 3. Your optional block
# (https://github.com/rack/rack/blob/v3.2.5/lib/rack/builder.rb#L164)
#
# The middleware should have the signature to match its arguments,
# thus that the signature changes depending on what configuration your middleware wants.
# As long as Rack application is the first argument, the signature is valid.
#
# If your middleware requires configuration, use `MiddlewareWithOptions` instead.

So the options are up to the choice of the middleware itself, and in this case, there should be no option after the rack app.

I updated all the types to match, and left some to-do's to remove the unused opt where that occurs.
I'm not going to remove the argument in this PR because I think that there's a minor chance that it needs extra testing.

Comment thread sig/datadog/appsec/contrib/rails/request_middleware.rbs Outdated
Comment thread sig/datadog/appsec/contrib/sinatra/request_middleware.rbs Outdated
Comment thread vendor/rbs/rack/0/rack.rbs Outdated
Copy link
Copy Markdown
Member

@Strech Strech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To unblock this PR from being merged, I'm approving this

But also, I would like to ask for few changes in lib/appsec typespecs and vendor/rack

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marcotc and others added 3 commits March 17, 2026 16:37
- Add Rack::Middleware and Rack::MiddlewareWithOptions[T] modules to
  vendor/rbs/rack/0/rack.rbs as shared type-only signatures
- Apply Rack::Middleware to RequestBodyMiddleware, Rails::RequestMiddleware,
  and Sinatra::RequestMiddleware RBS files
- Add TODO + steep:ignore for unused opt arg in all three Ruby files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@Strech Strech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏🏼 great work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos appsec Application Security monitoring product integrations Involves tracing integrations tracing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants