-
Notifications
You must be signed in to change notification settings - Fork 0
Add GitHub Issue Milestones functionality #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace ConduitUI\Issue\Contracts; | ||
|
|
||
| use ConduitUI\Issue\Data\Issue; | ||
| use ConduitUI\Issue\Data\Milestone; | ||
| use Illuminate\Support\Collection; | ||
|
|
||
| interface ManagesMilestonesInterface | ||
| { | ||
| /** | ||
| * @return \Illuminate\Support\Collection<int, \ConduitUI\Issue\Data\Milestone> | ||
| */ | ||
| public function listMilestones(string $owner, string $repo, array $filters = []): Collection; | ||
|
|
||
| public function getMilestone(string $owner, string $repo, int $milestoneNumber): Milestone; | ||
|
|
||
| public function createMilestone(string $owner, string $repo, array $data): Milestone; | ||
|
|
||
| public function updateMilestone(string $owner, string $repo, int $milestoneNumber, array $data): Milestone; | ||
|
|
||
| public function deleteMilestone(string $owner, string $repo, int $milestoneNumber): void; | ||
|
|
||
| public function assignIssueToMilestone(string $owner, string $repo, int $issueNumber, int $milestoneNumber): Issue; | ||
|
|
||
| public function removeIssueFromMilestone(string $owner, string $repo, int $issueNumber): Issue; | ||
|
|
||
| /** | ||
| * @return \Illuminate\Support\Collection<int, \ConduitUI\Issue\Data\Issue> | ||
| */ | ||
| public function listMilestoneIssues(string $owner, string $repo, int $milestoneNumber, array $filters = []): Collection; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace ConduitUI\Issue\Data; | ||
|
|
||
| use DateTime; | ||
|
|
||
| readonly class Milestone | ||
| { | ||
| public function __construct( | ||
| public int $id, | ||
| public int $number, | ||
| public string $title, | ||
| public ?string $description, | ||
| public string $state, | ||
| public int $openIssues, | ||
| public int $closedIssues, | ||
| public DateTime $createdAt, | ||
| public DateTime $updatedAt, | ||
| public ?DateTime $closedAt, | ||
| public ?DateTime $dueOn, | ||
| public string $htmlUrl, | ||
| public User $creator, | ||
| ) {} | ||
|
|
||
| public static function fromArray(array $data): self | ||
| { | ||
| return new self( | ||
| id: $data['id'], | ||
| number: $data['number'], | ||
| title: $data['title'], | ||
| description: $data['description'] ?? null, | ||
| state: $data['state'], | ||
| openIssues: $data['open_issues'], | ||
| closedIssues: $data['closed_issues'], | ||
| createdAt: new DateTime($data['created_at']), | ||
| updatedAt: new DateTime($data['updated_at']), | ||
| closedAt: $data['closed_at'] ? new DateTime($data['closed_at']) : null, | ||
| dueOn: $data['due_on'] ? new DateTime($data['due_on']) : null, | ||
| htmlUrl: $data['html_url'], | ||
| creator: User::fromArray($data['creator']), | ||
| ); | ||
| } | ||
|
|
||
| public function toArray(): array | ||
| { | ||
| return [ | ||
| 'id' => $this->id, | ||
| 'number' => $this->number, | ||
| 'title' => $this->title, | ||
| 'description' => $this->description, | ||
| 'state' => $this->state, | ||
| 'open_issues' => $this->openIssues, | ||
| 'closed_issues' => $this->closedIssues, | ||
| 'created_at' => $this->createdAt->format('c'), | ||
| 'updated_at' => $this->updatedAt->format('c'), | ||
| 'closed_at' => $this->closedAt?->format('c'), | ||
| 'due_on' => $this->dueOn?->format('c'), | ||
| 'html_url' => $this->htmlUrl, | ||
| 'creator' => $this->creator->toArray(), | ||
| ]; | ||
| } | ||
|
|
||
| public function isOpen(): bool | ||
| { | ||
| return $this->state === 'open'; | ||
| } | ||
|
|
||
| public function isClosed(): bool | ||
| { | ||
| return $this->state === 'closed'; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace ConduitUI\Issue\Traits; | ||
|
|
||
| use ConduitUI\Issue\Data\Issue; | ||
| use ConduitUI\Issue\Data\Milestone; | ||
| use Illuminate\Support\Collection; | ||
|
|
||
| trait ManagesMilestones | ||
| { | ||
| /** | ||
| * @return \Illuminate\Support\Collection<int, \ConduitUI\Issue\Data\Milestone> | ||
| */ | ||
| public function listMilestones(string $owner, string $repo, array $filters = []): Collection | ||
| { | ||
| $response = $this->connector->send( | ||
| $this->connector->get("/repos/{$owner}/{$repo}/milestones", $filters) | ||
| ); | ||
|
|
||
| return collect($response->json()) | ||
| ->map(fn (array $data) => Milestone::fromArray($data)); | ||
| } | ||
|
|
||
| public function getMilestone(string $owner, string $repo, int $milestoneNumber): Milestone | ||
| { | ||
| $response = $this->connector->send( | ||
| $this->connector->get("/repos/{$owner}/{$repo}/milestones/{$milestoneNumber}") | ||
| ); | ||
|
|
||
| return Milestone::fromArray($response->json()); | ||
| } | ||
|
|
||
| public function createMilestone(string $owner, string $repo, array $data): Milestone | ||
| { | ||
| $response = $this->connector->send( | ||
| $this->connector->post("/repos/{$owner}/{$repo}/milestones", $data) | ||
| ); | ||
|
|
||
| return Milestone::fromArray($response->json()); | ||
| } | ||
|
|
||
| public function updateMilestone(string $owner, string $repo, int $milestoneNumber, array $data): Milestone | ||
| { | ||
| $response = $this->connector->send( | ||
| $this->connector->patch("/repos/{$owner}/{$repo}/milestones/{$milestoneNumber}", $data) | ||
| ); | ||
|
|
||
| return Milestone::fromArray($response->json()); | ||
| } | ||
|
|
||
| public function deleteMilestone(string $owner, string $repo, int $milestoneNumber): void | ||
| { | ||
| $this->connector->send( | ||
| $this->connector->delete("/repos/{$owner}/{$repo}/milestones/{$milestoneNumber}") | ||
| ); | ||
| } | ||
|
Comment on lines
+16
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -type f -name "*.php" | grep -i connector | head -20Repository: conduit-ui/issue Length of output: 42 🏁 Script executed: find . -type f -name "*.php" -path "*/src/*" | head -30Repository: conduit-ui/issue Length of output: 621 🏁 Script executed: git ls-files "*.php" | grep -E "(Connector|connector)" | head -20Repository: conduit-ui/issue Length of output: 42 🏁 Script executed: cat -n src/Services/IssuesService.php | head -100Repository: conduit-ui/issue Length of output: 834 🏁 Script executed: cat -n src/Traits/ManagesIssues.php | head -150Repository: conduit-ui/issue Length of output: 2340 🏁 Script executed: find . -type f -name "composer.json" -o -name "composer.lock" | head -5Repository: conduit-ui/issue Length of output: 91 🏁 Script executed: cat composer.json | grep -A 5 -B 5 "GitHubConnector" 2>/dev/null || cat composer.json | head -100Repository: conduit-ui/issue Length of output: 2747 🏁 Script executed: find . -path "*/vendor/*" -name "Connector.php" 2>/dev/null | grep -i github | head -5Repository: conduit-ui/issue Length of output: 42 🏁 Script executed: find . -path "*/vendor/conduit-ui/*" -type f -name "*.php" 2>/dev/null | head -20Repository: conduit-ui/issue Length of output: 42 🏁 Script executed: rg "class Connector" -A 20 --type phpRepository: conduit-ui/issue Length of output: 42 🏁 Script executed: find ./tests -type f -name "*.php" 2>/dev/null | head -10Repository: conduit-ui/issue Length of output: 228 🏁 Script executed: rg "listMilestones|getMilestone" -A 10 --type phpRepository: conduit-ui/issue Length of output: 2820 🌐 Web query:
💡 Result: I couldn't find a GitHub repository named exactly "conduit-ui/connector". Related projects you might mean:
If you want, tell me which one you meant (or paste a link) and I’ll open it and summarize the repo contents. Sources: [1] Packagist conduit-ui listings; [2] Libraries.io entry for conduit-ui/github-connector; [3] Conduit-UI code listing (mirror/analysis). 🌐 Web query:
💡 Result: Short answer: Saloon does not automatically percent‑encode URL path segments for you — you should encode path parameters yourself before returning the endpoint in resolveEndpoint. Use rawurlencode (RFC‑3986) per segment (or a URI library like League\Uri\Encoder::encodePath if you need finer control) and then build the endpoint string. [1][2][3] Example (concept): Sources: Citations:
🌐 Web query:
💡 Result: I couldn't find a Packagist package named "conduit-ui/connector". The closest match is "conduit-ui/conduit" on Packagist. [1] Sources 🏁 Script executed: cat -n tests/TestCase.phpRepository: conduit-ui/issue Length of output: 924 🏁 Script executed: cat -n src/Traits/ManagesMilestones.phpRepository: conduit-ui/issue Length of output: 3235 Encode Saloon (the underlying HTTP client) does not automatically percent-encode URL path segments. If these parameters contain special characters (including public function listMilestones(string $owner, string $repo, array $filters = []): Collection
{
+ $owner = rawurlencode($owner);
+ $repo = rawurlencode($repo);
$response = $this->connector->send(
$this->connector->get("/repos/{$owner}/{$repo}/milestones", $filters)
);
@@
public function getMilestone(string $owner, string $repo, int $milestoneNumber): Milestone
{
+ $owner = rawurlencode($owner);
+ $repo = rawurlencode($repo);
$response = $this->connector->send(
$this->connector->get("/repos/{$owner}/{$repo}/milestones/{$milestoneNumber}")
);
@@
public function createMilestone(string $owner, string $repo, array $data): Milestone
{
+ $owner = rawurlencode($owner);
+ $repo = rawurlencode($repo);
$response = $this->connector->send(
$this->connector->post("/repos/{$owner}/{$repo}/milestones", $data)
);
@@
public function updateMilestone(string $owner, string $repo, int $milestoneNumber, array $data): Milestone
{
+ $owner = rawurlencode($owner);
+ $repo = rawurlencode($repo);
$response = $this->connector->send(
$this->connector->patch("/repos/{$owner}/{$repo}/milestones/{$milestoneNumber}", $data)
);
@@
public function deleteMilestone(string $owner, string $repo, int $milestoneNumber): void
{
+ $owner = rawurlencode($owner);
+ $repo = rawurlencode($repo);
$this->connector->send(
$this->connector->delete("/repos/{$owner}/{$repo}/milestones/{$milestoneNumber}")
);
}Note: This same pattern appears in 🤖 Prompt for AI Agents |
||
|
|
||
| public function assignIssueToMilestone(string $owner, string $repo, int $issueNumber, int $milestoneNumber): Issue | ||
| { | ||
| return $this->updateIssue($owner, $repo, $issueNumber, ['milestone' => $milestoneNumber]); | ||
| } | ||
|
|
||
| public function removeIssueFromMilestone(string $owner, string $repo, int $issueNumber): Issue | ||
| { | ||
| return $this->updateIssue($owner, $repo, $issueNumber, ['milestone' => null]); | ||
| } | ||
|
|
||
| /** | ||
| * @return \Illuminate\Support\Collection<int, \ConduitUI\Issue\Data\Issue> | ||
| */ | ||
| public function listMilestoneIssues(string $owner, string $repo, int $milestoneNumber, array $filters = []): Collection | ||
| { | ||
| $filters['milestone'] = (string) $milestoneNumber; | ||
|
|
||
| return $this->listIssues($owner, $repo, $filters); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Harden
fromArray()against missingclosed_at/due_onkeys.📝 Committable suggestion
🤖 Prompt for AI Agents