Skip to content

Clients

The clients module provides client classes for interacting with the GitHub API when making requests. Currently, this module includes a client for the GitHub REST API, which can be used to make authenticated requests to the GitHub REST API endpoints.

Using these classes can simplify the process of making requests to the GitHub API, as they handle the authentication process and handle lots of the boilerplate code required to make requests to the GitHub API (i.e. setting up the header).

Benefits of Using the Clients Module

Using the REST API Client as an example, the benefits of using the client defined in this module include:

  • Simplified Authentication: The client handles the authentication process for you, allowing you to easily authenticate requests to the GitHub API using a GitHub App's credentials.
  • Consistent Interface: The client provides a consistent interface for making requests to the GitHub API, abstracting away the details of how requests are made and allowing you to focus on the specific API endpoints you want to interact with.
  • Reusability: By using the client defined in this module, you can reuse the same client across different parts of your application or across different applications, ensuring consistency in how you interact with the GitHub API and reducing the amount of duplicate code you need to write when making requests to the GitHub API.

To provide a code example:

## Without the module:

... Access Token Logic Here ...

import requests

headers = {
    "Authorization": f"Bearer {access_token}",
    "Accept": "application/vnd.github.v3+json"
}

response = requests.get("https://api.github.com/orgs/your_github_organisation/repos", headers=headers)
response.raise_for_status()

repos = response.json()
print(repos)

## With the module:

from policy_methods_library.github.clients import GitHubRestClient

client = GitHubRestClient(
    owner=github_organisation,
    app_id=app_id,
    private_key=private_key,
) # Auth logic encapsulated within the client

response = client.make_request("GET", "/orgs/your_github_organisation/repos")

repos = response.json()
print(repos)

This highlights the amount of boilerplate code that can be removed by using the client defined in this module, as well as the simplified authentication process and consistent interface for making requests to the GitHub API that the client provides.

Client Module Contents

GitHubRestClient

A client for making REST API calls to GitHub, authenticated as a GitHub App.

Source code in src/policy_methods_library/github/clients.py
class GitHubRestClient:
    """A client for making REST API calls to GitHub, authenticated as a GitHub App."""

    def __init__(
        self,
        owner: str,
        app_id: Optional[str] = None,
        private_key: Optional[str] = None,
        access_token: Optional[str] = None,
    ):
        """Initialises the GitHubRestClient with credentials or a direct access token.

        Args:
            owner (str): The account to authenticate for (i.e. the organisation a GitHub App is installed in, or the account the access_token is for).
            app_id (str, optional): The GitHub App's identifier.
            private_key (str, optional): The GitHub App's private key in PEM format.
            access_token (str, optional): A pre-generated GitHub access token.

        Raises:
            ValueError: If neither a valid access token nor app credentials are provided.
        """
        if not owner:
            raise ValueError("Owner is required to authenticate with GitHub.")

        if access_token:
            self.access_token = access_token
            self.owner = owner
        elif app_id and private_key and owner:
            self.access_token = get_access_token(app_id, private_key, owner)
            self.owner = owner
        else:
            raise ValueError(
                "You must provide either an access_token and the owner or an app_id, private_key, and the owner."
            )

    def make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
        """Makes an authenticated request to the GitHub API.

        Args:
            method (str): The HTTP method (e.g., 'GET', 'POST').
            endpoint (str): The API endpoint (e.g., '/repos/{owner}/{repo}').
            **kwargs: Additional arguments to pass to the `requests` method (e.g., json=data).

        Returns:
            requests.Response: The response from the GitHub API.

        Raises:
            requests.HTTPError: If the API request fails.
        """
        url = f"https://api.github.com{endpoint}"
        headers = {
            "Authorization": f"Bearer {self.access_token}",
            "Accept": "application/vnd.github.v3+json",
        }

        request_headers = kwargs.pop("headers", None)

        if request_headers:
            headers.update(request_headers)

        kwargs.setdefault("timeout", 30)
        response = requests.request(method, url, headers=headers, **kwargs)
        response.raise_for_status()  # Raise an error for bad responses
        return response

__init__(owner, app_id=None, private_key=None, access_token=None)

Initialises the GitHubRestClient with credentials or a direct access token.

Parameters:

Name Type Description Default
owner str

The account to authenticate for (i.e. the organisation a GitHub App is installed in, or the account the access_token is for).

required
app_id str

The GitHub App's identifier.

None
private_key str

The GitHub App's private key in PEM format.

None
access_token str

A pre-generated GitHub access token.

None

Raises:

Type Description
ValueError

If neither a valid access token nor app credentials are provided.

Source code in src/policy_methods_library/github/clients.py
def __init__(
    self,
    owner: str,
    app_id: Optional[str] = None,
    private_key: Optional[str] = None,
    access_token: Optional[str] = None,
):
    """Initialises the GitHubRestClient with credentials or a direct access token.

    Args:
        owner (str): The account to authenticate for (i.e. the organisation a GitHub App is installed in, or the account the access_token is for).
        app_id (str, optional): The GitHub App's identifier.
        private_key (str, optional): The GitHub App's private key in PEM format.
        access_token (str, optional): A pre-generated GitHub access token.

    Raises:
        ValueError: If neither a valid access token nor app credentials are provided.
    """
    if not owner:
        raise ValueError("Owner is required to authenticate with GitHub.")

    if access_token:
        self.access_token = access_token
        self.owner = owner
    elif app_id and private_key and owner:
        self.access_token = get_access_token(app_id, private_key, owner)
        self.owner = owner
    else:
        raise ValueError(
            "You must provide either an access_token and the owner or an app_id, private_key, and the owner."
        )

make_request(method, endpoint, **kwargs)

Makes an authenticated request to the GitHub API.

Parameters:

Name Type Description Default
method str

The HTTP method (e.g., 'GET', 'POST').

required
endpoint str

The API endpoint (e.g., '/repos/{owner}/{repo}').

required
**kwargs

Additional arguments to pass to the requests method (e.g., json=data).

{}

Returns:

Type Description
Response

requests.Response: The response from the GitHub API.

Raises:

Type Description
HTTPError

If the API request fails.

Source code in src/policy_methods_library/github/clients.py
def make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
    """Makes an authenticated request to the GitHub API.

    Args:
        method (str): The HTTP method (e.g., 'GET', 'POST').
        endpoint (str): The API endpoint (e.g., '/repos/{owner}/{repo}').
        **kwargs: Additional arguments to pass to the `requests` method (e.g., json=data).

    Returns:
        requests.Response: The response from the GitHub API.

    Raises:
        requests.HTTPError: If the API request fails.
    """
    url = f"https://api.github.com{endpoint}"
    headers = {
        "Authorization": f"Bearer {self.access_token}",
        "Accept": "application/vnd.github.v3+json",
    }

    request_headers = kwargs.pop("headers", None)

    if request_headers:
        headers.update(request_headers)

    kwargs.setdefault("timeout", 30)
    response = requests.request(method, url, headers=headers, **kwargs)
    response.raise_for_status()  # Raise an error for bad responses
    return response