Skip to content

chore: update suggestions method to return prices#43

Merged
loks0n merged 16 commits intomasterfrom
fix-opensrs-issues
Oct 3, 2025
Merged

chore: update suggestions method to return prices#43
loks0n merged 16 commits intomasterfrom
fix-opensrs-issues

Conversation

@ChiragAgg5k
Copy link
Member

@ChiragAgg5k ChiragAgg5k commented Sep 25, 2025

Summary by CodeRabbit

  • New Features

    • Domain suggestions now aggregate multiple services, include premium items with pricing/availability, support filtering (limit/price), and de-duplicate results.
  • Documentation

    • Updated usage and API docs for the revised suggest method signature and example call.
  • Chores

    • Composer config updated to allow required package discovery plugins.
  • Tests

    • OpenSRS tests reworked to use new nameservers, run as standalone/dynamic domain tests, and updated test flows.

@coderabbitai
Copy link

coderabbitai bot commented Sep 25, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Reworked OpenSRS registrar adapter: suggest logic expanded to support limit and price filters, multi-service calls, XML sanitization/parsing, and service_override; buildEnvelop accepts nullable domain; constructor/API credentials adjusted; Registrar public suggest signature simplified; composer allow-plugins added; tests updated and nameservers changed.

Changes

Cohort / File(s) Summary
OpenSRS implementation
src/Domains/Registrar/OpenSRS.php
- Constructor signature changed (removed apiSecret) and header formatting adjusted for X-Username.
- Rewrote `suggest(array
Registrar API surface & adapter
src/Domains/Registrar.php, src/Domains/Registrar/Adapter.php, README.md
- Public Registrar::suggest signature simplified to suggest(array $query, array $tlds = []): array (removed min/max length parameters).
- Adapter interface updated to `suggest(array
Composer config
composer.json
- Added config.allow-plugins enabling php-http/discovery and tbachert/spi.
Tests: OpenSRS adjustments & nameservers
tests/OpenSRSTest.php
- Tests reworked to use newly generated domains or internal state instead of depending on a single provisioned domain; several test methods had signatures changed to remove parameters.
- Setup hardcodes kffsfudlvc.net and default nameservers changed to ns1.systemdns.com/ns2.systemdns.com.
- testSuggest expanded to cover limit and premium/price filtering; some tests add conditional skips and removed @depends links.
- generateRandomString made static.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant SDK as OpenSRS SDK
    participant API as OpenSRS API

    Client->>SDK: suggest(query, tlds, limit?, priceMax?, priceMin?)
    SDK->>API: name_suggest (services: ["suggestion","premium","lookup"], service_override?)
    API-->>SDK: XML response
    rect rgba(230,245,255,0.9)
      note right of SDK: sanitize XML response
      SDK->>SDK: sanitize(xml)
    end
    par Parse suggestions
      SDK->>SDK: XPath -> suggestion nodes
      SDK-->>SDK: suggestion entries (domain, available, type)
    and Parse premiums
      SDK->>SDK: XPath -> premium nodes
      SDK-->>SDK: premium entries (domain, price, available, type="premium")
    end
    SDK->>SDK: merge entries by domain -> unified array
    SDK-->>Client: consolidated suggestions array
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I hop through tags and XML light,
I sort suggestions day and night.
Premium carrots, plain seeds too,
I price and mark each name for you.
Composer opens gates — plugins in sight 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly highlights the core enhancement of the suggest method to return pricing information, which aligns with the signature expansion and parsing updates in the changeset. Although the PR includes additional refactors such as constructor adjustment and envelope builder improvements, the title accurately and concisely captures a primary functional change. It is specific and informative enough for a reviewer scanning the project history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-opensrs-issues

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

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5426adf and 9984a2d.

📒 Files selected for processing (3)
  • composer.json (1 hunks)
  • src/Domains/Registrar/OpenSRS.php (3 hunks)
  • tests/OpenSRSTest.php (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.28.0)
tests/OpenSRSTest.php

[high] 17-17: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (1)
composer.json (1)

35-41: LGTM on scoped plugin allowances

Thanks for tightening the Composer plugin policy—keeping the allow-plugins list narrowed to just php-http/discovery and tbachert/spi is the right move for Composer 2+.

@ChiragAgg5k ChiragAgg5k requested a review from Copilot September 25, 2025 11:34
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the OpenSRS domain suggestion functionality to include premium domains alongside regular suggestions. The changes merge premium domain suggestions with regular suggestions into a unified response format.

  • Enhanced domain suggestion service to include premium domain lookups
  • Updated response format to include domain type, availability status, and pricing information
  • Added composer configuration to allow specific plugins

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Domains/Registrar/OpenSRS.php Refactored suggest() method to process both regular and premium domain suggestions with enhanced data structure
composer.json Added plugin configuration to allow php-http/discovery and tbachert/spi plugins

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@ChiragAgg5k ChiragAgg5k changed the title chore: add missing space in x-username header chore: update suggestions method to return prices Sep 25, 2025
Copy link

@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: 4

🧹 Nitpick comments (3)
src/Domains/Registrar/OpenSRS.php (3)

326-331: Normalize availability status for premium entries

Be defensive against variants ('Available', 'true', '1').

-                    case 'status':
-                        $available = $value === 'available';
+                    case 'status':
+                        $available = in_array(strtolower($value), ['available', 'true', '1'], true);
                         break;

257-260: Optional: If 'lookup' stays, consider parsing its results

Currently, 'lookup' is requested but ignored. Either remove it to save a round-trip or parse it into $items similarly to 'premium' (domain/status). I can draft the parsing if you confirm the expected XML shape.


44-44: Minor: Header formatting consistency

Consider adding a space after the colon in headers for consistency (e.g., 'Content-Type: text/xml', 'X-Signature: ').

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4bb731 and bea0c6a.

📒 Files selected for processing (1)
  • src/Domains/Registrar/OpenSRS.php (5 hunks)
🧰 Additional context used
🪛 GitHub Actions: Tests
src/Domains/Registrar/OpenSRS.php

[error] 97-97: Authentication Error.


[error] 375-375: Authentication Error.


[error] 419-419: Authentication Error.

🔇 Additional comments (3)
src/Domains/Registrar/OpenSRS.php (3)

309-342: LGTM on premium parsing and merge

Parsing by keyed fields and merging into $items keyed by domain is solid. Price precision retained as float.


641-641: LGTM: buildEnvelop now tolerates null domain

The nullable domain param prevents emitting an empty domain tag. Looks correct.

Ensure all send() callers either pass domain explicitly when required by the action or include it under attributes as appropriate.


257-260: Use dt_array for services (no change needed)
OpenSRS name_suggest expects a dt_array of service names (lookup, suggestion, premium, personal_names, premium_brokered_transfer, premium_make_offer, etc.), not a dt_assoc of boolean flags.

Copy link

@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: 0

🧹 Nitpick comments (4)
src/Domains/Registrar.php (1)

26-29: Confirm intentional narrowing of suggest() input

Registrar::suggest now requires array, while Adapter allows array|string. If callers previously passed strings, this is a breaking change. Either keep array-only and document it clearly, or accept array|string for convenience.

-    public function suggest(array $query, array $tlds = []): array
+    public function suggest(array|string $query, array $tlds = []): array
     {
         return $this->adapter->suggest($query, $tlds);
     }
src/Domains/Registrar/Adapter.php (1)

24-29: Docblock-implementation mismatch

Doc says array $query but signature allows array|string. Update docblock for accuracy.

-    /**
-     * @param  array  $query
-     * @param  array  $tlds
-     * @return array
-     */
-    abstract public function suggest(array|string $query, array $tlds = []): array;
+    /**
+     * @param  array|string  $query
+     * @param  array  $tlds
+     * @return array
+     */
+    abstract public function suggest(array|string $query, array $tlds = []): array;
src/Domains/Registrar/OpenSRS.php (2)

230-246: Docblock mentions 'lookup' but results aren’t parsed

Either parse ‘lookup’ results or drop it from services and adjust docs. Suggest removing to ship safely now.

-     * - `lookup`: Performs availability checks on exact matches and variations

255-258: Request only services you parse

Currently ‘lookup’ is requested but unused. Remove it or add parsing for it. Minimal fix: remove it.

                 'services' => [
-                    'suggestion',  // Algorithmic domain suggestions
-                    'premium',     // Premium domain names with pricing
-                    'lookup'       // Availability lookup for exact matches
+                    'suggestion',  // Algorithmic domain suggestions
+                    'premium',     // Premium domain names with pricing
                 ],
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 721cc07 and f3f4596.

📒 Files selected for processing (4)
  • README.md (1 hunks)
  • src/Domains/Registrar.php (1 hunks)
  • src/Domains/Registrar/Adapter.php (1 hunks)
  • src/Domains/Registrar/OpenSRS.php (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/Domains/Registrar/Adapter.php (2)
src/Domains/Registrar.php (2)
  • suggest (26-29)
  • tlds (31-34)
src/Domains/Registrar/OpenSRS.php (2)
  • suggest (246-343)
  • tlds (345-349)
src/Domains/Registrar.php (2)
src/Domains/Registrar/Adapter.php (2)
  • suggest (28-28)
  • tlds (33-33)
src/Domains/Registrar/OpenSRS.php (2)
  • suggest (246-343)
  • tlds (345-349)
src/Domains/Registrar/OpenSRS.php (2)
src/Domains/Registrar.php (2)
  • suggest (26-29)
  • tlds (31-34)
src/Domains/Registrar/Adapter.php (2)
  • suggest (28-28)
  • tlds (33-33)
🪛 GitHub Actions: Tests
src/Domains/Registrar/OpenSRS.php

[error] 97-97: Authentication Error.


[error] 373-373: Authentication Error.


[error] 417-417: Authentication Error.

🔇 Additional comments (6)
README.md (1)

131-131: Docs synced with API change

Signature reflects array-only query; matches Registrar::suggest. If adapters accept string too, consider noting that only Registrar enforces array.

src/Domains/Registrar/OpenSRS.php (5)

281-292: Avoid positional indexing in XPath parsing

Relying on item[0]/item[1] is brittle. Select by key.

-        foreach ($suggestionElements as $element) {
-            $item = $element->xpath('dt_assoc/item');
-            $domain = (string) $item[0];
-            $available = (string) $item[1] === 'available';
-
-            $items[$domain] = [
-                'available' => $available,
-                'price' => null,
-                'type' => 'suggestion'
-            ];
-        }
+        foreach ($suggestionElements as $element) {
+            $domainNode = $element->xpath('dt_assoc/item[@key="domain"]');
+            $statusNode = $element->xpath('dt_assoc/item[@key="status"] | dt_assoc/item[@key="availability"]');
+            $domain = isset($domainNode[0]) ? (string)$domainNode[0] : null;
+            $status = isset($statusNode[0]) ? strtolower((string)$statusNode[0]) : '';
+            if ($domain) {
+                $items[$domain] = [
+                    'available' => in_array($status, ['available', 'true', '1'], true),
+                    'price' => null,
+                    'type' => 'suggestion',
+                ];
+            }
+        }

327-329: LGTM: price precision preserved

Casting to float retains cents; fixes prior truncation issue.


639-639: LGTM: buildEnvelop domain is now nullable

Cleaner envelope construction; avoids emitting empty domain field.


42-45: Critical: X-Username must use reseller username, not apiSecret

This likely causes auth failures (matches CI “Authentication Error”). Use $this->user['username'] and keep header formatting consistent.

-        $this->headers = [
-            'Content-Type:text/xml',
-            'X-Username: '.$this->apiSecret,
-        ];
+        $this->headers = [
+            'Content-Type: text/xml',
+            'X-Username: '.$this->user['username'],
+        ];

264-266: Guard against empty/failed HTTP response before XML parsing

curl_exec may return false/empty; sanitizeResponse will fatal on xpath. Add a guard.

-        $result = $this->send($message);
-        $result = $this->sanitizeResponse($result);
+        $result = $this->send($message);
+        if (!is_string($result) || $result === '') {
+            throw new Exception('Empty or invalid response from OpenSRS');
+        }
+        $result = $this->sanitizeResponse($result);

Copy link

@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: 0

🧹 Nitpick comments (2)
tests/OpenSRSTest.php (2)

44-53: Consider cleanup for purchased domains.

The test purchases a real domain but doesn't clean it up afterward. Over time, repeated test runs could accumulate purchased domains in the test account, potentially causing issues with account limits or incurring costs.

Consider adding a tearDown() method or an explicit cleanup step to delete or cancel test domains after use, if the OpenSRS test environment supports it.


123-137: Consider removing commented code.

Explicitly skipping this test with a clear explanation is better than having a test that always fails. However, consider removing the commented-out line 125 to reduce noise, as the comments below already explain why this test cannot run.

Apply this diff to remove the commented code:

 public function testTransfer(): void
 {
-    // $result = $this->client->transfer($domain, self::purchaseContact());
-
     // This will always fail mainly because it's a test env,
     // but also because:
     // - we use random domains to test
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f5f036 and 9cc529f.

📒 Files selected for processing (1)
  • tests/OpenSRSTest.php (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/OpenSRSTest.php (2)
src/Domains/Registrar/OpenSRS.php (6)
  • OpenSRS (8-720)
  • available (70-85)
  • getDomain (351-386)
  • updateNameservers (103-132)
  • updateDomain (388-421)
  • renew (423-467)
src/Domains/Registrar/Adapter.php (3)
  • available (13-13)
  • getDomain (39-39)
  • renew (46-46)
🔇 Additional comments (4)
tests/OpenSRSTest.php (4)

36-42: LGTM!

The test correctly generates a random domain and verifies availability. Using dynamic domain generation ensures test reliability and isolation.


116-118: Good addition: graceful handling of insufficient funds.

The conditional skip for accounts without sufficient funds is a practical improvement that prevents test failures due to test environment limitations.


23-23: Avoid hardcoded test domain
The hardcoded domain kffsfudlvc.net is only used in tests/OpenSRSTest.php and may cause flaky failures if it isn’t pre-registered or becomes unavailable. Consider one of the following:

  • Document that kffsfudlvc.net must be pre-registered in the test OpenSRS account
  • Dynamically purchase a domain in testPurchase() and reuse it for dependent tests
  • Mock the OpenSRS client to remove reliance on external state

63-68: Ensure testCancelPurchase is execution-order independent.
testCancelPurchase() calls cancel_pending_orders and asserts true; without a @Depends or creating a purchase here, it may be flaky if no pending orders exist. Consider either:

  • Adding @depends testPurchase
  • Invoking a purchase within this test
  • Mocking the API response to guarantee success

Copy link

@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

🧹 Nitpick comments (1)
src/Domains/Registrar/Adapter.php (1)

23-31: Docblock should reflect array|string query input

The signature now accepts array|string $query, but the docblock still documents @param array $query. Please update the annotation so consumers don’t miss the new scalar input support.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9cc529f and 5a9caa7.

📒 Files selected for processing (3)
  • src/Domains/Registrar/Adapter.php (1 hunks)
  • src/Domains/Registrar/OpenSRS.php (8 hunks)
  • tests/OpenSRSTest.php (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/Domains/Registrar/Adapter.php (2)
src/Domains/Registrar/OpenSRS.php (2)
  • suggest (238-379)
  • tlds (381-385)
src/Domains/Registrar.php (2)
  • suggest (26-29)
  • tlds (31-34)
tests/OpenSRSTest.php (1)
src/Domains/Registrar/OpenSRS.php (4)
  • OpenSRS (8-801)
  • available (68-83)
  • suggest (238-379)
  • renew (459-503)
src/Domains/Registrar/OpenSRS.php (3)
src/Domains/Registrar.php (3)
  • __construct (11-14)
  • suggest (26-29)
  • tlds (31-34)
src/Domains/Adapter.php (1)
  • __construct (28-39)
src/Domains/Registrar/Adapter.php (2)
  • suggest (31-31)
  • tlds (36-36)

Copy link

@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: 0

♻️ Duplicate comments (1)
tests/OpenSRSTest.php (1)

93-105: Loosen the assertCount(10, ...) expectation.

OpenSRS only promises "up to" the requested maximum and may return fewer than 10 suggestions in practice. The strict assertCount(10, $result) on Line 105 will fail intermittently.

Apply this diff to make the assertion more robust:

-        $this->assertCount(10, $result);
+        $this->assertLessThanOrEqual(10, count($result));
+        $this->assertGreaterThan(0, count($result));
🧹 Nitpick comments (1)
tests/OpenSRSTest.php (1)

15-33: Consider the test domain dependency architecture.

The refactoring creates a split approach where:

  • testAvailable and testPurchase generate random domains (good isolation)
  • testDomainInfo, testUpdateNameservers, testUpdateDomain, and testRenewDomain depend on the hard-coded 'kffsfudlvc.net' from setUp()

For consistency, consider one of these approaches:

  1. Purchase-based approach: Have setUp() call testPurchase() to create a domain and store it in $this->domain, so all tests use a freshly purchased domain
  2. Fixture approach: Document that 'kffsfudlvc.net' must be pre-provisioned in the test account
  3. Full isolation: Make each test independent by generating/purchasing domains as needed

The current mixed approach works but may confuse future maintainers about which domains exist and when.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a9caa7 and ef93e8e.

📒 Files selected for processing (2)
  • README.md (2 hunks)
  • tests/OpenSRSTest.php (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md
🧰 Additional context used
🧬 Code graph analysis (1)
tests/OpenSRSTest.php (1)
src/Domains/Registrar/OpenSRS.php (3)
  • OpenSRS (8-801)
  • suggest (238-379)
  • renew (459-503)
🔇 Additional comments (10)
tests/OpenSRSTest.php (10)

35-41: LGTM!

Generating a random domain for each availability check is good practice, reducing test interdependencies and improving test isolation.


43-52: LGTM!

Generating a random domain for purchase tests improves test isolation and reduces conflicts between test runs.


54-60: Verify the hard-coded domain exists in the test environment.

The test now depends on the hard-coded domain 'kffsfudlvc.net' from setUp(). Unlike testAvailable and testPurchase which generate random domains, this test will fail if the domain doesn't exist or isn't properly configured in the test environment.

Consider either:

  1. Documenting that this domain must be pre-provisioned in the OpenSRS test account
  2. Using the domain returned from testPurchase() via test dependencies
  3. Generating and purchasing a domain in setUp() for use across tests

69-91: LGTM! Clear test coverage for basic suggestions.

The test correctly validates that premium domains have prices and non-premium domains have null prices. Good separation of concerns.


107-128: LGTM! Premium price filtering test is well-structured.

The test correctly uses assertLessThanOrEqual(10, count($result)) to avoid brittleness and validates both the domain type and price ranges. The defensive null check on Line 124 is appropriate.


131-139: LGTM!

The change to use $this->domain is consistent with other test method updates. Note that this test shares the same dependency on the hard-coded domain as testDomainInfo.


141-154: LGTM!

The method signature change is consistent with the broader test refactoring. Shares the same domain dependency as other tests.


156-165: LGTM! Skip logic improves test robustness.

The addition of skip logic for insufficient funds (Lines 160-162) is a good practice for handling test environment limitations without failing the entire suite.


167-181: LGTM! Well-documented test skip.

The comprehensive comment clearly explains the technical limitations that prevent transfer testing in this environment. Good documentation practice.


208-219: LGTM! Static declaration resolves past issue.

Making generateRandomString() static aligns with its usage throughout the test class (self::generateRandomString()). This addresses the fatal error that would occur in PHP 8+ when calling a non-static method statically.

@loks0n loks0n merged commit d5f903e into master Oct 3, 2025
7 checks passed
@ChiragAgg5k ChiragAgg5k deleted the fix-opensrs-issues branch October 3, 2025 11:58
@coderabbitai coderabbitai bot mentioned this pull request Oct 15, 2025
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.

3 participants