Skip to content

HQD-127: Refactor hub assignment logic and validation#279

Open
bladeroot wants to merge 1 commit into
hiqdev:masterfrom
bladeroot:HQD-127
Open

HQD-127: Refactor hub assignment logic and validation#279
bladeroot wants to merge 1 commit into
hiqdev:masterfrom
bladeroot:HQD-127

Conversation

@bladeroot
Copy link
Copy Markdown
Member

@bladeroot bladeroot commented May 5, 2026

  • Improved AssignHubsForm with explicit model normalization and dynamic payload generation
  • Enhanced binding validation logic to correctly identify own device bindings
  • Refactored JS to use CSS classes instead of localized titles for column detection
  • Updated action to use structured attribute lists for API payloads

Summary by CodeRabbit

  • Bug Fixes
    • Improved hub assignment form validation to enforce supported model classes and prevent assignment errors.
    • Enhanced column reveal behavior to properly toggle both input fields and dropdown menus when displaying hidden hub rows.
    • Refined binding conflict detection to better identify when devices are self-connected, reducing false-positive errors.

- Improved AssignHubsForm with explicit model normalization and dynamic payload generation
- Enhanced binding validation logic to correctly identify own device bindings
- Refactored JS to use CSS classes instead of localized titles for column detection
- Updated action to use structured attribute lists for API payloads
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

📝 Walkthrough

Walkthrough

The PR refactors hub assignment logic to use explicit attribute-name definitions instead of underscore-based heuristics. Form adds getHubPayloadAttributeNames() method and improves model-class validation and binding-ownership checks. Action now uses the form method to populate hub payload. JavaScript selects columns by CSS selector instead of title text and enables both input and select fields.

Changes

Hub Assignment Flow Refinement

Layer / File(s) Summary
Form Type & Method Definition
src/forms/AssignHubsForm.php
Added getHubPayloadAttributeNames() public method that derives {variant}_id and {variant}_port attributes from hub variants. Added normalizeModelClass() to validate and map model classes to Hub or Server, throwing InvalidArgumentException for unsupported classes.
Form Validation Logic
src/forms/AssignHubsForm.php
JBOD self-connection check now compares IDs as strings. Port/switch conflict validation uses new isOwnBinding() method to determine if a binding belongs to the current model by matching device/base-device names, including prefix matches ending at non-digit boundaries.
Form Model Handling
src/forms/AssignHubsForm.php
fromOriginalModel() only calls setModelClass() when the original model is not already an AssignHubsForm instance. setModelClass() now delegates to normalizeModelClass().
Action Hub Payload
src/actions/AssignableHubs.php
beforeSave() now populates $hub['hubs'] by iterating over $model->getHubPayloadAttributeNames() instead of underscore-based heuristics. Copies each attribute from the form model with ?? '' fallback and unsets the original key.
UI Column Reveal
src/assets/js/assign-hubs-column-reveal.js
Removed TITLES-based column identification. Changed to select ol.nets, ol.pdus containers and find their closest .col-md-4 wrapper. Updated visibility toggles to enable/disable both input and select elements (previously input only).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • hiqdev/hipanel-module-server#269: Modifies AssignableHubs::beforeSave() and introduces AssignHubsForm::getHubPayloadAttributeNames() — the same core methods and behavior as this PR.

Suggested reviewers

  • SilverFire
  • hiqsol

Poem

🐰 Hub assignments now run clear and clean,
No more underscore guessing scenes!
Attributes explicit, bindings owned with care,
JavaScript selects with CSS flair,
Forms speak true—a rabbit's cheer!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main refactoring changes across all modified files, including form validation logic and JavaScript updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/actions/AssignableHubs.php`:
- Around line 64-69: The code currently clones $form, loads request data into
it, then calls getHubPayloadAttributeNames() which causes the whitelist to be
derived from the posted state (including a crafted type) and merges into
$hub['hubs']; instead, create/resolve the allowed hub payload keys from the
persisted model/type (use setModelClass($this->getAssignableClassName()) on a
fresh model instance or otherwise instantiate the model for the persisted type
without loading request data), call getHubPayloadAttributeNames() on that
trusted model to get the whitelist, build a new array with only those keys from
the request (do not merge), assign it to $hub['hubs'] (replacing the whole
array), and remove the unset($hub[$key]) merge logic; use the same symbols shown
(clone $form, setModelClass, getAssignableClassName,
getHubPayloadAttributeNames, load/validate) to locate and update the code.

In `@src/forms/AssignHubsForm.php`:
- Around line 306-313: The current prefix check in AssignHubsForm (loop over
$binding->device_name / $binding->base_device_name comparing $bindingName and
$name using str_starts_with + ctype_digit) is too permissive; either compare
stable identifiers (e.g. $binding->device_id / $binding->base_device_id against
the current device ID) if those IDs are available, or replace the
prefix+ctype_digit logic with a stricter anchored pattern that only treats exact
matches or the specific alias form you intend (for example only allow a single
separator plus numeric suffix like "name-123"); update the check around
$bindingName, $name and the str_starts_with branch accordingly so names like
"server-backup" or "serverx" no longer falsely count as the same device.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b6941ee5-3c63-4d0f-9e9c-16520826aa5d

📥 Commits

Reviewing files that changed from the base of the PR and between b90ea77 and 3360df4.

📒 Files selected for processing (3)
  • src/actions/AssignableHubs.php
  • src/assets/js/assign-hubs-column-reveal.js
  • src/forms/AssignHubsForm.php

Comment on lines 64 to +69
$model = clone $form;
$model::setModelClass($this->getAssignableClassName());
if ($model->load($hub, '') && $model->validate()) {
foreach ($model->toArray() as $key => $value) {
if (str_contains($key, '_')) {
$hub['hubs'][$key] = $value ?? '';
unset($hub[$key]);
}
foreach ($model->getHubPayloadAttributeNames() as $key) {
$hub['hubs'][$key] = $model->{$key} ?? '';
unset($hub[$key]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don't derive the hub payload whitelist from posted state.

$model is a blank clone here, so getHubPayloadAttributeNames() is driven by request-loaded attributes, including type. A crafted payload can switch to a broader variant set and also preserve extra keys inside an existing hubs array, because this code merges into $hub['hubs'] instead of rebuilding it. Resolve the allowed keys from the persisted model/type and replace hubs wholesale before sending the API payload.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/actions/AssignableHubs.php` around lines 64 - 69, The code currently
clones $form, loads request data into it, then calls
getHubPayloadAttributeNames() which causes the whitelist to be derived from the
posted state (including a crafted type) and merges into $hub['hubs']; instead,
create/resolve the allowed hub payload keys from the persisted model/type (use
setModelClass($this->getAssignableClassName()) on a fresh model instance or
otherwise instantiate the model for the persisted type without loading request
data), call getHubPayloadAttributeNames() on that trusted model to get the
whitelist, build a new array with only those keys from the request (do not
merge), assign it to $hub['hubs'] (replacing the whole array), and remove the
unset($hub[$key]) merge logic; use the same symbols shown (clone $form,
setModelClass, getAssignableClassName, getHubPayloadAttributeNames,
load/validate) to locate and update the code.

Comment on lines +306 to +313
foreach ([$binding->device_name ?? null, $binding->base_device_name ?? null] as $bindingName) {
if ($bindingName === null || $bindingName === '') {
continue;
}
$bindingName = (string)$bindingName;
$name = (string)$name;
if ($bindingName === $name || (str_starts_with($bindingName, $name) && !ctype_digit($bindingName[strlen($name)] ?? ''))) {
return true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tighten the own-binding name check.

Line 312 treats any non-digit suffix as “own”, so a device named server will also match bindings for server-backup or serverx. That can suppress the “already taken” error for a real conflict. Please constrain this to the exact alias pattern you intend to support, or compare stable identifiers instead of prefix-based names.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/forms/AssignHubsForm.php` around lines 306 - 313, The current prefix
check in AssignHubsForm (loop over $binding->device_name /
$binding->base_device_name comparing $bindingName and $name using
str_starts_with + ctype_digit) is too permissive; either compare stable
identifiers (e.g. $binding->device_id / $binding->base_device_id against the
current device ID) if those IDs are available, or replace the prefix+ctype_digit
logic with a stricter anchored pattern that only treats exact matches or the
specific alias form you intend (for example only allow a single separator plus
numeric suffix like "name-123"); update the check around $bindingName, $name and
the str_starts_with branch accordingly so names like "server-backup" or
"serverx" no longer falsely count as the same device.

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.

1 participant