app.sources.gitlab_api

GitLab REST API v4 transport: client setup, retries, pagination, errors.

Used only by app.sources.gitlab. open_client() builds the httpx.AsyncClient (the shared outbound TLS context of app.tls, so certificates are verified unless ADRIANO_TLS_VERIFY is false; timeouts, connection limits, no redirects, token in the PRIVATE-TOKEN header). GitLabApi wraps it:

  • Bounded concurrency: every HTTP request holds one permit of a semaphore of MAX_CONCURRENT_REQUESTS (matching the connection pool size). A permit is held for one request only, never across pages or retry waits, so callers that already bound their own concurrency (the polling service) cannot deadlock with it.

  • Retries: HTTP 429, any 5xx and transport errors (including timeouts) are retried up to MAX_ATTEMPTS attempts in total, waiting BACKOFF_BASE * 2 ** (attempt - 1) seconds (capped at BACKOFF_CAP) or the server’s Retry-After (seconds or HTTP date, capped at RETRY_AFTER_CAP). Exhausted retries raise SourceUnavailableError.

  • Errors: 401/403 raise SourceAccessError, 404 raises SourceNotFoundError, any other non-2xx raises SourceError.

  • Pagination: GitLabApi.get_pages() follows the Link rel="next" header (keyset and offset pagination) and falls back to X-Next-Page. A next link outside the API root is refused, so the token is never sent to another origin.

The token is only ever placed in the client headers. Messages and log lines name the request path (which never carries a secret) and the token’s env var name, never its value.

Public API: MAX_CONCURRENT_REQUESTS, MAX_ATTEMPTS, BACKOFF_BASE, BACKOFF_CAP, RETRY_AFTER_CAP, PER_PAGE, MAX_PAGES, TIMEOUT, Sleep, api_root(), open_client(), parse_model(), GitLabApi.

app.sources.gitlab_api.MAX_CONCURRENT_REQUESTS = 4

Concurrent HTTP requests per adapter (also the connection pool size).

app.sources.gitlab_api.MAX_ATTEMPTS = 4

Attempts per request, including the first one.

app.sources.gitlab_api.BACKOFF_BASE = 0.5

First backoff wait in seconds (doubled per retry).

app.sources.gitlab_api.BACKOFF_CAP = 30.0

Longest computed backoff wait in seconds.

app.sources.gitlab_api.RETRY_AFTER_CAP = 60.0

Longest honoured Retry-After wait in seconds.

app.sources.gitlab_api.PER_PAGE = 100

Items requested per page.

app.sources.gitlab_api.MAX_PAGES = 1000

Pages followed per listing before giving up (loop guard).

app.sources.gitlab_api.TIMEOUT = Timeout(connect=10.0, read=30.0, write=30.0, pool=30.0)

Request timeouts (connect 10 s; read/write/pool 30 s).

type app.sources.gitlab_api.Sleep = Callable[[float], Awaitable[None]]

Async sleep used for retry waits (injectable for tests).

type app.sources.gitlab_api.QueryParams = Mapping[str, str | int]
app.sources.gitlab_api.api_root(base_url)

Build the REST API v4 root of a GitLab instance.

Parameters:

base_url (str) – Instance URL without trailing slash.

Return type:

str

Returns:

<base_url>/api/v4/.

app.sources.gitlab_api.open_client(base_url, token, *, transport=None, ssl_context=None)

Build the HTTP client of one poll.

Parameters:
  • base_url (str) – Instance URL without trailing slash.

  • token (str) – Access token (sent as PRIVATE-TOKEN).

  • transport (AsyncBaseTransport | None) – Transport override (tests use httpx.MockTransport); None uses the default transport.

  • ssl_context (SSLContext | None) – TLS context (see app.tls); None uses the default outbound context.

Return type:

AsyncClient

Returns:

The client; the caller closes it.

app.sources.gitlab_api.parse_model[M: BaseModel](model, data, what)

Validate one API object.

Parameters:
  • model (type[M]) – Expected shape.

  • data (object) – Decoded JSON.

  • what (str) – Request description for the message.

Return type:

M

Returns:

The model.

Raises:

SourceUnavailableError – If the shape is unexpected.

class app.sources.gitlab_api.GitLabApi(client, *, token_env, sleep=<function sleep>, max_concurrency=4)

Bases: object

Retrying, concurrency-bounded GET access to the REST API.

Parameters:
  • client (AsyncClient) – Client from open_client().

  • token_env (str) – Env var name of the token (for messages only).

  • sleep (Sleep) – Async sleep for retry waits.

  • max_concurrency (int) – Concurrent requests allowed.

async get(url, params=None)

GET with retries.

Parameters:
  • url (str) – Path relative to the API root, or an absolute URL inside it.

  • params (QueryParams | None) – Query parameters.

Return type:

Response

Returns:

The 2xx response.

Raises:

SourceError – See the module documentation.

async get_json(url, params=None)

GET and decode JSON.

Parameters:
  • url (str) – Path relative to the API root.

  • params (QueryParams | None) – Query parameters.

Return type:

object

Returns:

The decoded body.

Raises:

SourceError – On HTTP failure or invalid JSON.

async get_pages(url, params)

GET every page of a list endpoint.

Parameters:
  • url (str) – Path relative to the API root.

  • params (QueryParams) – Query parameters of the first page (per_page is added).

Return type:

list[object]

Returns:

All items in page order.

Raises:

SourceError – On any failure (no partial result).