Skip to content

Fix OTP 28 compatibility and test issues #668

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

matthewsinclair
Copy link

Fixes multiple OTP 28 compatibility issues and test failures.

Problem

OTP 28 introduced stricter validation for module attributes that contain references, compiled regexes, or other complex data structures that cannot be properly escaped. This affects libraries that store compiled schemas or parsers in module attributes at compile time.

OTP 28 Compatibility Fixes

1. Module Attribute to Runtime Function

File: lib/open_api_spex/cast_parameters.ex

  • Issue: @default_parsers module attribute containing compiled regex patterns cannot be escaped under OTP 28
  • Fix: Convert to default_content_parsers/0 private function
  • Impact: Maintains identical functionality while fixing compilation errors

2. Schema Pattern Format

File: test/support/schemas.ex

  • Issue: Compiled regex pattern ~r/[a-zA-Z][a-zA-Z0-9_]+/ in schema definition
  • Fix: Replace with string pattern "[a-zA-Z][a-zA-Z0-9_]+"
  • Impact: OpenAPI schemas support regex patterns as strings

Test Compatibility Fixes

3. Regex Comparison in Tests

File: test/cast/string_test.exs

  • Issue: Comparing compiled regex objects with different internal references
  • Fix: Compare regex.source instead of regex objects
  • Impact: Tests now pass consistently

4. Phoenix Router API Update

File: test/operation_test.exs

  • Issue: Phoenix.Router.Route.build/13 changed to build/14 in Phoenix 1.7+
  • Fix: Update function call with correct arity and arguments
  • Impact: Maintains compatibility with current Phoenix versions

Testing Results

  • All tests passing: 388 tests, 0 failures
  • Clean compilation: No warnings or errors on OTP 28
  • Backward compatibility: No functional changes to library behavior

Changes Summary

  • Convert module attributes containing non-escapable data to runtime functions
  • Update test assertions for OTP 28 compatibility
  • Fix Phoenix API compatibility issues
  • Maintain 100% backward compatibility

This resolves compilation errors on OTP 28 while maintaining identical functionality and ensuring all tests pass.

@03juan
Copy link

03juan commented May 27, 2025

This fixes the failing test but not the root cause, as the examples, docs, and typespec still support generating a struct-based schema with a regex pattern as a macro.

open_api_spex/README.md

Lines 183 to 194 in 410f3aa

defmodule User do
require OpenApiSpex
OpenApiSpex.schema(%{
# The title is optional. It defaults to the last section of the module name.
# So the derived title for MyApp.User is "User".
title: "User",
description: "A user of the app",
type: :object,
properties: %{
id: %Schema{type: :integer, description: "User ID"},
name: %Schema{type: :string, description: "User name", pattern: ~r/[a-zA-Z][a-zA-Z0-9_]+/},

name: %Schema{type: :string, description: "User name", pattern: ~r/[a-zA-Z][a-zA-Z0-9_]+/},

pattern: String.t() | Regex.t() | nil,

For backwards compatibility defmacro should test whether body has a regex pattern and define schema/0 to either build the struct in the function call for OTP28 or set and reference the @schema module attribute as it is now.

See my attempt at this in PR matthewsinclair#1

|> Enum.reject(&is_nil/1)
|> Enum.join("\n\n")
end

def schema, do: @schema
if OpenApiSpex.Schema.has_regex_pattern?(unquote(body)) do
Copy link
Contributor

@zorbash zorbash Jun 2, 2025

Choose a reason for hiding this comment

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

Given this change is only necessary for OTP 28 and it might introduce a performance penalty, should it affect applications not running on OTP 28?

@@ -530,4 +530,24 @@ defmodule OpenApiSpex.Schema do
defp default(value) do
raise "Expected %Schema{}, schema module, or %Reference{}. Got: #{inspect(value)}"
end

@doc false
def has_regex_pattern?(%Schema{pattern: %Regex{}}), do: true
Copy link
Contributor

Choose a reason for hiding this comment

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

My suggestion is to only log a warning when a Regex is used in conjunction with OTP 28, with a message to update their code to pattern: String.t.

@matthewsinclair matthewsinclair force-pushed the fix/otp-28-compatibility branch from 3f1dc20 to b3d4f53 Compare June 2, 2025 10:30
…ling

- Implement OTP version detection in schema macro
- Add runtime schema compilation for OTP 28+ when regex patterns are present
- Maintain module attribute optimization for non-regex schemas on all OTP versions
- Add deprecation warning for regex patterns on OTP 28+ with migration guidance
- Fix cast_parameters.ex to avoid regex in module attributes
- Update string test to check pattern source instead of regex struct equality

This ensures backward compatibility while providing a clear migration path
for OTP 28+ users to move from regex patterns to string patterns.
@matthewsinclair matthewsinclair force-pushed the fix/otp-28-compatibility branch from b3d4f53 to 2ed65d2 Compare June 2, 2025 11:38
@sevenseacat
Copy link

should this PR be closed in favour of your other one? Which one is more complete?

@matthewsinclair
Copy link
Author

Yes. Apologies for the double-up. There were two separate comment threads, and I think our changes crossed in the aether.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants