Skip to content

Commit 1968dba

Browse files
authored
Merge pull request #174 from ably/DX-255-error-codes
[DX-255] Await subscribe to handle errors and add friendly error hints
2 parents 05d5bca + ee319d8 commit 1968dba

21 files changed

Lines changed: 640 additions & 268 deletions

File tree

src/base-command.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from "./services/config-manager.js";
1111
import { ControlApi } from "./services/control-api.js";
1212
import { CommandError } from "./errors/command-error.js";
13+
import { getFriendlyAblyErrorHint } from "./utils/errors.js";
1314
import { coreGlobalFlags } from "./flags.js";
1415
import { InteractiveHelper } from "./services/interactive-helper.js";
1516
import { BaseFlags, CommandConfig } from "./types/cli.js";
@@ -939,9 +940,10 @@ export abstract class AblyBaseCommand extends InteractiveBaseCommand {
939940
logData,
940941
);
941942
} else if (level <= 1) {
942-
// Standard Non-JSON: Log only SDK ERRORS (level <= 1) clearly
943-
// Use a format similar to logCliEvent's non-JSON output
944-
this.log(`${chalk.red.bold(`[AblySDK Error]`)} ${message}`);
943+
// SDK errors are handled by setupChannelStateLogging() and fail()
944+
// Only show raw SDK errors in verbose mode (handled above)
945+
// In non-verbose mode, log to stderr for debugging without polluting stdout
946+
this.logToStderr(`${chalk.red.bold(`[AblySDK Error]`)} ${message}`);
945947
}
946948
// If not verbose non-JSON and level > 1, suppress non-error SDK logs
947949
}
@@ -1515,12 +1517,27 @@ export abstract class AblyBaseCommand extends InteractiveBaseCommand {
15151517
},
15161518
);
15171519

1520+
const friendlyHint = getFriendlyAblyErrorHint(
1521+
cmdError.code ??
1522+
(typeof cmdError.context.errorCode === "number"
1523+
? cmdError.context.errorCode
1524+
: undefined),
1525+
);
1526+
15181527
if (this.shouldOutputJson(flags)) {
1519-
this.log(this.formatJsonRecord("error", cmdError.toJsonData(), flags));
1528+
const jsonData = cmdError.toJsonData();
1529+
if (friendlyHint) {
1530+
jsonData.hint = friendlyHint;
1531+
}
1532+
this.log(this.formatJsonRecord("error", jsonData, flags));
15201533
this.exit(1);
15211534
}
15221535

15231536
let humanMessage = cmdError.message;
1537+
if (friendlyHint) {
1538+
humanMessage += `\n${friendlyHint}`;
1539+
}
1540+
15241541
const code = cmdError.code ?? cmdError.context.errorCode;
15251542
if (code !== undefined) {
15261543
const helpUrl = cmdError.context.helpUrl;

src/commands/bench/publisher.ts

Lines changed: 93 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -313,58 +313,64 @@ export default class BenchPublisher extends AblyBaseCommand {
313313
"waitingForSubscribers",
314314
"Waiting for subscribers...",
315315
);
316-
await new Promise<void>((resolve) => {
317-
const subscriberCheck = (member: Ably.PresenceMessage) => {
318-
if (
319-
member.data &&
320-
typeof member.data === "object" &&
321-
"role" in member.data &&
322-
member.data.role === "subscriber"
323-
) {
324-
this.logCliEvent(
325-
flags,
326-
"benchmark",
327-
"subscriberDetected",
328-
`Subscriber detected: ${member.clientId}`,
329-
{ clientId: member.clientId },
330-
);
331-
channel.presence.unsubscribe("enter", subscriberCheck);
332-
resolve();
333-
}
334-
};
335-
336-
channel.presence.subscribe("enter", subscriberCheck);
337-
channel.presence
338-
.get()
339-
.then((members) => {
340-
const subscribers = members.filter(
341-
(m) =>
342-
m.data &&
343-
typeof m.data === "object" &&
344-
"role" in m.data &&
345-
m.data.role === "subscriber",
346-
);
347-
if (subscribers.length > 0) {
348-
this.logCliEvent(
349-
flags,
350-
"benchmark",
351-
"subscribersFound",
352-
`Found ${subscribers.length} subscribers already present`,
353-
);
354-
channel.presence.unsubscribe("enter", subscriberCheck);
355-
resolve();
356-
}
357-
})
358-
.catch((error) => {
359-
this.logCliEvent(
360-
flags,
361-
"presence",
362-
"getPresenceError",
363-
`Error getting initial presence: ${errorMessage(error)}`,
364-
);
365-
// Continue waiting
366-
});
316+
let foundSubscriber: () => void;
317+
const subscriberPromise = new Promise<void>((resolve) => {
318+
foundSubscriber = resolve;
367319
});
320+
321+
const subscriberCheck = (member: Ably.PresenceMessage) => {
322+
if (
323+
member.data &&
324+
typeof member.data === "object" &&
325+
"role" in member.data &&
326+
member.data.role === "subscriber"
327+
) {
328+
this.logCliEvent(
329+
flags,
330+
"benchmark",
331+
"subscriberDetected",
332+
`Subscriber detected: ${member.clientId}`,
333+
{ clientId: member.clientId },
334+
);
335+
channel.presence.unsubscribe("enter", subscriberCheck);
336+
foundSubscriber();
337+
}
338+
};
339+
340+
await channel.presence.subscribe("enter", subscriberCheck);
341+
342+
// Check if subscribers are already present
343+
try {
344+
const members = await channel.presence.get();
345+
const subscribers = members.filter(
346+
(m) =>
347+
m.data &&
348+
typeof m.data === "object" &&
349+
"role" in m.data &&
350+
m.data.role === "subscriber",
351+
);
352+
if (subscribers.length > 0) {
353+
this.logCliEvent(
354+
flags,
355+
"benchmark",
356+
"subscribersFound",
357+
`Found ${subscribers.length} subscribers already present`,
358+
);
359+
channel.presence.unsubscribe("enter", subscriberCheck);
360+
// Already found, no need to wait
361+
} else {
362+
await subscriberPromise;
363+
}
364+
} catch (error) {
365+
this.logCliEvent(
366+
flags,
367+
"presence",
368+
"getPresenceError",
369+
`Error getting initial presence: ${errorMessage(error)}`,
370+
);
371+
// Continue waiting for subscribe callback
372+
await subscriberPromise;
373+
}
368374
} else {
369375
const members = await channel.presence.get();
370376
const subscribers = members.filter(
@@ -583,33 +589,42 @@ export default class BenchPublisher extends AblyBaseCommand {
583589
testId,
584590
};
585591

586-
channel.presence.subscribe("enter", (member: Ably.PresenceMessage) => {
587-
this.logCliEvent(
588-
flags,
589-
"presence",
590-
"memberEntered",
591-
`Member entered presence: ${member.clientId}`,
592-
{ clientId: member.clientId, data: member.data },
593-
);
594-
});
595-
channel.presence.subscribe("leave", (member: Ably.PresenceMessage) => {
596-
this.logCliEvent(
597-
flags,
598-
"presence",
599-
"memberLeft",
600-
`Member left presence: ${member.clientId}`,
601-
{ clientId: member.clientId },
602-
);
603-
});
604-
channel.presence.subscribe("update", (member: Ably.PresenceMessage) => {
605-
this.logCliEvent(
606-
flags,
607-
"presence",
608-
"memberUpdated",
609-
`Member updated presence: ${member.clientId}`,
610-
{ clientId: member.clientId, data: member.data },
611-
);
612-
});
592+
await channel.presence.subscribe(
593+
"enter",
594+
(member: Ably.PresenceMessage) => {
595+
this.logCliEvent(
596+
flags,
597+
"presence",
598+
"memberEntered",
599+
`Member entered presence: ${member.clientId}`,
600+
{ clientId: member.clientId, data: member.data },
601+
);
602+
},
603+
);
604+
await channel.presence.subscribe(
605+
"leave",
606+
(member: Ably.PresenceMessage) => {
607+
this.logCliEvent(
608+
flags,
609+
"presence",
610+
"memberLeft",
611+
`Member left presence: ${member.clientId}`,
612+
{ clientId: member.clientId },
613+
);
614+
},
615+
);
616+
await channel.presence.subscribe(
617+
"update",
618+
(member: Ably.PresenceMessage) => {
619+
this.logCliEvent(
620+
flags,
621+
"presence",
622+
"memberUpdated",
623+
`Member updated presence: ${member.clientId}`,
624+
{ clientId: member.clientId, data: member.data },
625+
);
626+
},
627+
);
613628

614629
this.logCliEvent(
615630
flags,

0 commit comments

Comments
 (0)