fix(issue): tolerate null fields in comment and link decode (YT-11) #55

Merged
David merged 2 commits from fix/issue-inspect-null-fields-YT-11 into main 2026-05-24 17:11:11 +02:00
Owner

Summary

Fixes YT-11. yt issue inspect was dropping the entire comments and links arrays into a warnings entry whenever YouTrack returned an explicit JSON null for an optional field on a comment (created/updated for system- or agent-authored comments) or on a link type (localizedName / sourceToTarget / targetToSource on link types without those labels). #[serde(default)] covers the missing case but rejects explicit null, so a single null upstream blanked the whole sub-fetch.

Changes

  • src/yt/models.rs: IssueComment.created / updated are now Option<i64> per the issue's proposed approach. IssueLinkType.localized_name / source_to_target / target_to_source are now Option<String> (with skip_serializing_if = "Option::is_none" so --json output stays clean). IssueComment.text, Issue.summary, Author.login, Author.full_name, IssueLinkType.name, and IssueLinkType.directed use the existing null_or_missing_to_default helper so the type stays stable for consumers and a JSON null no longer kills decode.
  • src/commands/issue/inspect.rs: print_comment prints - when created is None; pick_link_label walks Option<String> link-type labels with as_deref + is_empty guards.
  • Added decode-from-null tests for IssueComment, IssueLinkType, and IssueLink (with a null summary inside the nested Issue). Updated existing pick_link_label and full-inspect render fixtures for the new shape.

Verification

Live smoke test against niceguyit.myjetbrains.com confirms all acceptance criteria:

  • yt issue inspect MK-16 returns the three agent-authored comments with full body text and no warnings (was: empty Recent comments: plus two invalid type: null warnings).
  • yt issue inspect --json MK-16 returns len(comments) == 3, each with a populated text field, and warnings == [].
  • yt issue inspect YT-11 no longer emits the two decode warnings cited in the issue description.
  • cargo test --all-targets: 285 passed, 0 failed.
  • cargo clippy --all-targets -- -D warnings: clean.

The comments-endpoint failure column 7442 / links-endpoint failure column 101 both reproduced before this patch and are gone after it.

Test plan

  • cargo test --all-targets passes locally.
  • cargo clippy --all-targets -- -D warnings passes locally.
  • yt issue inspect MK-16 against live YT shows all three comments with no warnings.
  • yt issue inspect --json MK-16 populates comments and warnings is empty.
  • yt issue inspect YT-11 against live YT shows no decode warnings.
  • Reviewer: confirm --json output still parses for any tooling that depends on localized_name always being a string (the field is now omitted when null instead of being an empty string).

YouTrack issue: YT-11. Will auto-transition to Done on merge via the commit trailer.

## Summary Fixes YT-11. `yt issue inspect` was dropping the entire `comments` and `links` arrays into a `warnings` entry whenever YouTrack returned an explicit JSON `null` for an optional field on a comment (created/updated for system- or agent-authored comments) or on a link type (localizedName / sourceToTarget / targetToSource on link types without those labels). `#[serde(default)]` covers the missing case but rejects explicit null, so a single null upstream blanked the whole sub-fetch. ## Changes - `src/yt/models.rs`: `IssueComment.created` / `updated` are now `Option<i64>` per the issue's proposed approach. `IssueLinkType.localized_name` / `source_to_target` / `target_to_source` are now `Option<String>` (with `skip_serializing_if = "Option::is_none"` so `--json` output stays clean). `IssueComment.text`, `Issue.summary`, `Author.login`, `Author.full_name`, `IssueLinkType.name`, and `IssueLinkType.directed` use the existing `null_or_missing_to_default` helper so the type stays stable for consumers and a JSON null no longer kills decode. - `src/commands/issue/inspect.rs`: `print_comment` prints `-` when `created` is `None`; `pick_link_label` walks `Option<String>` link-type labels with `as_deref` + `is_empty` guards. - Added decode-from-null tests for `IssueComment`, `IssueLinkType`, and `IssueLink` (with a null `summary` inside the nested `Issue`). Updated existing `pick_link_label` and full-inspect render fixtures for the new shape. ## Verification Live smoke test against `niceguyit.myjetbrains.com` confirms all acceptance criteria: - `yt issue inspect MK-16` returns the three agent-authored comments with full body text and no warnings (was: empty `Recent comments:` plus two `invalid type: null` warnings). - `yt issue inspect --json MK-16` returns `len(comments) == 3`, each with a populated `text` field, and `warnings == []`. - `yt issue inspect YT-11` no longer emits the two decode warnings cited in the issue description. - `cargo test --all-targets`: 285 passed, 0 failed. - `cargo clippy --all-targets -- -D warnings`: clean. The `comments`-endpoint failure column 7442 / `links`-endpoint failure column 101 both reproduced before this patch and are gone after it. ## Test plan - [x] `cargo test --all-targets` passes locally. - [x] `cargo clippy --all-targets -- -D warnings` passes locally. - [x] `yt issue inspect MK-16` against live YT shows all three comments with no warnings. - [x] `yt issue inspect --json MK-16` populates `comments` and `warnings` is empty. - [x] `yt issue inspect YT-11` against live YT shows no decode warnings. - [ ] Reviewer: confirm `--json` output still parses for any tooling that depends on `localized_name` always being a string (the field is now omitted when null instead of being an empty string). YouTrack issue: [YT-11](https://niceguyit.myjetbrains.com/issue/YT-11). Will auto-transition to `Done` on merge via the commit trailer.
fix(issue): tolerate null fields in comment and link decode (YT-11)
Some checks failed
Check / fmt + clippy + build + tests (pull_request) Failing after 10s
1959372932
The `comments` and `links` sub-fetches of `yt issue inspect` decoded into models that used plain `#[serde(default)]` for every field. YouTrack returns explicit JSON `null` for some optional fields (notably `created`/`updated` on system- and agent-authored comments, and `localizedName`/`sourceToTarget`/`targetToSource` on link types without those labels configured). `#[serde(default)]` handles the missing case but rejects explicit null, so a single null in any sub-fetch dropped the whole array into a `warnings` entry and the formatted summary lost the comment bodies / link rows.

Model changes (`src/yt/models.rs`):

- `IssueComment.created` and `IssueComment.updated`: `i64` -> `Option<i64>`, per the issue's proposed approach. Distinguishes "comment was never edited" from "edited at epoch zero" and lets the renderer print `-` for absent timestamps.
- `IssueComment.text`, `Issue.summary`, `Author.login`, `Author.fullName`: now use the existing `null_or_missing_to_default` helper so a JSON `null` decodes to an empty string instead of failing. Keeps the field type stable so consumers do not change.
- `IssueLinkType.localized_name`, `source_to_target`, `target_to_source`: `String` -> `Option<String>`, per the proposed approach. `skip_serializing_if = "Option::is_none"` keeps the `--json` output clean (no `null` keys for unset labels).
- `IssueLinkType.name` and `directed`: hardened with `null_or_missing_to_default` so a null on either does not blow up decode, while keeping the underlying types so no downstream callers change.

Renderer changes (`src/commands/issue/inspect.rs`):

- `print_comment` now formats `c.created` as `format_millis(ts)` when `Some`, else literal `-`. Satisfies the acceptance criterion "If created is null, print `-`".
- `pick_link_label` walks `Option<String>` link-type labels with `as_deref` + `is_empty` guards instead of plain `is_empty`. Behaviour identical when labels are populated; gracefully falls through when any of them is null or empty.

Tests:

- Added `issue_comment_with_null_timestamps_decodes`, `issue_comment_with_present_timestamps_decodes`, `issue_link_type_with_null_labels_decodes`, `issue_link_with_null_summary_inside_issues_decodes` in `src/yt/models.rs` covering each newly tolerated null path.
- Updated the existing `pick_link_label_prefers_localized_then_name_then_target` and `render_smoke_does_not_panic_on_full_inspect` fixtures to construct the new `Option<String>` / `Option<i64>` shape.

Smoke-tested against the live YouTrack:

- `yt issue inspect MK-16` (the original reproducer) returns all three agent-authored comments with full body text, no warnings.
- `yt issue inspect --json MK-16` returns a populated `comments` array (length 3, each with a `text` field) and an empty `warnings` array.
- `yt issue inspect YT-11` no longer emits the two `invalid type: null` warnings it used to.

#YT-11 State Done
Merge branch 'main' into fix/issue-inspect-null-fields-YT-11
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt + clippy + build + tests (pull_request) Successful in 15s
0f407f497e
David merged commit 13263dc364 into main 2026-05-24 17:11:11 +02:00
David deleted branch fix/issue-inspect-null-fields-YT-11 2026-05-24 17:11:11 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pandoras-box/youtrack-cli!55
No description provided.