Exgit.CloudflareArtifacts (exgit v0.1.0)

Copy Markdown View Source

Cloudflare Artifacts REST API client.

Wraps the control-plane endpoints documented at https://developers.cloudflare.com/artifacts/api/rest-api/ — repos (create / list / get / delete / fork / import) and repo tokens (create / list / delete).

Function and field names mirror upstream verbatim: create_repo not mint_repo, default_branch not branch, plaintext not secret. The struct shapes (Repo, Token) match the TypeScript interfaces in the upstream docs so the two can be cross-referenced directly.

Setup

new/1 returns a %Req.Request{} with base_url, bearer auth, and a default user-agent already set:

client = Exgit.CloudflareArtifacts.new(
  account_id: System.fetch_env!("CF_ACCOUNT_ID"),
  namespace: "default",
  api_token: System.fetch_env!("CF_API_TOKEN")
)

Any extra options (e.g. :plug for testing, :retry, :receive_timeout) are forwarded to Req.new/1.

Full lifecycle

{:ok, %Repo{remote: remote}} =
  Exgit.CloudflareArtifacts.create_repo(client,
    name: "starter-repo",
    default_branch: "main"
  )

{:ok, %Token{plaintext: token}} =
  Exgit.CloudflareArtifacts.create_token(client,
    repo: "starter-repo",
    scope: "write",
    ttl: 86_400
  )

transport = Exgit.Transport.HTTP.new(remote,
  auth: Exgit.Credentials.Artifacts.auth(token)
)

# ... push, fetch, clone via the existing transport ...

{:ok, _} = Exgit.CloudflareArtifacts.delete_repo(client, "starter-repo")

Return shapes

Single-resource endpoints return {:ok, struct}. List endpoints return {:ok, items, result_info}result_info is the upstream pagination map verbatim (cursor for repos, offset for tokens).

Errors:

  • {:error, %Req.Response{}} — non-2xx, or 2xx with success: false. The parsed v4 envelope is in body; the upstream error list lives at body["errors"] (each entry has code, message, optional documentation_url).
  • {:error, exception} — Req-level transport failure (DNS, TLS, connection refused, etc.).

Pattern-match on specific upstream codes directly:

case Exgit.CloudflareArtifacts.create_token(client, repo: r, ttl: 99_999_999) do
  {:error, %Req.Response{body: %{"errors" => [%{"code" => 10103} | _]}}} ->
    :ttl_out_of_range
  ...
end

Summary

Functions

Create a repo. POST /repos.

Mint a repo-scoped token. POST /tokens.

Delete a repo. DELETE /repos/:name. Returns 202 upstream; the resulting Repo carries only :id.

Revoke a token. DELETE /tokens/:id. The returned Token carries only :id.

Fork a repo. POST /repos/:name/fork.

Get a repo by name. GET /repos/:name.

Import a public HTTPS git remote. POST /repos/:name/import.

List repos. GET /repos.

List tokens for a repo. GET /repos/:name/tokens.

Build a configured %Req.Request{} for the Cloudflare Artifacts API.

Functions

create_repo(req, opts)

@spec create_repo(
  Req.Request.t(),
  keyword()
) :: {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}

Create a repo. POST /repos.

Required option: :name. Optional: :description, :default_branch, :read_only. The returned Repo includes a short-lived inline :token — use create_token/2 for longer TTLs.

create_token(req, opts)

@spec create_token(
  Req.Request.t(),
  keyword()
) :: {:ok, Exgit.CloudflareArtifacts.Token.t()} | {:error, term()}

Mint a repo-scoped token. POST /tokens.

Required option: :repo. Optional: :scope (`"read""write"` or
the matching atoms `:read:write; default"write"),:ttl`

(seconds, default 86_400; capped at 31_536_000 — code: 10103 if exceeded). Returns a Token with :plaintext set.

delete_repo(req, name)

@spec delete_repo(Req.Request.t(), String.t()) ::
  {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}

Delete a repo. DELETE /repos/:name. Returns 202 upstream; the resulting Repo carries only :id.

delete_token(req, token_id)

@spec delete_token(Req.Request.t(), String.t()) ::
  {:ok, Exgit.CloudflareArtifacts.Token.t()} | {:error, term()}

Revoke a token. DELETE /tokens/:id. The returned Token carries only :id.

fork_repo(req, source_name, opts)

@spec fork_repo(Req.Request.t(), String.t(), keyword()) ::
  {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}

Fork a repo. POST /repos/:name/fork.

Required option: :name (the new repo). Optional: :description, :read_only, :default_branch_only. The returned Repo adds an :objects count.

get_repo(req, name)

@spec get_repo(Req.Request.t(), String.t()) ::
  {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}

Get a repo by name. GET /repos/:name.

import_repo(req, name, opts)

@spec import_repo(Req.Request.t(), String.t(), keyword()) ::
  {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}

Import a public HTTPS git remote. POST /repos/:name/import.

name is the destination repo name; :url in the body is the HTTPS source remote. Optional body fields: :branch, :depth, :read_only. May 409 while a previous import/fork is still in progress.

list_repos(req, opts \\ [])

@spec list_repos(
  Req.Request.t(),
  keyword()
) :: {:ok, [Exgit.CloudflareArtifacts.Repo.t()], map()} | {:error, term()}

List repos. GET /repos.

Optional: :limit (default 50, max 200), :cursor, :search, :sort ("created_at" | "updated_at" | "last_push_at" | "name"), :direction ("asc" | "desc").

list_tokens(req, repo_name, opts \\ [])

@spec list_tokens(Req.Request.t(), String.t(), keyword()) ::
  {:ok, [Exgit.CloudflareArtifacts.Token.t()], map()} | {:error, term()}

List tokens for a repo. GET /repos/:name/tokens.

Optional: :state (`"active""expired""revoked""all"` or
the matching atoms `:active:expired:revoked:all`; default

"active"), :per_page (default 30, max 100), :page (default 1). Listed tokens have :plaintext set to nil — the API never re-emits the secret.

new(opts)

@spec new(keyword()) :: Req.Request.t()

Build a configured %Req.Request{} for the Cloudflare Artifacts API.

Required: :account_id, :api_token. Optional: :namespace (defaults to "default"), :base_url. Any other keys (:plug, :retry, :receive_timeout, …) are forwarded to Req.new/1.