Skip to content

Conversation

@maratal
Copy link
Collaborator

@maratal maratal commented Jan 3, 2026

Closes #2163

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Fixed query parameter handling to eliminate duplicate parameters in URLs
    • Improved request ID consistency and tracking during retry attempts

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 3, 2026

Walkthrough

The changes replace the appendQueryItem: API with setQueryItemNamed:withValue: in NSMutableURLRequest (ARTUtils) category to replace rather than append query items with the same name. Implementation updates propagate the originalRequestId through retry attempts, test utilities gain String extraction helper methods, and RestClientTests consolidates test flow with a shared ARTRest instance.

Changes

Cohort / File(s) Summary
Query Item API Changes
Source/PrivateHeaders/Ably/ARTNSMutableURLRequest+ARTUtils.h, Source/ARTNSMutableURLRequest+ARTUtils.m
Replaced appendQueryItem:(NSURLQueryItem *)item with setQueryItemNamed:(NSString *)name withValue:(NSString *)value. New method removes existing items with the same name before inserting; safer nil-handling for mutableQueryItems initialization.
Request ID Propagation
Source/ARTRest.m
Updated call site to use setQueryItemNamed:@"request_id" instead of appendQueryItem:, and propagate originalRequestId as requestId in fallback retry paths.
Test Infrastructure
Test/AblyTests/Test Utilities/TestUtilities.swift
Added three String extension methods: substring(after:), substring(before:), and substring(between:andString:) for delimiter-based string extraction in tests.
Test Updates
Test/AblyTests/Tests/RestClientTests.swift
Consolidated test flow to use a shared ARTRest instance and TestProxyHTTPExecutor; replaced per-request assertions with unified post-publish validation of request count, hosts, and request_id consistency across all fallback attempts.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Duplicates bouncing away so fast,
Query items now won't take their last,
Request IDs travel through fallback's dance,
Tests share resources with careful stance,
Helpers extract strings left and right,
A cleaner request flow, oh what delight! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
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.
Out of Scope Changes check ❓ Inconclusive The test utility String extension methods added appear necessary for validating request_id extraction in tests, though they could be considered moderately out of scope for the core bug fix. Clarify whether the String extension methods in TestUtilities.swift are essential for the primary objective or if they should be separated into a different PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: replacing appendQueryItem with a new setQueryItemNamed method and ensuring originalRequestId propagation across retry attempts.
Linked Issues check ✅ Passed The code changes fully address the linked issue objectives: setQueryItemNamed replaces duplicate items with same name, originalRequestId is passed to recursive executeRequest calls, and shared request_id is maintained across fallback attempts.

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

✨ 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/2163-originalRequestId-propagation

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.

@maratal maratal changed the title Original request id propagation [ECO-5664] Original request id propagation Jan 3, 2026
@github-actions github-actions bot temporarily deployed to staging/pull/2172/features January 3, 2026 21:18 Inactive
@maratal maratal changed the base branch from main to 2156-rest-requests-building January 3, 2026 21:19
@github-actions github-actions bot temporarily deployed to staging/pull/2172/jazzydoc January 3, 2026 21:23 Inactive
@maratal
Copy link
Collaborator Author

maratal commented Jan 3, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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)
Test/AblyTests/Tests/RestClientTests.swift (1)

2077-2105: LGTM! Test effectively verifies request_id propagation.

The test correctly validates that a single request_id is propagated across all retry attempts (primary + 2 fallbacks). This aligns with the PR objectives to fix request ID propagation during fallback retries.

Optional: More robust query parameter extraction

Line 2103 uses substring(after: "request_id=") which extracts everything after the delimiter. If other query parameters follow request_id, they would be included in the extracted value. While the test still works (since the structure is consistent), you could make it more robust:

-let requestIds = capturedURLs.map { $0.substring(after: "request_id=") }
+let requestIds = capturedURLs.compactMap { url in
+    guard let urlObj = URL(string: url),
+          let components = URLComponents(url: urlObj, resolvingAgainstBaseURL: true),
+          let requestId = components.queryItems?.first(where: { $0.name == "request_id" })?.value else {
+        return nil
+    }
+    return requestId
+}

This uses proper URL parsing instead of string manipulation, making the test more resilient to URL structure changes.

Source/ARTNSMutableURLRequest+ARTUtils.m (1)

5-32: Consider adding nil checks for parameters.

The implementation correctly removes existing query items with the same name before adding the new one, ensuring uniqueness. However, the method doesn't validate the name and value parameters.

Add parameter validation

While NSURLQueryItem may handle nil values, it's better to be explicit about expectations:

 - (void)setQueryItemNamed:(NSString *)name withValue:(NSString *)value {
+    if (name == nil || value == nil) {
+        return;
+    }
+    
     NSURLComponents *components = [NSURLComponents componentsWithURL:self.URL resolvingAgainstBaseURL:YES];

This makes the API contract clearer and prevents potential issues if NSURLQueryItem behavior changes or if nil values cause unexpected query string formatting.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 58952a8 and 82c8759.

📒 Files selected for processing (5)
  • Source/ARTNSMutableURLRequest+ARTUtils.m
  • Source/ARTRest.m
  • Source/PrivateHeaders/Ably/ARTNSMutableURLRequest+ARTUtils.h
  • Test/AblyTests/Test Utilities/TestUtilities.swift
  • Test/AblyTests/Tests/RestClientTests.swift
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-10T14:14:47.456Z
Learnt from: lawrence-forooghian
Repo: ably/ably-cocoa PR: 2162
File: Source/ARTJsonLikeEncoder.m:522-525
Timestamp: 2025-12-10T14:14:47.456Z
Learning: In Source/ARTJsonLikeEncoder.m, ensure that artMessage.actionIsInternallySet is only set to YES when the SDK explicitly sets the action field (e.g., in ARTMessage initWithName:data:). Messages decoded from the wire must not have actionIsInternallySet set, even if they contain an action field, because the flag should distinguish SDK-internal actions from wire-provided actions. Apply this behavior broadly to Objective-C files in the Source directory that involve message encoding/decoding, and add tests to verify that decoding does not flip the flag.

Applied to files:

  • Source/ARTNSMutableURLRequest+ARTUtils.m
  • Source/ARTRest.m
🔇 Additional comments (4)
Test/AblyTests/Test Utilities/TestUtilities.swift (1)

1545-1572: LGTM! Clean string utilities for test assertions.

The three substring helper methods are well-implemented and will be useful for extracting query parameter values in tests. They handle missing delimiters gracefully by returning nil.

Source/PrivateHeaders/Ably/ARTNSMutableURLRequest+ARTUtils.h (1)

12-12: LGTM! API change clarifies intent.

The new setQueryItemNamed:withValue: method name better communicates that it replaces any existing query item with the same name, rather than appending a duplicate. The simpler signature (taking name and value directly) is also more convenient.

Source/ARTRest.m (2)

504-513: LGTM! Request ID generation logic is correct.

The code correctly handles request ID propagation:

  • First request (fallbacks == nil): generates a new UUID-based request ID
  • Retry requests (fallbacks != nil): reuses the original request ID from the first attempt

This ensures all retries for a given request share the same request_id value.


447-460: LGTM! Core fix for request ID propagation.

This is the key fix described in the PR objectives. By passing requestId to the originalRequestId: parameter at line 457, the request ID is correctly propagated through the entire fallback retry chain. Previously, originalRequestId would have been nil on the first retry, causing either a missing request_id or a new one to be generated.

The fix ensures:

  • All fallback attempts share the same request_id from the original request
  • ARTErrorInfo instances for fallback attempts carry the correct requestId

@maratal maratal force-pushed the fix/2163-originalRequestId-propagation branch from 82c8759 to af36992 Compare January 8, 2026 23:09
@maratal maratal changed the base branch from 2156-rest-requests-building to main January 8, 2026 23:10
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 (3)
Test/AblyTests/Tests/RestClientTests.swift (1)

2081-2104: Avoid brittle request_id extraction via substring; parse the query instead.

substring(after: "request_id=") can silently produce wrong values if the parameter is missing, reordered, followed by other params, or encoded. Prefer the existing extractURLQueryValue(..., key:) helper and assert non-nil IDs before comparing.

Proposed change
-        let capturedURLs = requests.map { $0.url!.absoluteString }
+        let capturedURLs = requests.map { $0.url!.absoluteString }
         XCTAssertTrue(NSRegularExpression.match(capturedURLs.at(0), pattern: "//rest.ably.io"))
         XCTAssertTrue(NSRegularExpression.match(capturedURLs.at(1), pattern: "//[a-e].ably-realtime.com"))
         XCTAssertTrue(NSRegularExpression.match(capturedURLs.at(2), pattern: "//[a-e].ably-realtime.com"))
         
-        let requestIds = capturedURLs.map { $0.substring(after: "request_id=") }
-        XCTAssertEqual(Set(requestIds).count, 1) // all requestIds are equal
+        let requestIds = requests.compactMap { extractURLQueryValue($0.url, key: "request_id") }
+        XCTAssertEqual(requestIds.count, requests.count) // every request has request_id
+        XCTAssertEqual(Set(requestIds).count, 1) // all requestIds are equal
Source/PrivateHeaders/Ably/ARTNSMutableURLRequest+ARTUtils.h (1)

12-12: Good API shape; consider documenting replacement semantics.

Source/ARTNSMutableURLRequest+ARTUtils.m (1)

5-32: Correct duplicate-removal, but note it can reorder query params (remove + append).
If you want to preserve the original position of an existing query item, update the first match in-place and only remove subsequent duplicates.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 82c8759 and af36992.

📒 Files selected for processing (7)
  • Source/ARTNSMutableURLRequest+ARTUtils.m
  • Source/ARTRest.m
  • Source/PrivateHeaders/Ably/ARTNSMutableURLRequest+ARTUtils.h
  • Source/include/Ably/ARTChannelProtocol.h
  • Source/include/Ably/ARTMessageOperation.h
  • Test/AblyTests/Test Utilities/TestUtilities.swift
  • Test/AblyTests/Tests/RestClientTests.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • Test/AblyTests/Test Utilities/TestUtilities.swift
  • Source/ARTRest.m
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-10T14:14:47.456Z
Learnt from: lawrence-forooghian
Repo: ably/ably-cocoa PR: 2162
File: Source/ARTJsonLikeEncoder.m:522-525
Timestamp: 2025-12-10T14:14:47.456Z
Learning: In Source/ARTJsonLikeEncoder.m, ensure that artMessage.actionIsInternallySet is only set to YES when the SDK explicitly sets the action field (e.g., in ARTMessage initWithName:data:). Messages decoded from the wire must not have actionIsInternallySet set, even if they contain an action field, because the flag should distinguish SDK-internal actions from wire-provided actions. Apply this behavior broadly to Objective-C files in the Source directory that involve message encoding/decoding, and add tests to verify that decoding does not flip the flag.

Applied to files:

  • Source/ARTNSMutableURLRequest+ARTUtils.m
🔇 Additional comments (2)
Source/include/Ably/ARTMessageOperation.h (1)

8-20: Doc updates look consistent and clearer.

Source/include/Ably/ARTChannelProtocol.h (1)

73-113: Doc updates are helpful and align with expected semantics.

…d when connecting via fallback hosts.

`appendQueryItem:` adds request_id each time, so to avoid multiple additions I've replaced it with `setQueryItemNamed:`.
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 (1)
Test/AblyTests/Tests/RestClientTests.swift (1)

2103-2104: Verify non-nil request_id values before equality check.

The test correctly validates that all requests share the same request_id, but the current implementation using substring(after:) could be more robust. If request_id is followed by other query parameters, the extracted values would include those additional parameters, potentially causing false positives.

Consider these improvements:

♻️ Suggested improvements for robustness
 let requestIds = capturedURLs.map { $0.substring(after: "request_id=") }
+// Verify all request_ids are non-nil
+XCTAssertTrue(requestIds.allSatisfy { $0 != nil && !$0!.isEmpty })
+
+// Extract just the request_id value (before any subsequent query params)
+let cleanRequestIds = requestIds.compactMap { $0?.substring(before: "&") ?? $0 }
+XCTAssertEqual(cleanRequestIds.count, 3)
-XCTAssertEqual(Set(requestIds).count, 1) // all requestIds are equal
+XCTAssertEqual(Set(cleanRequestIds).count, 1) // all requestIds are equal
+
+// Verify the request_id is a valid UUID-like string
+let requestId = cleanRequestIds.first!
+XCTAssertGreaterThan(requestId.count, 0)

Alternative: Use the existing extractURLQueryValue(_:key:) helper (line 738) for more reliable extraction:

-let requestIds = capturedURLs.map { $0.substring(after: "request_id=") }
+let requestIds = requests.compactMap { extractURLQueryValue($0.url, key: "request_id") }
+XCTAssertEqual(requestIds.count, 3)
 XCTAssertEqual(Set(requestIds).count, 1) // all requestIds are equal
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between af36992 and 20d8384.

📒 Files selected for processing (5)
  • Source/ARTNSMutableURLRequest+ARTUtils.m
  • Source/ARTRest.m
  • Source/PrivateHeaders/Ably/ARTNSMutableURLRequest+ARTUtils.h
  • Test/AblyTests/Test Utilities/TestUtilities.swift
  • Test/AblyTests/Tests/RestClientTests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Source/ARTRest.m
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-10T14:14:47.456Z
Learnt from: lawrence-forooghian
Repo: ably/ably-cocoa PR: 2162
File: Source/ARTJsonLikeEncoder.m:522-525
Timestamp: 2025-12-10T14:14:47.456Z
Learning: In Source/ARTJsonLikeEncoder.m, ensure that artMessage.actionIsInternallySet is only set to YES when the SDK explicitly sets the action field (e.g., in ARTMessage initWithName:data:). Messages decoded from the wire must not have actionIsInternallySet set, even if they contain an action field, because the flag should distinguish SDK-internal actions from wire-provided actions. Apply this behavior broadly to Objective-C files in the Source directory that involve message encoding/decoding, and add tests to verify that decoding does not flip the flag.

Applied to files:

  • Source/ARTNSMutableURLRequest+ARTUtils.m
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: check (macOS, test_macOS)
  • GitHub Check: check (iOS, test_iOS18_4)
  • GitHub Check: check (tvOS, test_tvOS18_4)
  • GitHub Check: build
  • GitHub Check: check
  • GitHub Check: check
🔇 Additional comments (7)
Source/PrivateHeaders/Ably/ARTNSMutableURLRequest+ARTUtils.h (1)

12-12: LGTM! API change appropriately reflects replacement semantics.

The method signature change from appendQueryItem: to setQueryItemNamed:withValue: is well-designed. The "set" terminology correctly communicates that the method replaces (rather than appends) query items with the same name, which aligns with the PR objective to prevent duplicate request_id parameters.

Source/ARTNSMutableURLRequest+ARTUtils.m (3)

5-9: LGTM! Proper early return guard.

The method signature and early return pattern are correct and consistent with the existing replaceHostWith: method in the same file.


11-20: LGTM! Safe and efficient duplicate removal.

The implementation correctly handles the removal of existing query items with the same name:

  • Uses NSMutableIndexSet to collect indices, avoiding mutation-during-iteration issues
  • The ?: @[] fallback safely handles nil queryItems
  • removeObjectsAtIndexes: is the appropriate method for batch removal

22-32: LGTM! Correct query item addition and URL update.

The implementation properly:

  • Creates a new NSURLQueryItem with the provided name and value
  • Updates components.queryItems with the modified array
  • Guards against nil when updating self.URL

The pattern is consistent with the existing replaceHostWith: method.

Test/AblyTests/Test Utilities/TestUtilities.swift (3)

1545-1551: LGTM! Correct implementation.

The substring(after:) method correctly returns the portion of the string after the first occurrence of the delimiter, or nil if not found.


1553-1559: LGTM! Correct implementation.

The substring(before:) method correctly returns the portion of the string before the first occurrence of the delimiter, or nil if not found.


1561-1572: LGTM! Correct implementation.

The substring(between:andString:) method correctly:

  • Finds the first delimiter and returns nil if not found
  • Searches for the second delimiter only in the remaining string after the first
  • Returns the substring between the two delimiters

The implementation properly handles edge cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

originalRequestId propagation in executeRequest

2 participants