pub struct GitHubClient { /* private fields */ }Expand description
GitHub REST API client for issues management.
Implementations§
Source§impl GitHubClient
impl GitHubClient
pub fn new(token: &str) -> Self
Sourcepub fn against(base: &str, token: &str) -> Self
pub fn against(base: &str, token: &str) -> Self
A client for an API at base. Tests use it; nothing else does.
Sourcepub fn against_within(base: &str, token: &str, request: Duration) -> Self
pub fn against_within(base: &str, token: &str, request: Duration) -> Self
against with a request bound of the caller’s
choosing, so a test can watch a silent server be given up on without
waiting out the real one.
Sourcepub async fn validate_token(&self) -> Result<String>
pub async fn validate_token(&self) -> Result<String>
Validate the token by fetching the authenticated user.
Sourcepub async fn list_rulesets(
&self,
owner: &str,
repo: &str,
) -> Result<Vec<RepositoryRuleset>>
pub async fn list_rulesets( &self, owner: &str, repo: &str, ) -> Result<Vec<RepositoryRuleset>>
Every repository ruleset, by name and id.
Read before writing so applying protection twice is applying it once: GitHub’s create endpoint does not upsert, and a second run would leave two rulesets with the same name saying the same thing.
Sourcepub async fn put_ruleset(
&self,
owner: &str,
repo: &str,
name: &str,
body: &Value,
) -> Result<RepositoryRuleset>
pub async fn put_ruleset( &self, owner: &str, repo: &str, name: &str, body: &Value, ) -> Result<RepositoryRuleset>
Create a ruleset, or replace the one that already has this name.
The body is passed through rather than modelled: GitHub’s rule shapes are a tagged union that grows, and a partial model here would silently drop the parts of a rule it did not know about.
Source§impl GitHubClient
impl GitHubClient
Sourcepub async fn list_issues_changed(
&self,
owner: &str,
repo: &str,
conditions: &Conditions,
) -> Result<Fetched<Vec<GitHubIssue>>>
pub async fn list_issues_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubIssue>>>
List issues, asking only for what changed.
Two mechanisms, and both matter: since keeps the RESPONSE small on a
repository with thousands of issues, and the ETag makes the whole
request free when nothing moved at all. Neither is an optimisation for
its own sake — a workbench that mirrors forty repositories continuously
is rate-limited into uselessness without them.
since is inclusive, so the issue that set the mark comes back once
more and is written over itself. One row rewritten beats one update
missed.
Sourcepub async fn list_gists_changed(
&self,
conditions: &Conditions,
) -> Result<Fetched<Vec<GitHubGist>>>
pub async fn list_gists_changed( &self, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubGist>>>
List the authenticated user’s gists, conditionally.
No since here on purpose: the answer is authoritative about which
gists EXIST, and a partial answer cannot be — a gist absent from an
incremental page would read as deleted.
Source§impl GitHubClient
impl GitHubClient
Sourcepub async fn list_pulls_changed(
&self,
owner: &str,
repo: &str,
conditions: &Conditions,
) -> Result<Fetched<Vec<GitHubPull>>>
pub async fn list_pulls_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubPull>>>
Pull requests, newest movement first.
A separate request from issues even though GitHub’s issues endpoint returns both: that endpoint’s pull requests are issue-shaped, missing the branch, the draft flag and the merge — everything that makes a pull request worth listing separately.
Sourcepub async fn checks_for(
&self,
owner: &str,
repo: &str,
commit: &str,
) -> Result<GitHubChecks>
pub async fn checks_for( &self, owner: &str, repo: &str, commit: &str, ) -> Result<GitHubChecks>
What CI says about one commit: check runs AND commit statuses.
Both, because a repository can use either — GitHub Actions writes check runs, most other systems write statuses — and a workbench that read one would report “no checks” for half the fleet.
pub async fn list_releases_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubRelease>>>
pub async fn repository_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<GitHubRepository>>
pub async fn list_milestones_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubMilestone>>>
pub async fn list_labels_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubLabel>>>
Sourcepub async fn list_comments_changed(
&self,
owner: &str,
repo: &str,
number: i64,
conditions: &Conditions,
) -> Result<Fetched<Vec<GitHubComment>>>
pub async fn list_comments_changed( &self, owner: &str, repo: &str, number: i64, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubComment>>>
Comments on one issue or pull request.
Sourcepub async fn list_notifications_changed(
&self,
conditions: &Conditions,
) -> Result<Fetched<Vec<GitHubNotification>>>
pub async fn list_notifications_changed( &self, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubNotification>>>
What GitHub thinks needs this person, across every repository.
Sourcepub async fn rate_limit(&self) -> Result<GitHubRateLimit>
pub async fn rate_limit(&self) -> Result<GitHubRateLimit>
How much API budget is left.
Never conditional and never counted: GitHub exempts this endpoint from the limit it reports, which is the only reason it can be asked when the budget is gone — exactly when somebody needs to know.
Sourcepub async fn traffic(&self, owner: &str, repo: &str) -> Result<GitHubTraffic>
pub async fn traffic(&self, owner: &str, repo: &str) -> Result<GitHubTraffic>
Views and clones over the last fortnight, which is all GitHub keeps.
Needs push access. A repository somebody only reads answers 403, and that is recorded as this scope’s error rather than as an empty series — zero views and “you may not ask” are different facts.
Source§impl GitHubClient
impl GitHubClient
Sourcepub async fn list_projects(&self, login: &str) -> Result<Vec<GitHubProject>>
pub async fn list_projects(&self, login: &str) -> Result<Vec<GitHubProject>>
Every project one owner has.
login may be a user or an organisation, and repositoryOwner resolves
either — which is the point. Asking organization(login:) and
user(login:) in one query looks like it does the same thing and does
not: GitHub answers the field that does not match with an error, and a
GraphQL answer carrying errors AND data is a failed query as far as
anything reading it is concerned. So every owner failed with “could not
resolve to an Organization” (for a user) or “to a User” (for an
organisation), and no board ever arrived.
A login that is neither comes back as a null owner and no error, which
is an owner with no boards rather than a failure — and a real failure,
a token without read:project, still arrives as an error and is still
raised.
Every page of them. The list is authoritative — a board absent from it is a board that is gone, and its cards are forgotten — so stopping at a first page would delete the boards past it.
Sourcepub async fn viewer(&self) -> Result<GitHubViewer>
pub async fn viewer(&self) -> Result<GitHubViewer>
Who the token belongs to, and every organisation they are a member of.
What “all of this person’s boards” is made from: a board belongs to a user or an organisation, and the token’s own account and its organisations are every owner whose boards this person can reach without anybody having to name them first.
Membership the token cannot see — an organisation that hides it from third-party access — is simply absent, as GitHub answers it.
Sourcepub async fn add_project_column(
&self,
project_id: &str,
field_id: &str,
name: &str,
color: &str,
description: &str,
) -> Result<GitHubProject>
pub async fn add_project_column( &self, project_id: &str, field_id: &str, name: &str, color: &str, description: &str, ) -> Result<GitHubProject>
Add one option to the board’s column field.
The project is read immediately before constructing the replacement list. Sending the browser’s possibly stale list would let a second editor’s new column disappear. Existing ids, colours and descriptions are carried exactly so GitHub preserves their identity and card values.
Sourcepub async fn remove_project_column(
&self,
project_id: &str,
field_id: &str,
option_id: &str,
) -> Result<GitHubProject>
pub async fn remove_project_column( &self, project_id: &str, field_id: &str, option_id: &str, ) -> Result<GitHubProject>
Remove one option from the board’s column field.
GitHub clears the field value on cards that used the removed option. The handler consequently re-reads every item after this returns rather than patching those cards locally.
Sourcepub async fn project_items(
&self,
project_id: &str,
) -> Result<Vec<GitHubProjectItem>>
pub async fn project_items( &self, project_id: &str, ) -> Result<Vec<GitHubProjectItem>>
Every card on one board, following GitHub’s pagination to the end.
All of them rather than the first page: a board is read as columns, and a column missing its tail is a column with the wrong count in its header — which is worse than a slower sync.
Sourcepub async fn project_item(&self, item_id: &str) -> Result<GitHubProjectItem>
pub async fn project_item(&self, item_id: &str) -> Result<GitHubProjectItem>
One card, re-read.
What every write here ends with. The alternative is patching the held
payload by hand — which was how a move worked, and which quietly
invents the parts of the answer it does not know: GitHub stamps
updatedAt, may close an item, may have had another person change a
field a second earlier. Asking costs one request and is never wrong.
Sourcepub async fn set_project_field(
&self,
project_id: &str,
item_id: &str,
field_id: &str,
value: &ProjectFieldWrite,
) -> Result<GitHubProjectItem>
pub async fn set_project_field( &self, project_id: &str, item_id: &str, field_id: &str, value: &ProjectFieldWrite, ) -> Result<GitHubProjectItem>
Set one field on one card.
Every field kind through one door, because they are one act: a person setting a due date and a person dragging a card between columns are both saying “this item’s F is now V”, and two code paths for that is how one of them ends up not refreshing what the other does.
Sourcepub async fn clear_project_field(
&self,
project_id: &str,
item_id: &str,
field_id: &str,
) -> Result<GitHubProjectItem>
pub async fn clear_project_field( &self, project_id: &str, item_id: &str, field_id: &str, ) -> Result<GitHubProjectItem>
Take a field’s value off a card.
Distinct from setting it to empty: an empty text value is a value, and a board that could not tell “no due date” from “a due date of nothing” would show a column of blanks where it means to show none.
Sourcepub async fn move_project_item_after(
&self,
project_id: &str,
item_id: &str,
after: Option<&str>,
) -> Result<GitHubProjectItem>
pub async fn move_project_item_after( &self, project_id: &str, item_id: &str, after: Option<&str>, ) -> Result<GitHubProjectItem>
Put a card after another one, or first in the board’s order.
after is the card it lands behind; None puts it at the top. This is
what a drag WITHIN a column means, and without it a board’s order is
whatever GitHub last decided.
Sourcepub async fn add_project_draft(
&self,
project_id: &str,
title: &str,
body: &str,
) -> Result<GitHubProjectItem>
pub async fn add_project_draft( &self, project_id: &str, title: &str, body: &str, ) -> Result<GitHubProjectItem>
Add a card that exists only on the board.
A draft is a note with a place in the plan and no repository yet. It is the cheapest way to put something on a board, and refusing to create one is what sends a person to the website mid-thought.
Sourcepub async fn update_project_draft(
&self,
item_id: &str,
draft_id: &str,
title: &str,
body: &str,
) -> Result<GitHubProjectItem>
pub async fn update_project_draft( &self, item_id: &str, draft_id: &str, title: &str, body: &str, ) -> Result<GitHubProjectItem>
Rewrite a draft’s title and body.
Only a draft: an issue’s title belongs to the issue and is edited where issues are, not on a board that happens to show it.
Sourcepub async fn add_project_item_by_url(
&self,
project_id: &str,
url: &str,
) -> Result<GitHubProjectItem>
pub async fn add_project_item_by_url( &self, project_id: &str, url: &str, ) -> Result<GitHubProjectItem>
Put an existing issue or pull request on the board, by its URL.
The URL is what a person has in hand — it is what GitHub’s own “add item” takes, and what is in the clipboard after reading an issue. It is resolved to a node id here rather than asking anybody to find one.
Sourcepub async fn archive_project_item(
&self,
project_id: &str,
item_id: &str,
archived: bool,
) -> Result<GitHubProjectItem>
pub async fn archive_project_item( &self, project_id: &str, item_id: &str, archived: bool, ) -> Result<GitHubProjectItem>
Archive a card, or bring it back.
Archiving is what a board does instead of deleting: the item keeps its field values and its history, and stops taking up a column. Deleting is separate, and is the one that cannot be undone.
Source§impl GitHubClient
impl GitHubClient
Sourcepub async fn list_issues(
&self,
owner: &str,
repo: &str,
state: &str,
) -> Result<Vec<GitHubIssue>>
pub async fn list_issues( &self, owner: &str, repo: &str, state: &str, ) -> Result<Vec<GitHubIssue>>
List issues for a repository.
Sourcepub async fn get_issue(
&self,
owner: &str,
repo: &str,
number: i64,
) -> Result<GitHubIssue>
pub async fn get_issue( &self, owner: &str, repo: &str, number: i64, ) -> Result<GitHubIssue>
Get a single issue.
Sourcepub async fn create_issue(
&self,
owner: &str,
repo: &str,
req: &CreateIssueRequest,
) -> Result<GitHubIssue>
pub async fn create_issue( &self, owner: &str, repo: &str, req: &CreateIssueRequest, ) -> Result<GitHubIssue>
Create an issue.
Sourcepub async fn update_issue(
&self,
owner: &str,
repo: &str,
number: i64,
req: &UpdateIssueRequest,
) -> Result<GitHubIssue>
pub async fn update_issue( &self, owner: &str, repo: &str, number: i64, req: &UpdateIssueRequest, ) -> Result<GitHubIssue>
Update an issue (close, reopen, edit title/body/labels).
Sourcepub async fn list_comments(
&self,
owner: &str,
repo: &str,
number: i64,
) -> Result<Vec<GitHubComment>>
pub async fn list_comments( &self, owner: &str, repo: &str, number: i64, ) -> Result<Vec<GitHubComment>>
List comments on an issue.
Sourcepub async fn add_comment(
&self,
owner: &str,
repo: &str,
number: i64,
body: &str,
) -> Result<GitHubComment>
pub async fn add_comment( &self, owner: &str, repo: &str, number: i64, body: &str, ) -> Result<GitHubComment>
Add a comment to an issue.
Sourcepub async fn list_gists(&self) -> Result<Vec<GitHubGist>>
pub async fn list_gists(&self) -> Result<Vec<GitHubGist>>
List published gists for the authenticated user.
Sourcepub async fn get_gist(&self, gist_id: &str) -> Result<GitHubGist>
pub async fn get_gist(&self, gist_id: &str) -> Result<GitHubGist>
Get a single gist including file contents.
Sourcepub async fn create_gist(&self, req: &CreateGistRequest) -> Result<GitHubGist>
pub async fn create_gist(&self, req: &CreateGistRequest) -> Result<GitHubGist>
Create a gist.
Sourcepub async fn update_gist(
&self,
gist_id: &str,
req: &UpdateGistRequest,
) -> Result<GitHubGist>
pub async fn update_gist( &self, gist_id: &str, req: &UpdateGistRequest, ) -> Result<GitHubGist>
Update a gist.