fix(push publish): parse --message as a full Ably message shape#352
fix(push publish): parse --message as a full Ably message shape#352umair-ably merged 3 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR fixes Changes
Review Notes
|
There was a problem hiding this comment.
Review summary
| File | Status | Issues |
|---|---|---|
src/commands/push/publish.ts |
1 issue | Undocumented behavior change |
test/unit/commands/push/publish.test.ts |
1 issue | Missing test for changed edge case |
The core feature is correctly implemented — error handling, base class, output helpers, JSON structure, and flag architecture all look good. One behavioral change is not covered by tests and is inconsistent with the PR description's claim.
src/commands/push/publish.ts + test/unit/commands/push/publish.test.ts
Undocumented behavior change for bare objects containing a data key
prepareMessageFromInput treats a plain-object JSON value as an Ably message shape, which means it unwraps the data field when present:
--message '{"data":"hello"}'
Old behavior → publishMessage.data = { data: "hello" } (whole object stored as data)
New behavior → publishMessage.data = "hello" (inner value extracted)
The PR description says "Plain string and bare-object --message values keep existing behaviour" and lists '{"key":"val"}' as an example. That example is accurate — when the object has no name, data, or extras keys, the whole object becomes data via prepareMessageFromInput's else if branch, matching the old result. But '{"data":"something"}' falls into the "data" in messageData branch, which extracts just the inner value.
The same gap exists in the test suite. The existing "should parse JSON message data when publishing via channel" test uses '{"key":"val"}' (no special keys, unchanged behavior), not '{"data":"val"}' (unwrapped, changed behavior). None of the new tests cover it either.
Why it matters: a user who had --message '{"data":{"key":"val"}}' expecting the subscriber to receive data === { data: { key: "val" } } will now receive data === { key: "val" }. This may be the correct semantic (matching channels publish), but it is a breaking change that is not documented or tested.
Suggested fix: Add a test case that asserts the new behavior explicitly, and update the PR description to accurately describe this change. For example:
it("should extract data field when --message object has a data key", async () => {
const mock = getMockAblyRest();
const channel = mock.channels._getChannel("my-channel");
await runCommand(
["push:publish", "--channel", "my-channel", "--title", "Hi",
"--message", '{"data":"extracted"}', "--force"],
import.meta.url,
);
// data is extracted from the wrapper, not stored as { data: "extracted" }
expect(channel.publish).toHaveBeenCalledWith(
expect.objectContaining({ data: "extracted" }),
);
});Everything else looks correct: this.fail() usage, component casing ("pushPublish"), the extras.push collision guard, the extras merge order ({ ...userExtras, push: payload } keeps CLI-owned push last), JSON domain nesting under notification, and logSuccessMessage placement outside the JSON path.
Reuses prepareMessageFromInput (already used by channels publish) so JSON input can set name, data, and extras — not just data. The CLI push payload is still attached as extras.push; user-supplied extras are merged, and extras.push in --message is rejected with a clear error since the CLI owns that field via its push flags. JSON output includes messageName and messageData when present.
Explicitly assert that --message '{"data":"x"}' now unwraps to
data: "x" rather than data: { data: "x" } — a behaviour change
from the previous JSON.parse-into-data implementation.
47411f5 to
295a823
Compare
Summary
ably push publish --messagenow parses JSON input as a full Ably message (name,data,extras) instead of always stuffing the parsed value intodata. This matches the semantics ofably channels publishand makes it possible to set the realtime message'sname.prepareMessageFromInput()helper insrc/utils/message.ts— no new utility.extras.pushon the published message; any user-suppliedextraskeys are merged alongside it.--messagealready containsextras.push, the command fails with a clear error (the CLI owns that field via--title/--body/--payload/ etc.).--json) now includesmessageNameandmessageDatawhen present, making the result self-describing (replaces the previousmessageData: trueboolean sentinel).Follow-up to #310 (addresses the reviewer comment that
--messagecouldn't set the message name).Behaviour change⚠️
Moving to
prepareMessageFromInputmeans any JSON object that already contains top-levelname,data, orextraskeys is now treated as a full Ably message shape. This is the point of the change, but two cases are worth calling out explicitly:--messageinput'hello'(plain string)data: "hello"data: "hello"— unchanged'{"key":"val"}'(object, no reserved keys)data: { key: "val" }data: { key: "val" }— unchanged'{"name":"alert","data":"hi"}'data: { name: "alert", data: "hi" }name: "alert",data: "hi"— new capability'{"data":"hi"}'data: { data: "hi" }data: "hi"— inner value unwrapped'{"extras":{"headers":{...}}}'data: { extras: {...} }extras: { headers: {...}, push: ... }— merged'{"extras":{"push":{...}}}'data: { extras: { push: {...} } }extras.pushCallers who relied on the previous (arguably incorrect) behaviour where
{"data":"x"}was stored asdata: { data: "x" }will see the inner value unwrapped instead. The new shape matcheschannels publishand how a plain Ably realtime subscriber expects messages.Test plan
pnpm prepare(build + manifest) succeedspnpm exec eslint src/commands/push/publish.ts test/unit/commands/push/publish.test.ts— 0 errorspnpm test test/unit/commands/push/publish.test.ts— 32/32 pass, including:nameanddatadatadatakey is unwrapped (behaviour-change assertion)extrasmerged withpushextras.pushcollision fails with clear error, publish is not calledmessageName+messageDatapnpm test:unit— full suite greenably push publish --channel demo --title Hi --message '{"name":"alert","data":"hi"}' --force— verify a realtime subscriber seesname === "alert"anddata === "hi".ably push publish --channel demo --title Hi --message '{"extras":{"push":{"notification":{"title":"x"}}}}' --force— verify the collision error and non-zero exit.