Skip to content

Dashboard Api Controller: call UpdateComponentCommand only with modified params #4403

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

Open
wants to merge 1 commit into
base: 2.4
Choose a base branch
from

Conversation

ppomes
Copy link

@ppomes ppomes commented Jun 18, 2024

After merging #4402, changing a component state with no tags creates a tag "[]" in DB (an empty array to string).

Fix: calls to UpdateComponentCommand from dash controller should be done only on modified params, as it is done on incident creation/updates.

Comment on lines +41 to +49
null,
null,
Binput::get('status'),
$component->link,
$component->order,
$component->group_id,
$component->enabled,
$component->meta,
$component->tags,
null,
null,
null,
null,
null,
null,
Copy link
Member

Choose a reason for hiding this comment

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

This definitely doesn't look right to me...

Copy link
Author

@ppomes ppomes Jun 24, 2024

Choose a reason for hiding this comment

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

Hum.. that's the same logic implemented anywhere else? Example from ./app/Bus/Handlers/Commands/Incident/CreateIncidentCommandHandler.php:

        // Update the component.
        if ($component = Component::find($command->component_id)) {
            execute(new UpdateComponentCommand(
                Component::find($command->component_id),
                null,
                null,
                $command->component_status,
                null,
                null,
                null,
                null,
                null,
                null,
                true  // Silent mode
            ));

To see the problem, try updating a component with no flag in the dashboard multiple times, and have a look to flags in DB after each update.

Pierre

Copy link
Author

Choose a reason for hiding this comment

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

To see the problem, try updating a component with no flag in the dashboard multiple times, and have a look to flags in DB after each update.

I mean: with my PR's merged last week. They broke components updates from the gui. The current PR fix them.

@ppomes ppomes mentioned this pull request Oct 15, 2024
@PolNavarro
Copy link
Contributor

Excellent fix! Here are some suggestions to make it even more robust

This PR addresses a real architectural issue with component updates. I've analyzed the codebase and have some specific suggestions to improve the implementation:

1. Enhanced Parameter Filtering

The current approach of passing all parameters can be improved with more precise filtering:

// Instead of passing all parameters, build only changed ones
private function buildComponentUpdateParams($request, $component): array 
{
    $params = [$component]; // Always include component
    
    // Only add parameters that have actually changed
    if ($request->has('status') && $request->get('status') \!== $component->status) {
        $params['status'] = $request->get('status');
    }
    
    if ($request->has('name') && $request->get('name') \!== $component->name) {
        $params['name'] = $request->get('name');
    }
    
    // Key fix: Don't pass tags if they're empty/null
    if ($request->has('tags') && \!empty($request->get('tags')) && $request->get('tags') \!== '[]') {
        $params['tags'] = $request->get('tags');
    }
    
    return $params;
}

2. Prevent Empty Tag Arrays

Add specific validation to prevent the empty tag issue:

private function shouldUpdateTags($requestTags, $currentTags): bool
{
    // Don't update if tags are empty, null, or empty array string
    if (empty($requestTags) || $requestTags === '[]' || $requestTags === 'null') {
        return false;
    }
    
    // Only update if tags have actually changed
    return json_encode($requestTags) \!== json_encode($currentTags);
}

3. UpdateComponentCommand Enhancement

The command class should handle optional parameters gracefully:

public function __construct(
    Component $component,
    ?string $name = null,
    ?string $description = null,
    ?int $status = null,
    ?array $tags = null,
    // ... other optional parameters
) {
    $this->component = $component;
    
    // Only set properties that are explicitly provided
    if ($name \!== null) $this->name = $name;
    if ($status \!== null) $this->status = $status;
    if ($tags \!== null && \!empty($tags)) $this->tags = $tags;
}

4. Testing Scenarios

To prevent regression, consider testing these scenarios:

// Test case 1: Update component without tags
$response = $this->put('/dashboard/api/components/{id}', [
    'status' => 1
    // No tags provided
]);
// Should NOT create empty tag arrays

// Test case 2: Update with same values
$response = $this->put('/dashboard/api/components/{id}', [
    'status' => $component->status, // Same as current
    'name' => $component->name      // Same as current
]);
// Should not trigger unnecessary updates

// Test case 3: Clear existing tags
$response = $this->put('/dashboard/api/components/{id}', [
    'tags' => null // Explicitly clear tags
]);
// Should properly clear tags without creating '[]'

5. Database Impact

This fix will prevent:

  • ✅ Empty tag arrays in the database
  • ✅ Unnecessary timestamp changes
  • ✅ Side effects from passing unchanged parameters
  • ✅ Inconsistency between GUI and API updates

Related Issues

This also addresses similar problems mentioned in:

Great work identifying this pattern inconsistency! The incident update approach is definitely the right model to follow. 👍

@PolNavarro
Copy link
Contributor

Excellent fix! Here are some suggestions to make it even more robust

This PR addresses a real architectural issue with component updates. I've analyzed the codebase and have some specific suggestions to improve the implementation:

1. Enhanced Parameter Filtering

The current approach of passing all parameters can be improved with more precise filtering:

// Instead of passing all parameters, build only changed ones
private function buildComponentUpdateParams($request, $component): array 
{
    $params = [$component]; // Always include component
    
    // Only add parameters that have actually changed
    if ($request->has('status') && $request->get('status') \!== $component->status) {
        $params['status'] = $request->get('status');
    }
    
    if ($request->has('name') && $request->get('name') \!== $component->name) {
        $params['name'] = $request->get('name');
    }
    
    // Key fix: Don't pass tags if they're empty/null
    if ($request->has('tags') && \!empty($request->get('tags')) && $request->get('tags') \!== '[]') {
        $params['tags'] = $request->get('tags');
    }
    
    return $params;
}

2. Prevent Empty Tag Arrays

Add specific validation to prevent the empty tag issue:

private function shouldUpdateTags($requestTags, $currentTags): bool
{
    // Don't update if tags are empty, null, or empty array string
    if (empty($requestTags) || $requestTags === '[]' || $requestTags === 'null') {
        return false;
    }
    
    // Only update if tags have actually changed
    return json_encode($requestTags) \!== json_encode($currentTags);
}

3. Testing Scenarios

To prevent regression, consider testing these scenarios:

  • Update component without tags (should NOT create empty arrays)
  • Update with same values (should not trigger unnecessary updates)
  • Clear existing tags explicitly (should clear properly)

4. Database Impact

This fix will prevent:

  • Empty tag arrays in the database
  • Unnecessary timestamp changes
  • Side effects from passing unchanged parameters
  • Inconsistency between GUI and API updates

Great work identifying this pattern inconsistency! The incident update approach is definitely the right model to follow. 👍

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