Skip to content

Fix channel rule commands handling of null and correct property names#98

Open
m-hulbert wants to merge 3 commits intomainfrom
fix/channel-rules-commands
Open

Fix channel rule commands handling of null and correct property names#98
m-hulbert wants to merge 3 commits intomainfrom
fix/channel-rules-commands

Conversation

@m-hulbert
Copy link
Contributor

@m-hulbert m-hulbert commented Oct 10, 2025

Description

This PR fixes issues with the channel-rules commands;

  • channel-rules create
  • channel-rules update
  • channel-rules delete

channel-rules create

  • Was always failing because it was passing channelNamepsace instead of id in the request.
  • Correctly handles null being returned for batchingInterval and conflationInterval.

channel-rules update and channel-rules delete

  • These commands would complete but with an error. They now correctly handle null being returned for batchingInterval and conflationInterval.

Summary by CodeRabbit

  • New Features
    • None
  • Bug Fixes
    • More consistent display of batchingInterval and conflationInterval in create, update, and delete outputs, including proper handling of null/zero values.
  • Refactor
    • JSON output for channel rule namespaces now uses an id field for the namespace identifier, replacing the previous key.
  • Documentation
    • None
  • Tests
    • None
  • Chores
    • None

@m-hulbert m-hulbert requested a review from a team October 10, 2025 12:23
@m-hulbert m-hulbert self-assigned this Oct 10, 2025
@m-hulbert m-hulbert requested review from zknill and removed request for a team October 10, 2025 12:23
@vercel
Copy link

vercel bot commented Oct 10, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
cli-web-cli Ready Ready Preview Comment Oct 10, 2025 1:44pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 10, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Renamed Namespace payload field from channelNamespace to id and updated create command to map name to id. Adjusted logging conditions for batchingInterval and conflationInterval across create, update, and delete commands to use loose null checks (!= null). No changes to command signatures or overall control flow.

Changes

Cohort / File(s) Summary of edits
Channel rules commands: payload and logging checks
src/commands/apps/channel-rules/create.ts, src/commands/apps/channel-rules/update.ts, src/commands/apps/channel-rules/delete.ts
Create: map name -> Namespace.id (was channelNamespace); broaden interval logging checks to use != null. Update/Delete: replace strict undefined checks with loose null checks for batchingInterval and conflationInterval in human-readable output paths.
Control API types: Namespace shape
src/services/control-api.ts
Namespace interface change: remove channelNamespace:string; add id:string. No logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant CLI as Channel Rules CLI
  participant API as Control API

  U->>CLI: apps channel-rules create/update/delete
  Note over CLI: Build payload using Namespace.id<br/>(renamed from channelNamespace)
  CLI->>API: createNamespace/updateNamespace/deleteNamespace
  API-->>CLI: Response (Namespace)
  alt --json flag
    CLI-->>U: JSON output
  else human-readable
    Note over CLI: Log batchingInterval / conflationInterval<br/>when value != null
    CLI-->>U: Text summary
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

Thump goes my paw on the meadow’s floor,
We swapped a name, just one field more.
Intervals peek when not quite null,
Logs now gleam with a tidier pull.
I nibble docs in carrot hues—
Hop, hop! to ship these channel rules. 🥕🐇

Pre-merge checks and finishing touches

✅ 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 primary changes by referencing both the correction of property names and handling of null values in the channel rule commands, matching the pull request objectives in a concise, specific sentence.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/commands/apps/channel-rules/create.ts (1)

122-136: Update tests to expect id instead of channelNamespace
Tests in test/unit/commands/channel-rule/create.test.ts.skip and test/integration/control-api.test.ts still assert on channelNamespace in the POST body—change those to id to match the updated payload key.

src/services/control-api.ts (1)

221-238: Update tests to use id instead of channelNamespace
All assertions in test/unit/commands/channel-rule/create.test.ts.skip and test/integration/control-api.test.ts still post { channelNamespace: … }. Replace every channelNamespace key with id to match the updated payload.

🧹 Nitpick comments (4)
src/services/control-api.ts (2)

47-64: Align Namespace types with API nulls for intervals.

The API may return null for batchingInterval and conflationInterval. Reflect this in types to avoid unsoundness.

Apply:

 export interface Namespace {
   appId: string;
   authenticated?: boolean;
   batchingEnabled?: boolean;
-  batchingInterval?: number;
+  batchingInterval?: number | null;
   conflationEnabled?: boolean;
-  conflationInterval?: number;
+  conflationInterval?: number | null;
   conflationKey?: string;
   created: number;
   exposeTimeSerial?: boolean;
   id: string;
   modified: number;
   persistLast?: boolean;
   persisted: boolean;
   populateChannelRegistry?: boolean;
   pushEnabled: boolean;
   tlsOnly?: boolean;
 }

555-571: Preserve API error codes to enable friendly auth errors in commands.

To support handling error.code === 40100 in commands (per guidelines), include the response code when available.

As a minimal change, attach code if present:

-      let errorMessage = `API request failed (${response.status} ${response.statusText})`;
+      let errorMessage = `API request failed (${response.status} ${response.statusText})`;
+      let errorCode: unknown = undefined;
       if (
         typeof responseData === "object" &&
         responseData !== null &&
         "message" in responseData &&
         typeof responseData.message === "string"
       ) {
         errorMessage += `: ${responseData.message}`;
+        if ("code" in responseData) {
+          // preserve code for downstream handling
+          // eslint-disable-next-line @typescript-eslint/no-explicit-any
+          errorCode = (responseData as any).code;
+        }
       } else if (
         typeof responseData === "string" &&
         responseData.length < 100
       ) {
         // Include short string responses directly
         errorMessage += `: ${responseData}`;
       }
-      throw new Error(errorMessage);
+      const err = new Error(errorMessage) as Error & { code?: unknown; status?: number };
+      if (errorCode !== undefined) err.code = errorCode;
+      err.status = response.status;
+      throw err;

Commands can then check for (error as any).code === 40100 to print a login hint. As per coding guidelines.

src/commands/apps/channel-rules/delete.ts (1)

45-51: Avoid appId shadowing inside try.

Inner let appId shadows the outer variable, so catch blocks may log undefined appId. Assign to the outer variable instead.

Apply:

-      let appId = flags.app;
+      appId = flags.app;
       if (!appId) {
         appId = await this.resolveAppId(flags);
       }
src/commands/apps/channel-rules/create.ts (1)

94-100: Remove appId shadowing inside try.

Inner let appId hides the outer variable; errors in the try block will yield undefined appId in catch output. Assign to the outer variable.

Apply:

-      let appId = flags.app;
+      appId = flags.app;
       if (!appId) {
         appId = await this.resolveAppId(flags);
       }
📜 Review details

Configuration used: CodeRabbit 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 3da394f and f7d1b50.

📒 Files selected for processing (4)
  • src/commands/apps/channel-rules/create.ts (3 hunks)
  • src/commands/apps/channel-rules/delete.ts (2 hunks)
  • src/commands/apps/channel-rules/update.ts (2 hunks)
  • src/services/control-api.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/commands/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/AI-Assistance.mdc)

src/commands/**/*.ts: Commands should follow the oclif structure: export default class MyCommand extends Command { ... }
Command should support auth via the standard auth helper (import { getAuth } from '../../helpers/auth') and use it in the run method.
Use try/catch for error handling in commands, and provide user-friendly error messages (e.g., handle error.code === 40100 for authentication failures).
Do not use direct HTTP requests (e.g., fetch) for data plane APIs; use the Ably SDK instead.
Do not use hardcoded endpoint URLs; use configuration values (e.g., flags.controlHost, config.controlHost) when constructing URLs.
Do not use non-standard command structures (e.g., export default class { async execute(args) { ... } }); always use the oclif Command class.

Files:

  • src/commands/apps/channel-rules/update.ts
  • src/commands/apps/channel-rules/create.ts
  • src/commands/apps/channel-rules/delete.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/Development.mdc)

**/*.{ts,tsx}: Use TypeScript and follow best practice naming conventions
ESLint is used to ensure all code adheres best practices; ensure you check that all code passes the linter before concluding your work.

Files:

  • src/commands/apps/channel-rules/update.ts
  • src/commands/apps/channel-rules/create.ts
  • src/services/control-api.ts
  • src/commands/apps/channel-rules/delete.ts
src/commands/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/Development.mdc)

Follow oclif framework best practices as described in the oclif documentation.

Files:

  • src/commands/apps/channel-rules/update.ts
  • src/commands/apps/channel-rules/create.ts
  • src/commands/apps/channel-rules/delete.ts
⏰ 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). (3)
  • GitHub Check: setup
  • GitHub Check: e2e-cli
  • GitHub Check: test
🔇 Additional comments (3)
src/commands/apps/channel-rules/update.ts (1)

308-312: Good null-handling; avoids toString on null.

Switching to != null correctly skips logging when values are null or undefined, preventing runtime errors and matching API behavior.

Also applies to: 320-324

src/commands/apps/channel-rules/delete.ts (1)

138-142: Correct null-handling for interval logging.

Using != null avoids logging and formatting null values. Good fix.

Also applies to: 150-154

src/commands/apps/channel-rules/create.ts (1)

212-216: Correctly handle null intervals in output.

The != null checks prevent logging nulls and avoid calling toString on null. Good.

Also applies to: 224-228

);
}

if (createdNamespace.batchingInterval !== undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

should these be falsy checks?

        if (!createdNamespace.batchingInterval) {

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

Fixes the apps channel-rules CLI commands to use the correct namespace identifier field and to avoid errors when the Control API returns null for interval fields.

Changes:

  • Switch namespace create payload from channelNamespace to id.
  • Handle null values for batchingInterval and conflationInterval in create/update/delete command output.
  • Update integration test namespace creation payload to use id.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/integration/control-api.test.ts Updates namespace creation test payload to use id and normalizes whitespace.
src/services/control-api.ts Updates createNamespace input type to use id instead of channelNamespace.
src/commands/apps/channel-rules/create.ts Sends id in create request; adjusts interval display guards to handle null.
src/commands/apps/channel-rules/update.ts Adjusts interval display guards to handle null.
src/commands/apps/channel-rules/delete.ts Adjusts interval display guards to handle null.
Comments suppressed due to low confidence (6)

src/commands/apps/channel-rules/delete.ts:142

  • != null is a non-strict comparison and will be flagged by ESLint eqeqeq (enabled via recommended rules). Prefer a strict null/undefined guard (or typeof ... === "number") before calling .toString().
        if (namespace.batchingInterval != null) {
          this.log(
            `Batching Interval: ${chalk.green(namespace.batchingInterval.toString())}`,
          );
        }

src/commands/apps/channel-rules/delete.ts:154

  • Non-strict null check (!= null) will violate ESLint eqeqeq and is inconsistent with the strict checks used elsewhere in this file. Use a strict guard for null/undefined (or typeof ... === "number") before .toString().
        if (namespace.conflationInterval != null) {
          this.log(
            `Conflation Interval: ${chalk.green(namespace.conflationInterval.toString())}`,
          );
        }

src/commands/apps/channel-rules/create.ts:216

  • Non-strict null check (!= null) will trip ESLint's eqeqeq rule (enabled via the base recommended rules) and is inconsistent with the rest of this file’s strict !== undefined checks. Use a strict guard instead (e.g., !== null && !== undefined or typeof ... === "number") before calling .toString().
        if (createdNamespace.batchingInterval != null) {
          this.log(
            `Batching Interval: ${chalk.green(createdNamespace.batchingInterval.toString())}`,
          );
        }

src/commands/apps/channel-rules/create.ts:228

  • Non-strict null check (!= null) is likely to fail linting under eqeqeq and diverges from nearby strict !== undefined patterns. Prefer a strict guard (e.g., !== null && !== undefined or typeof ... === "number") before .toString().
        if (createdNamespace.conflationInterval != null) {
          this.log(
            `Conflation Interval: ${chalk.green(createdNamespace.conflationInterval.toString())}`,
          );
        }

src/commands/apps/channel-rules/update.ts:312

  • Using != null will violate ESLint eqeqeq and is inconsistent with the strict comparisons used elsewhere in this command. Switch to a strict null/undefined guard (or typeof ... === "number") before calling .toString().
        if (updatedNamespace.batchingInterval != null) {
          this.log(
            `Batching Interval: ${chalk.green(updatedNamespace.batchingInterval.toString())}`,
          );
        }

src/commands/apps/channel-rules/update.ts:324

  • Non-strict null check (!= null) will fail eqeqeq linting and differs from the surrounding strict !== undefined checks. Use a strict guard for null/undefined (or typeof ... === "number") before calling .toString().
        if (updatedNamespace.conflationInterval != null) {
          this.log(
            `Conflation Interval: ${chalk.green(updatedNamespace.conflationInterval.toString())}`,
          );
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 224 to 230
authenticated?: boolean;
batchingEnabled?: boolean;
batchingInterval?: number;
channelNamespace: string;
id: string;
conflationEnabled?: boolean;
conflationInterval?: number;
conflationKey?: string;
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

Now that the CLI explicitly handles batchingInterval/conflationInterval potentially being null, the Namespace model (and related method return types) should reflect that (number | null) rather than only number. Keeping the type accurate will prevent future code from accidentally calling number methods on null and removes the need for non-strict null checks in consumers.

Copilot uses AI. Check for mistakes.
Copy link

Choose a reason for hiding this comment

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

@coderabbitai do you think aboe suggestion is correct? Provide detailed explaination for the same

Copy link

Choose a reason for hiding this comment

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

@sacOO7, yes, Copilot's suggestion is correct. Here's a detailed explanation:

Current Type Definition

The Namespace interface currently defines these fields as:

batchingInterval?: number;
conflationInterval?: number;

The ? operator makes these optional properties, which means their type is number | undefined. A property is undefined when it's absent/not set.

The Problem

According to the PR objectives, the API can explicitly return null for these fields (not just omit them). However, null is not the same as undefined in TypeScript:

  • undefined = property is absent/not set
  • null = property is explicitly set to null

Since the current types are number | undefined, TypeScript won't catch type errors if code assumes these are always numbers when the property exists:

// This would compile fine with current types, but crash at runtime if value is null
const interval = namespace.batchingInterval; // Type: number | undefined
if (interval !== undefined) {
  console.log(interval.toFixed(2)); // ❌ Runtime error if interval is null
}

The Fix

The checks in your PR were changed from !== undefined to != null (loose equality) specifically because the API returns null. The types should match this reality:

batchingInterval?: number | null;
conflationInterval?: number | null;

This expands to number | null | undefined, which accurately represents:

  • Field might be absent (undefined)
  • Field might be explicitly null (null)
  • Field might contain a number

Recommendation

Update the Namespace interface to reflect the actual API behavior:

export interface Namespace {
  // ... other fields ...
  batchingInterval?: number | null;
  conflationInterval?: number | null;
  // ... other fields ...
}

This makes the type system more accurate and helps prevent runtime errors by forcing explicit null checks.

Copy link

Choose a reason for hiding this comment

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

@coderabbitai https://github.com/coderabbitai It's also suggested to change channelNamespace into id, is it expected or no?

Copy link

Choose a reason for hiding this comment

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

@sacOO7, yes, changing channelNamespace to id is expected and correct. Here's why:

The Namespace Interface Uses id

Looking at the Namespace interface (which is the return type of createNamespace), the field is defined as:

export interface Namespace {
  // ...
  id: string;  // ← The field name in the interface
  // ...
}

There is no channelNamespace field in the Namespace interface.

Input Should Match Output

The createNamespace method:

  • Accepts parameters to create a namespace
  • Returns a Namespace object

The parameter field names should align with the field names in the returned Namespace type. Since the API returns an object with an id field (not channelNamespace), the input parameter should also use id.

Bug Fix Per PR Objectives

The PR description explicitly states:

"Previously failed because it sent channelNamepsace instead of id in the request."

This confirms it was a bug - the API expects id as the field name, but the code was incorrectly sending channelNamespace.

Summary

The change is correct: The parameter should be id to match:

  1. The Namespace interface field name
  2. What the Control API expects
  3. Consistency between input parameters and output types

@ably ably deleted a comment from coderabbitai bot Feb 26, 2026
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.

4 participants