Skip to content

Conversation

@thearifismail
Copy link
Contributor

@thearifismail thearifismail commented Dec 12, 2025

Overview

This PR is being created to address RHINENG-21925. Just account key:value to the serialized group to keep the returned goup same as before.

PR Checklist

  • Keep PR title short, ideally under 72 characters
  • Descriptive comments provided in complex code blocks
  • Include raw query examples in the PR description, if adding/modifying SQL query
  • Tests: validate optimal/expected output
  • Tests: validate exceptions and failure scenarios
  • Tests: edge cases
  • Recovers or fails gracefully during potential resource outages (e.g. DB, Kafka)
  • Uses type hinting, if convenient
  • Documentation, if this PR changes the way other services interact with host inventory
  • Links to related PRs

Secure Coding Practices Documentation Reference

You can find documentation on this checklist here.

Secure Coding Checklist

  • Input Validation
  • Output Encoding
  • Authentication and Password Management
  • Session Management
  • Access Control
  • Cryptographic Practices
  • Error Handling and Logging
  • Data Protection
  • Communication Security
  • System Configuration
  • Database Security
  • File Management
  • Memory Management
  • General Coding Practices

Summary by Sourcery

Bug Fixes:

  • Prevent missing or empty account values in serialized groups by falling back to the current identity account number when available.

@thearifismail thearifismail requested a review from a team as a code owner December 12, 2025 23:55
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Dec 12, 2025

Reviewer's Guide

Ensures the serialized group dictionary always includes an account field by deriving it from the group when present or falling back to the current identity’s account number when missing or empty, while safely handling missing request context or identity data.

Sequence diagram for serialize_group_without_host_count account resolution

sequenceDiagram
    participant Caller
    participant Serialization as serialize_group_without_host_count
    participant Group
    participant Auth as get_current_identity
    participant Identity

    Caller->>Serialization: serialize_group_without_host_count(group)
    Serialization->>Group: getattr(group, account)
    alt group.account is not None and not empty
        Serialization-->>Caller: dict with account = group.account
    else group.account is None or empty
        Serialization->>Auth: get_current_identity()
        alt request context and identity available
            Auth-->>Serialization: Identity
            Serialization->>Identity: getattr(identity, account_number)
            alt identity.account_number is not None and not empty
                Serialization-->>Caller: dict with account = identity.account_number
            else identity.account_number is None or empty
                Serialization-->>Caller: dict with account = None
            end
        else RuntimeError or AttributeError
            Auth-->>Serialization: raises RuntimeError/AttributeError
            Serialization-->>Caller: dict with account = None
        end
    end
Loading

Flow diagram for account selection in serialize_group_without_host_count

flowchart TD
    A[Start serialize_group_without_host_count] --> B[Set account = None]
    B --> C[Get group_account = getattr group, account]
    C --> D{group_account is not None
and group_account != ""}
    D -->|Yes| E[Set account = group_account]
    D -->|No| F[Import get_current_identity]
    F --> G[Call get_current_identity]
    G --> H{RuntimeError or
AttributeError?}
    H -->|Yes| I[Keep account = None]
    H -->|No| J[identity = get_current_identity result]
    J --> K[identity_account = getattr identity, account_number]
    K --> L{identity_account is not None
and identity_account != ""}
    L -->|Yes| M[Set account = identity_account]
    L -->|No| N[Keep account = None]
    E --> O[Build result dict with account]
    I --> O
    M --> O
    N --> O
    O --> P[Return result dict]
    P --> Q[End]
Loading

File-Level Changes

Change Details Files
Make serialized groups always include an account value, falling back to the current identity when the group’s account is missing or empty.
  • Introduce local account variable in serialize_group_without_host_count and compute its value before returning the payload.
  • Prefer the group’s account attribute when it exists and is non-empty, otherwise attempt to read account_number from the current identity via get_current_identity.
  • Handle RuntimeError and AttributeError when fetching identity, defaulting account to None if identity or account_number is unavailable.
  • Replace the direct group.account reference in the returned dict with the computed account value.
app/serialization.py

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

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • The account resolution logic in serialize_group_without_host_count is quite verbose; consider simplifying it (e.g., group.account or identity.account_number or None with a small helper for non-empty strings) to improve readability and reduce repeated None/empty checks.
  • Catching (RuntimeError, AttributeError) around get_current_identity() makes it easy to hide unexpected errors; it would be safer to narrow the exception handling to the specific failure modes you expect or to log when identity resolution fails.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The account resolution logic in `serialize_group_without_host_count` is quite verbose; consider simplifying it (e.g., `group.account or identity.account_number or None` with a small helper for non-empty strings) to improve readability and reduce repeated `None`/empty checks.
- Catching `(RuntimeError, AttributeError)` around `get_current_identity()` makes it easy to hide unexpected errors; it would be safer to narrow the exception handling to the specific failure modes you expect or to log when identity resolution fails.

## Individual Comments

### Comment 1
<location> `app/serialization.py:264-268` </location>
<code_context>
+        # Account is missing from group or has no value, try to get from identity
+        from app.auth import get_current_identity
+
+        try:
+            identity = get_current_identity()
+            identity_account = getattr(identity, "account_number", None)
+            account = identity_account if identity_account is not None and identity_account != "" else None
+        except (RuntimeError, AttributeError):
+            # No request context available or identity missing account_number
+            account = None
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Narrowing the AttributeError catch or validating identity before attribute access could avoid masking unrelated bugs

Catching `AttributeError` over both `get_current_identity()` and `getattr` can hide real bugs inside `get_current_identity`. Consider calling `identity = get_current_identity()` outside the `try`, and only wrapping the attribute access (or relying on `getattr` and just catching `RuntimeError`). This keeps unexpected `AttributeError`s from being silently turned into `account = None`.
</issue_to_address>

### Comment 2
<location> `app/serialization.py:252` </location>
<code_context>


 def serialize_group_without_host_count(group: Group) -> dict:
+    # Handle account field: if missing or has no value, try to get from identity
+    account = None
+
</code_context>

<issue_to_address>
**issue (complexity):** Consider extracting the account resolution logic into a dedicated helper to keep the serializer focused on shaping the response data.

You can reduce the added complexity by extracting the account resolution into a small helper and leveraging truthiness instead of repeated `is not None and != ""` checks.

For example:

```python
def _resolve_group_account(group: Group) -> str | None:
    group_account = getattr(group, "account", None)
    if group_account:  # non-empty string
        return group_account

    from app.auth import get_current_identity

    try:
        identity = get_current_identity()
        identity_account = getattr(identity, "account_number", None)
        return identity_account or None  # normalize empty string / falsy to None
    except (RuntimeError, AttributeError):
        return None
```

Then your serializer becomes simpler and focused only on shaping the response:

```python
def serialize_group_without_host_count(group: Group) -> dict:
    return {
        "id": _serialize_uuid(group.id),
        "org_id": group.org_id,
        "account": _resolve_group_account(group),
        "name": group.name,
        "ungrouped": group.ungrouped,
        "created": _serialize_datetime(group.created_on),
        "updated": _serialize_datetime(group.modified_on),
    }
```

This preserves the current behavior (group account → identity account → `None`) while moving the inline control flow, dynamic import, and exception handling into a dedicated helper.
</issue_to_address>

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.

Comment on lines 264 to 268
try:
identity = get_current_identity()
identity_account = getattr(identity, "account_number", None)
account = identity_account if identity_account is not None and identity_account != "" else None
except (RuntimeError, AttributeError):
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Narrowing the AttributeError catch or validating identity before attribute access could avoid masking unrelated bugs

Catching AttributeError over both get_current_identity() and getattr can hide real bugs inside get_current_identity. Consider calling identity = get_current_identity() outside the try, and only wrapping the attribute access (or relying on getattr and just catching RuntimeError). This keeps unexpected AttributeErrors from being silently turned into account = None.



def serialize_group_without_host_count(group: Group) -> dict:
# Handle account field: if missing or has no value, try to get from identity
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (complexity): Consider extracting the account resolution logic into a dedicated helper to keep the serializer focused on shaping the response data.

You can reduce the added complexity by extracting the account resolution into a small helper and leveraging truthiness instead of repeated is not None and != "" checks.

For example:

def _resolve_group_account(group: Group) -> str | None:
    group_account = getattr(group, "account", None)
    if group_account:  # non-empty string
        return group_account

    from app.auth import get_current_identity

    try:
        identity = get_current_identity()
        identity_account = getattr(identity, "account_number", None)
        return identity_account or None  # normalize empty string / falsy to None
    except (RuntimeError, AttributeError):
        return None

Then your serializer becomes simpler and focused only on shaping the response:

def serialize_group_without_host_count(group: Group) -> dict:
    return {
        "id": _serialize_uuid(group.id),
        "org_id": group.org_id,
        "account": _resolve_group_account(group),
        "name": group.name,
        "ungrouped": group.ungrouped,
        "created": _serialize_datetime(group.created_on),
        "updated": _serialize_datetime(group.modified_on),
    }

This preserves the current behavior (group account → identity account → None) while moving the inline control flow, dynamic import, and exception handling into a dedicated helper.

Copy link
Collaborator

@kruai kruai left a comment

Choose a reason for hiding this comment

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

This PR fetches the account number during serialization if it doesn't exist. This both doesn't set the account on the Group record, and adds inefficiency when serializing groups that just don't have account data.
I recommend checking the event we consume from the workspaces outbox topic to see if the account number is populated. If not, we can double-check that we're sending the account number in the RBAC POST API request, and go from there.

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