Skip to main content

GitHubClient

Struct GitHubClient 

Source
pub struct GitHubClient { /* private fields */ }
Expand description

GitHub REST API client for issues management.

Implementations§

Source§

impl GitHubClient

Source

pub fn new(token: &str) -> Self

Source

pub fn against(base: &str, token: &str) -> Self

A client for an API at base. Tests use it; nothing else does.

Source

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.

Source

pub async fn validate_token(&self) -> Result<String>

Validate the token by fetching the authenticated user.

Source

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.

Source

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

Source

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.

Source

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

Source

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.

Source

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.

Source

pub async fn list_releases_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubRelease>>>

Source

pub async fn list_tags_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubTag>>>

Source

pub async fn repository_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<GitHubRepository>>

Source

pub async fn list_milestones_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubMilestone>>>

Source

pub async fn list_labels_changed( &self, owner: &str, repo: &str, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubLabel>>>

Source

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.

Source

pub async fn list_notifications_changed( &self, conditions: &Conditions, ) -> Result<Fetched<Vec<GitHubNotification>>>

What GitHub thinks needs this person, across every repository.

Source

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.

Source

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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

pub async fn delete_project_item( &self, project_id: &str, item_id: &str, ) -> Result<()>

Take a card off the board.

The issue itself is untouched — this removes the card, which is the only thing a board owns. A draft has nowhere else to be, so deleting one deletes it.

Source§

impl GitHubClient

Source

pub async fn list_issues( &self, owner: &str, repo: &str, state: &str, ) -> Result<Vec<GitHubIssue>>

List issues for a repository.

Source

pub async fn get_issue( &self, owner: &str, repo: &str, number: i64, ) -> Result<GitHubIssue>

Get a single issue.

Source

pub async fn create_issue( &self, owner: &str, repo: &str, req: &CreateIssueRequest, ) -> Result<GitHubIssue>

Create an issue.

Source

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).

Source

pub async fn list_comments( &self, owner: &str, repo: &str, number: i64, ) -> Result<Vec<GitHubComment>>

List comments on an issue.

Source

pub async fn add_comment( &self, owner: &str, repo: &str, number: i64, body: &str, ) -> Result<GitHubComment>

Add a comment to an issue.

Source

pub async fn list_gists(&self) -> Result<Vec<GitHubGist>>

List published gists for the authenticated user.

Source

pub async fn get_gist(&self, gist_id: &str) -> Result<GitHubGist>

Get a single gist including file contents.

Source

pub async fn create_gist(&self, req: &CreateGistRequest) -> Result<GitHubGist>

Create a gist.

Source

pub async fn update_gist( &self, gist_id: &str, req: &UpdateGistRequest, ) -> Result<GitHubGist>

Update a gist.

Trait Implementations§

Source§

impl Clone for GitHubClient

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more