Skip to content

fix(auth): add machine-readable codes to password validation 422 response (EVO-992)#15

Merged
dpaes merged 1 commit into
developfrom
fix/EVO-992
May 11, 2026
Merged

fix(auth): add machine-readable codes to password validation 422 response (EVO-992)#15
dpaes merged 1 commit into
developfrom
fix/EVO-992

Conversation

@marcelogorutuba

@marcelogorutuba marcelogorutuba commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Updated User#password_complexity to use typed error symbols (:missing_lowercase, :missing_uppercase, :missing_number, :missing_special_char) instead of plain strings
  • Updated format_validation_errors in BaseController to include a codes array (e.g. password.missing_special_char) alongside existing messages and full_messages — additive, no breaking change
  • Added spec/models/user_password_spec.rb covering all four complexity error codes and the happy path

Validation

  • evo-auth-service-community: ruby -c app/models/user.rb
  • evo-auth-service-community: ruby -c app/controllers/api/base_controller.rb
  • evo-auth-service-community: ruby -c spec/models/user_password_spec.rb

Changed Files

  • app/models/user.rb
  • app/controllers/api/base_controller.rb
  • spec/models/user_password_spec.rb

Related PRs

Linked Issue

  • EVO-992

🤖 Generated with Claude Code

…onse

Return structured `codes` array (e.g. `password.missing_special_char`)
alongside human-readable messages in validation error details, so the
frontend can map each failure to a localized message without screen-scraping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented May 7, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds machine-readable password complexity error codes and exposes them through the existing validation error formatting, plus tests for password validation behavior.

Sequence diagram for password validation 422 response with machine-readable codes

sequenceDiagram
  actor ApiClient
  participant UsersController
  participant UserModel
  participant ActiveModelErrors
  participant BaseController

  ApiClient->>UsersController: POST /users (password)
  UsersController->>UserModel: assign_attributes(password)
  UsersController->>UserModel: valid?
  UserModel->>UserModel: password_complexity
  alt password_missing_requirements
    UserModel->>ActiveModelErrors: add password :missing_lowercase
    UserModel->>ActiveModelErrors: add password :missing_uppercase
    UserModel->>ActiveModelErrors: add password :missing_number
    UserModel->>ActiveModelErrors: add password :missing_special_char
  end
  UserModel-->>UsersController: validation_result(errors)

  UsersController->>BaseController: format_validation_errors(errors)
  BaseController->>ActiveModelErrors: attribute_names
  loop each field
    BaseController->>ActiveModelErrors: where(field)
    ActiveModelErrors-->>BaseController: field_errors
    BaseController->>ActiveModelErrors: full_messages_for(field)
    ActiveModelErrors-->>BaseController: full_messages
  end
  BaseController-->>UsersController: [{field,codes,messages,full_messages}]

  UsersController-->>ApiClient: 422 JSON {errors:[{field,codes,messages,full_messages}]}
Loading

Class diagram for User password complexity and BaseController validation formatting

classDiagram
  class User {
    +String password
    +password_complexity()
  }

  class ActiveModelErrors {
    +attribute_names() Array~Symbol~
    +where(attribute) Array~ActiveModelError~
    +full_messages_for(attribute) Array~String~
  }

  class ActiveModelError {
    +Symbol attribute
    +Object type
    +String message
  }

  class ApiBaseController {
    +format_validation_errors(errors) Array~Hash~
  }

  User *-- ActiveModelErrors : has_errors
  ActiveModelErrors o-- ActiveModelError : contains
  ApiBaseController ..> ActiveModelErrors : formats

  class PasswordErrorTypes {
    <<enumeration>>
    missing_lowercase
    missing_uppercase
    missing_number
    missing_special_char
  }

  ActiveModelError ..> PasswordErrorTypes : type

  note for User "password_complexity adds password errors with symbolic types and messages"
  note for ApiBaseController "format_validation_errors builds field,codes,messages,full_messages"
Loading

File-Level Changes

Change Details Files
Make password complexity validation use typed error symbols instead of only human-readable strings.
  • Refactors password_complexity to add errors with symbol types (:missing_lowercase, :missing_uppercase, :missing_number, :missing_special_char).
  • Keeps existing human-readable error messages by passing them explicitly via the message keyword argument.
  • Preserves existing regex checks for lowercase, uppercase, numeric, and special characters without changing the validation rules.
app/models/user.rb
Extend validation error formatter to surface machine-readable error codes alongside messages.
  • Changes format_validation_errors to iterate over errors.attribute_names to preserve per-field error ordering and access full error objects.
  • Collects per-field errors via errors.where(field) instead of relying on errors.messages.
  • Builds a new codes array in each error hash by composing ".<error_type>" for symbol-typed errors, while preserving existing messages and full_messages output.
app/controllers/api/base_controller.rb
Add model specs to cover password complexity error codes and success case.
  • Introduces spec file dedicated to password complexity behavior for the User model.
  • Covers all four complexity failures, asserting the presence of the corresponding error types/codes.
  • Covers the happy path to ensure no validation errors are added when the password meets all complexity requirements.
spec/models/user_password_spec.rb

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The switch from errors.messages.map to errors.attribute_names.map in format_validation_errors means :base errors will no longer be emitted (since attribute_names excludes :base), so consider explicitly including :base or iterating over the same keys as before to avoid changing behavior for non-attribute validation errors.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The switch from `errors.messages.map` to `errors.attribute_names.map` in `format_validation_errors` means `:base` errors will no longer be emitted (since `attribute_names` excludes `:base`), so consider explicitly including `:base` or iterating over the same keys as before to avoid changing behavior for non-attribute validation errors.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dpaes dpaes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Aprovado via /evo-code-review (EVO-992). Débito conhecido: ausência de spec de request cobrindo a serialização do array codes em error.details; cobertura de teste no frontend zero para o fluxo. Aprovação consciente do owner (Davidson).

@dpaes dpaes merged commit 13983b7 into develop May 11, 2026
1 check passed
@dpaes dpaes deleted the fix/EVO-992 branch May 11, 2026 16:33
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.

2 participants