Skip to content

Refactor project:* commands to Action/Result pattern#284

Merged
mglaman merged 4 commits intomainfrom
refactor/project-commands-action-result-pattern
Feb 25, 2026
Merged

Refactor project:* commands to Action/Result pattern#284
mglaman merged 4 commits intomainfrom
refactor/project-commands-action-result-pattern

Conversation

@mglaman
Copy link
Owner

@mglaman mglaman commented Feb 25, 2026

Summary

  • Extract API logic from all five project:* commands into channel-agnostic Action classes returning typed Result DTOs, matching the pattern established for issue:* commands in Refactor remaining issue:* commands to Action/Result pattern #278/Refactor issue:link, issue:branch, issue:apply to Action/Result pattern #283
  • Add GetProjectLinkAction and GetProjectKanbanLinkAction (pure, no Client) sharing a single ProjectLinkResult
  • Add GetProjectReleasesAction, GetProjectIssuesAction, and GetProjectReleaseNotesAction with Client injected via constructor
  • Move type/core filter switch logic from ProjectIssues::execute() into GetProjectIssuesAction
  • Replace exit(1) in ReleaseNotes::execute() with throw new \RuntimeException() caught in execute()
  • ProjectCommandBase is unchanged

Test plan

  • vendor/bin/phpunit — 45 tests, all passing (14 new tests covering happy paths, project-not-found, semver fallback, and JSON serialisation)
  • vendor/bin/phpstan analyse src — no errors

Closes #279

🤖 Generated with Claude Code

Extracts API logic from all project:* commands into channel-agnostic
Action classes returning typed Result DTOs, matching the pattern
established for issue:* commands in #278/#283.

- Add GetProjectLinkAction, GetProjectKanbanLinkAction (pure, no Client)
- Add GetProjectReleasesAction, GetProjectIssuesAction,
  GetProjectReleaseNotesAction (Client-injected)
- Add ProjectLinkResult (shared), ProjectReleasesResult,
  ProjectIssuesResult, ProjectReleaseNotesResult
- Replace exit(1) in ReleaseNotes with RuntimeException caught in execute()
- Move type/core filter switch logic into GetProjectIssuesAction
- Add 14 PHPUnit tests covering happy paths, not-found errors,
  semver fallback, and JSON serialisation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Contributor

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

Refactors the project:* CLI commands to follow the existing Action/Result pattern used by issue:* commands, moving Drupal.org API interactions into reusable, channel-agnostic Action classes that return typed Result DTOs.

Changes:

  • Introduces new GetProject*Action classes under src/Api/Action/Project/ and corresponding Project*Result DTOs under src/Api/Result/Project/.
  • Updates project:releases, project:release-notes, project:issues, project:link, and project:kanban commands to delegate to these Actions.
  • Adds unit tests for the new Project Actions and JSON serialization.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/src/Action/Project/GetProjectReleasesActionTest.php Adds coverage for releases action behavior, not-found path, and JSON serialization.
tests/src/Action/Project/GetProjectReleaseNotesActionTest.php Adds coverage for release-notes action behavior, semver fallback, error paths, and JSON serialization.
tests/src/Action/Project/GetProjectLinkActionTest.php Adds coverage for project link action and JSON serialization.
tests/src/Action/Project/GetProjectKanbanLinkActionTest.php Adds coverage for kanban link action and JSON serialization.
tests/src/Action/Project/GetProjectIssuesActionTest.php Adds coverage for issues action behavior, not-found path, and JSON serialization.
src/Cli/Command/Project/Releases.php Delegates release fetching to GetProjectReleasesAction.
src/Cli/Command/Project/ReleaseNotes.php Delegates release-notes lookup to GetProjectReleaseNotesAction and replaces exit(1) with exception handling.
src/Cli/Command/Project/ProjectIssues.php Delegates issue filtering/fetching to GetProjectIssuesAction.
src/Cli/Command/Project/Link.php Uses GetProjectLinkAction to produce the URL to open.
src/Cli/Command/Project/Kanban.php Uses GetProjectKanbanLinkAction to produce the URL to open.
src/Api/Result/Project/ProjectReleasesResult.php Adds typed DTO + JSON shape for releases output.
src/Api/Result/Project/ProjectReleaseNotesResult.php Adds typed DTO + JSON shape for release notes output.
src/Api/Result/Project/ProjectLinkResult.php Adds shared DTO for link/kanban URL output.
src/Api/Result/Project/ProjectIssuesResult.php Adds DTO for project issues output + JSON shape.
src/Api/Action/Project/GetProjectReleasesAction.php Adds Action that resolves project and fetches releases via Client.
src/Api/Action/Project/GetProjectReleaseNotesAction.php Adds Action that resolves project and fetches release notes (with semver fallback).
src/Api/Action/Project/GetProjectLinkAction.php Adds pure Action that builds the drupal.org project URL.
src/Api/Action/Project/GetProjectKanbanLinkAction.php Adds pure Action that builds the contribkanban URL.
src/Api/Action/Project/GetProjectIssuesAction.php Adds Action that resolves project and performs release/core/type filtering + issue lookup.

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

mglaman and others added 2 commits February 25, 2026 13:22
Restores parity with the original exit(1) message in ReleaseNotes::execute(),
which read "No release found for $version." (with period).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ctions

ProjectCommandBase::initialize() already fetches and validates the project,
storing it in $this->projectData. The three project actions were re-fetching
it internally via getProject(), causing a redundant HTTP request per command.

Change GetProjectReleasesAction, GetProjectIssuesAction, and
GetProjectReleaseNotesAction to accept a Project entity as their first
argument instead of a machine name string. Remove the null guard from each
action (the caller is responsible for a valid Project). Update the three
corresponding commands to pass $this->projectData. Update tests to
construct Project::fromStdClass() and pass it directly, removing
getProject() mocks and the now-inapplicable testInvokeThrowsWhenProjectNotFound
cases.

Also fix CLAUDE.md: Client.php has only retry middleware (429/503), not
cache middleware as previously stated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Contributor

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

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


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

Map raw stdClass issue objects through IssueNode::fromStdClass() in
GetProjectIssuesAction, consistent with how ProjectReleasesResult uses
Release[]. Update jsonSerialize() to emit an explicit array shape and
use typed camelCase property access in the ProjectIssues command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mglaman mglaman merged commit 4eb7429 into main Feb 25, 2026
9 checks passed
@mglaman mglaman deleted the refactor/project-commands-action-result-pattern branch February 25, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor project:* commands to Action/Result pattern

2 participants