Skip to content

User experience#141

Merged
jfilak merged 3 commits intojfilak:masterfrom
filak-sap:user_experience
Mar 26, 2026
Merged

User experience#141
jfilak merged 3 commits intojfilak:masterfrom
filak-sap:user_experience

Conversation

@filak-sap
Copy link
Copy Markdown
Contributor

No description provided.

Print system information retrieved from /sap/bc/adt/system/information
and /sap/bc/adt/core/http/systeminformation.

Renaming "systemID" to "SID" because that's what everybody would expect.
For easy checks that user has valid credentials configured for the given
endpoint.
Not all execution paths have handler e.g. "sapcli" and without this
patch, the process dies with a stacktrace for missing member and that is
not a nice user experience.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

The pull request introduces a system information retrieval module for ADT, adds endpoint-filtering functionality to GCTS user credentials, implements a CLI systeminfo command, adds a guard for missing CLI commands, and provides comprehensive test coverage for all new features.

Changes

Cohort / File(s) Summary
Documentation
doc/commands/gcts.md
Added optional `[-e
System Information Module
sap/adt/system.py
New module providing SystemInfoEntry and SystemInformation classes, concurrent XML/JSON fetching from ADT endpoints, and get_information() function with result merging.
CLI Entry Point
sap/cli/_entry.py
Added guard to validate execute attribute in parsed arguments; raises error with "No command specified" if missing.
ABAP CLI Command
sap/cli/abap.py
Added systeminfo CLI subcommand with optional --key argument for fetching and displaying system information entries.
GCTS CLI Enhancement
sap/cli/gcts.py
Extended get_user_credentials with --endpoint option; filters credentials by canonicalized endpoint and validates credential state.
Test Fixtures
test/unit/fixtures_adt_system.py
Added fixture data: SYSTEM_INFORMATION_XML, RESPONSE_SYSTEM_INFORMATION, JSON_SYSTEM_INFORMATION, and RESPONSE_JSON_SYSTEM_INFORMATION.
System Information Tests
test/unit/test_sap_adt_system.py
Comprehensive unit tests for SystemInformation, SystemInfoEntry, and get_information() including concurrent fetch verification, merging behavior, and state validation.
ABAP CLI Tests
test/unit/test_sap_cli_abap.py
Added TestAbapSystemInfo suite validating systeminfo command output, --key filtering, and HTTP request verification.
GCTS CLI Tests
test/unit/test_sap_cli_gcts.py
New TestgCTSUserGetCredentialsEndpoint suite covering endpoint filtering, canonicalization, state validation, and output formatting.
CLI Entry Tests
test/unit/test_sap_cli__entry.py
Enhanced test coverage with mock command/subcommand infrastructure and new TestParseCommandLineNoCommand suite validating missing command error handling.

Sequence Diagrams

sequenceDiagram
    participant Client
    participant Connection
    participant XMLEndpoint as ADT<br/>(system/information)
    participant JSONEndpoint as ADT<br/>(core/http/systeminformation)
    
    Client->>Connection: get_information()
    par Concurrent Fetches
        Connection->>XMLEndpoint: GET system/information
        XMLEndpoint-->>Connection: Atom XML feed
        Connection->>JSONEndpoint: GET core/http/systeminformation<br/>(Accept: SAP JSON)
        JSONEndpoint-->>Connection: JSON object
    and
    end
    
    Connection->>Connection: Parse XML entries
    Connection->>Connection: Parse JSON entries
    Connection->>Connection: Merge by identity<br/>(XML overwrites JSON)
    Connection-->>Client: SystemInformation<br/>(merged entries)
Loading
sequenceDiagram
    participant CLI
    participant GCTSCommand as get-credentials
    participant Filter as Endpoint Filter
    participant Output
    
    CLI->>GCTSCommand: Execute with --endpoint
    GCTSCommand->>GCTSCommand: Fetch all credentials
    GCTSCommand->>Filter: Canonicalize endpoint<br/>(trim, strip /, lowercase)
    Filter->>Filter: Filter credentials by<br/>canonicalized endpoint
    Filter->>Filter: Validate credential state<br/>(state != 'false')
    
    alt Match found & valid
        Filter-->>GCTSCommand: Filtered credentials
        GCTSCommand->>Output: Print matching entries
    else No match
        Filter-->>GCTSCommand: Empty result
        GCTSCommand->>Output: Error: No credentials
    else Match but invalid state
        Filter-->>GCTSCommand: Error
        GCTSCommand->>Output: Error: Invalid state
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'User experience' is vague and generic, not clearly describing what was changed or improved in this pull request. Use a more specific title that describes the main changes, such as 'Add system information command and gcts endpoint filtering' or similar.
Description check ❓ Inconclusive No pull request description was provided by the author. Add a description explaining the changes, their purpose, and how they improve user experience.

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

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

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: 1

🧹 Nitpick comments (4)
test/unit/test_sap_cli_abap.py (1)

160-173: Unused variable console can be prefixed with underscore.

The static analysis correctly identifies that console is unpacked but never used. Consider prefixing it to indicate intentional non-use.

♻️ Suggested fix
     def test_systeminfo_sends_requests(self):
         """Test that systeminfo sends GET requests to both endpoints"""
         connection = Connection([RESPONSE_SYSTEM_INFORMATION, RESPONSE_JSON_SYSTEM_INFORMATION])
         args = parse_args(['systeminfo'])
-        console, factory = make_console_factory()
+        _console, factory = make_console_factory()
         args.console_factory = factory
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/test_sap_cli_abap.py` around lines 160 - 173, In
test_systeminfo_sends_requests rename the unused local variable console (from
the tuple unpacking console, factory = make_console_factory()) to _console to
indicate intentional non-use; update the unpacking in the
test_systeminfo_sends_requests function so it reads _console, factory =
make_console_factory() while leaving Connection, parse_args, args, and the
subsequent assertions unchanged.
sap/cli/gcts.py (1)

112-128: Error message could be more informative when multiple invalid credentials exist.

When all matching credentials have state == 'false', the error message on line 126 only reports the first match's state. If there are multiple matches, users might not realize all credentials are invalid.

Consider clarifying this:

♻️ Suggested improvement
     if not valid:
-        raise SAPCliError(f'Credentials for endpoint {endpoint} are not valid: {matching[0]["state"]}')
+        raise SAPCliError(f'All {len(matching)} credential(s) for endpoint {endpoint} have invalid state')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sap/cli/gcts.py` around lines 112 - 128, In _filter_credentials_by_endpoint,
improve the error when all matching credentials are invalid by reporting either
all matching credential states or a clearer summary instead of using
matching[0]["state"]; collect the states from matching (e.g., [cred.get("state",
"") for cred in matching]) and include them or a count and the canonical
endpoint in the SAPCliError message raised when valid is empty so users can see
that multiple credentials exist and their states.
sap/adt/system.py (1)

53-53: Use defusedxml for safer XML parsing.

ElementTree.fromstring() is flagged as potentially vulnerable to XML attacks (e.g., billion laughs, external entity expansion). While the data comes from authenticated SAP connections, using defusedxml provides defense-in-depth. Note that defusedxml is not a complete drop-in replacement—it requires adding a new dependency and updating both sap/adt/system.py (line 53) and sap/adt/abapgit.py (2 instances) for consistency. The returned Element objects are compatible with stdlib ElementTree methods, so the refactor is straightforward.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sap/adt/system.py` at line 53, Replace stdlib XML parsing with defusedxml:
change the import and parsing call in sap/adt/system.py where root =
ElementTree.fromstring(resp.text) to use defusedxml's ElementTree (e.g., import
defusedxml.ElementTree as ET and call ET.fromstring(resp.text)); do the same for
the two occurrences in sap/adt/abapgit.py so all XML parsing uses defusedxml.
Update the import statements accordingly and ensure returned Element objects are
used the same way as before.
test/unit/test_sap_adt_system.py (1)

7-17: Remove unused imports.

Static analysis correctly identifies unused imports:

  • Request from mock (line 10)
  • SYSTEM_INFORMATION_XML from fixtures_adt_system (line 15)
  • JSON_SYSTEM_INFORMATION from fixtures_adt_system (line 16)
🧹 Proposed fix to remove unused imports
 from mock import (
     Connection,
     Response,
-    Request,
 )
 from fixtures_adt_system import (
     RESPONSE_SYSTEM_INFORMATION,
     RESPONSE_JSON_SYSTEM_INFORMATION,
-    SYSTEM_INFORMATION_XML,
-    JSON_SYSTEM_INFORMATION,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/test_sap_adt_system.py` around lines 7 - 17, Remove the unused
imports by deleting Request from the mock import tuple and removing
SYSTEM_INFORMATION_XML and JSON_SYSTEM_INFORMATION from the fixtures_adt_system
import tuple in test_sap_adt_system.py so only Connection and Response and only
RESPONSE_SYSTEM_INFORMATION and RESPONSE_JSON_SYSTEM_INFORMATION remain; update
the import lines that reference Request, SYSTEM_INFORMATION_XML, and
JSON_SYSTEM_INFORMATION to avoid unused symbol warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sap/adt/system.py`:
- Around line 53-59: The loop that builds entries from ElementTree.fromstring
can raise AttributeError when entry_elem.find(f'{XMLNS_ATOM}id') or
.find(f'{XMLNS_ATOM}title') returns None; update the loop that populates entries
(the for entry_elem in root.findall(...) block that creates
SystemInfoEntry(identity, title)) to defensively check the result of
entry_elem.find(...) before accessing .text, e.g., extract element =
entry_elem.find(...), skip or supply a safe default if element is None, and
optionally log or warn about missing id/title so you don’t call .text on None.

---

Nitpick comments:
In `@sap/adt/system.py`:
- Line 53: Replace stdlib XML parsing with defusedxml: change the import and
parsing call in sap/adt/system.py where root = ElementTree.fromstring(resp.text)
to use defusedxml's ElementTree (e.g., import defusedxml.ElementTree as ET and
call ET.fromstring(resp.text)); do the same for the two occurrences in
sap/adt/abapgit.py so all XML parsing uses defusedxml. Update the import
statements accordingly and ensure returned Element objects are used the same way
as before.

In `@sap/cli/gcts.py`:
- Around line 112-128: In _filter_credentials_by_endpoint, improve the error
when all matching credentials are invalid by reporting either all matching
credential states or a clearer summary instead of using matching[0]["state"];
collect the states from matching (e.g., [cred.get("state", "") for cred in
matching]) and include them or a count and the canonical endpoint in the
SAPCliError message raised when valid is empty so users can see that multiple
credentials exist and their states.

In `@test/unit/test_sap_adt_system.py`:
- Around line 7-17: Remove the unused imports by deleting Request from the mock
import tuple and removing SYSTEM_INFORMATION_XML and JSON_SYSTEM_INFORMATION
from the fixtures_adt_system import tuple in test_sap_adt_system.py so only
Connection and Response and only RESPONSE_SYSTEM_INFORMATION and
RESPONSE_JSON_SYSTEM_INFORMATION remain; update the import lines that reference
Request, SYSTEM_INFORMATION_XML, and JSON_SYSTEM_INFORMATION to avoid unused
symbol warnings.

In `@test/unit/test_sap_cli_abap.py`:
- Around line 160-173: In test_systeminfo_sends_requests rename the unused local
variable console (from the tuple unpacking console, factory =
make_console_factory()) to _console to indicate intentional non-use; update the
unpacking in the test_systeminfo_sends_requests function so it reads _console,
factory = make_console_factory() while leaving Connection, parse_args, args, and
the subsequent assertions unchanged.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3edd5433-08b2-44db-9c58-2a99d2341fd3

📥 Commits

Reviewing files that changed from the base of the PR and between aedf8e5 and 087652d.

📒 Files selected for processing (10)
  • doc/commands/gcts.md
  • sap/adt/system.py
  • sap/cli/_entry.py
  • sap/cli/abap.py
  • sap/cli/gcts.py
  • test/unit/fixtures_adt_system.py
  • test/unit/test_sap_adt_system.py
  • test/unit/test_sap_cli__entry.py
  • test/unit/test_sap_cli_abap.py
  • test/unit/test_sap_cli_gcts.py

Comment on lines +53 to +59
root = ElementTree.fromstring(resp.text)

entries = []
for entry_elem in root.findall(f'{XMLNS_ATOM}entry'):
identity = entry_elem.find(f'{XMLNS_ATOM}id').text
title = entry_elem.find(f'{XMLNS_ATOM}title').text
entries.append(SystemInfoEntry(identity, title))
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 | 🟡 Minor

Potential AttributeError if XML entry lacks id or title element.

If the server returns an <entry> without an <atom:id> or <atom:title> child, find() returns None and accessing .text will raise AttributeError. Consider adding defensive checks.

🛡️ Suggested defensive handling
     for entry_elem in root.findall(f'{XMLNS_ATOM}entry'):
-        identity = entry_elem.find(f'{XMLNS_ATOM}id').text
-        title = entry_elem.find(f'{XMLNS_ATOM}title').text
-        entries.append(SystemInfoEntry(identity, title))
+        id_elem = entry_elem.find(f'{XMLNS_ATOM}id')
+        title_elem = entry_elem.find(f'{XMLNS_ATOM}title')
+        if id_elem is not None and title_elem is not None:
+            entries.append(SystemInfoEntry(id_elem.text, title_elem.text))
🧰 Tools
🪛 Ruff (0.15.7)

[error] 53-53: Using xml to parse untrusted data is known to be vulnerable to XML attacks; use defusedxml equivalents

(S314)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sap/adt/system.py` around lines 53 - 59, The loop that builds entries from
ElementTree.fromstring can raise AttributeError when
entry_elem.find(f'{XMLNS_ATOM}id') or .find(f'{XMLNS_ATOM}title') returns None;
update the loop that populates entries (the for entry_elem in root.findall(...)
block that creates SystemInfoEntry(identity, title)) to defensively check the
result of entry_elem.find(...) before accessing .text, e.g., extract element =
entry_elem.find(...), skip or supply a safe default if element is None, and
optionally log or warn about missing id/title so you don’t call .text on None.

@jfilak jfilak merged commit 4ce6c12 into jfilak:master Mar 26, 2026
3 checks passed
This was referenced Mar 27, 2026
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