# Sourcehut
A simple client library for the [sr.ht](https://sr.ht/) REST API.
## Requirements
* [http-client](https://wiki.call-cc.org/eggref/5/http-client)
* [intarweb](https://wiki.call-cc.org/eggref/5/intarweb)
* [medea](https://wiki.call-cc.org/eggref/5/medea)
* [module-declarations](https://wiki.call-cc.org/eggref/5/module-declarations)
* [openssl](https://wiki.call-cc.org/eggref/5/openssl)
* [optimism](https://wiki.call-cc.org/eggref/5/optimism)
* [simple-exceptions](https://wiki.call-cc.org/eggref/5/simple-exceptions)
* [srfi-1](https://wiki.call-cc.org/eggref/5/srfi-1)
* [srfi-133](https://wiki.call-cc.org/eggref/5/srfi-133)
## Quick Start
Create a new job on [builds.sr.ht](https://builds.sr.ht/) and fetch
information about it:
```scheme
(import (sourcehut)
(sourcehut builds))
(access-token "your-access-token-goes-here")
(create (job manifest: "xyz"))
; => ((#:service "builds" #:path "/api/jobs")
; (id . 1234))
(retrieve (job 1234))
; => ((#:service "builds" #:path "/api/jobs/1234")
; (id . 1234)
; (status . "running")
; (setup_log ...)
; (tasks . #(...))
; (runner ...))
(retrieve (manifest 1234))
; => "xyz"
```
Subscribe or unsubscribe from a mailing list:
```scheme
(create (subscription list: "~sircmpwn/sr.ht-announce"))
; => ((id . 24)
; (created . "2018-07-08T23:46:31+00:00")
; (list (name . "sr.ht-announce")
; (owner (canonical_name . "~sircmpwn") (name . "sircmpwn"))))
(retrieve (subscriptions))
; => ((#:service "lists" #:path "/api/subscriptions")
; (next . null)
; (results
; .
; #(((id . 24)
; (created . "2018-07-08T23:46:31+00:00")
; (list (name . "sr.ht-announce")
; (owner (canonical_name . "~sircmpwn") (name . "sircmpwn"))))))
; (total . 1)
; (results_per_page . 50))
(delete (subscription 24))
; => #t
```
## Usage
### Authentication
There are two ways to authenticate API requests. The first is to set the
`access-token` parameter:
```scheme
(import (sourcehut))
(access-token "...")
```
The second is to set the `SRHT_ACCESS_TOKEN` environment variable:
```scheme
(import (chicken process-context))
(set-environment-variable! "SRHT_ACCESS_TOKEN" "...")
```
If both of these are set, the parameter value is used.
### Creating Requests
This library follows a typical [CRUD][] pattern, where you (1) create a
payload representing a remote resource, then (2) send it to the server
with one of the `create`, `retrieve`, `update` or `delete` procedures.
Resources are represented as Scheme objects per [medea][]'s default
JSON-to-Scheme conversion rules. Requests and responses are represented
as association lists, where the first item specifies a remote endpoint
from which a resource should be (or has been) fetched:
```scheme
(mailing-lists)
; => ((#:service "lists" #:path "/api/lists"))
(mailing-list "foo")
; => ((#:service "lists" #:path "/api/lists/foo"))
(mailing-list name: "foo" description: "bar")
; => ((#:service "lists" #:path "/api/lists")
; (name . "foo")
; (description . "bar"))
```
Objects of this shape are referred to as "crud" throughout this
documentation.
[CRUD]: https://en.wikipedia.org/wiki/Create,_read,_update_and_delete
[medea]: https://wiki.call-cc.org/eggref/5/medea
### Pagination
Many API responses are subject to [pagination][pagination].
To specify a starting ID, use the `page` combinator. This sets the
`start` parameter for GET requests, per [sr.ht's API][pagination-api]:
```scheme
(import (only (sourcehut) retrieve page))
; retrieve the first page of results
(retrieve (emails "~user"))
; retrieve results starting from id 42
(retrieve (page (emails "~user") 42))
```
[pagination]: https://en.wikipedia.org/wiki/Pagination
[pagination-api]: https://man.sr.ht/api-conventions.md#pagination
### API
#### sourcehut
The `(sourcehut)` library provides configuration parameters and procedures
for submitting API requests.
[parameter] (access-token) => string
[parameter] (access-token string) => string
Sets the client's API token for authentication.
The value should be a [personal access token][tokens], which can be
created (or revoked) from your [account settings page][oauth].
This library does not support OAuth-based authentication.
[oauth]: https://meta.sr.ht/oauth
[tokens]: https://man.sr.ht/meta.sr.ht/oauth-api.md#personal-access-tokens
[parameter] (service-domain) => string
[parameter] (service-domain string) => string
Specifies the hostname of the remote service, useful for connecting to a
sr.ht instance other than .
The default value is simply `"sr.ht"`.
[procedure] (create crud) => any
[procedure] (retrieve crud) => any
[procedure] (update crud) => any
[procedure] (delete crud) => any
Submits a CRUD payload to the server.
These procedures correspond to the `POST`, `GET`, `PUT` and `DELETE`
request methods, respectively. The result is a Scheme representation of
the response (generally a "crud object"), or `#f` if the requested
resource was not found.
If the response is an error (other than HTTP 404), a condition of type
`(exn http sourcehut)` is signaled.
[procedure] (page crud) => crud
Sets the starting ID for results fetched with `retrieve`.
Refer to [Pagination](#Pagination) for details.
#### builds
The `(sourcehut builds)` library provides request builders for
[builds.sr.ht](https://man.sr.ht/builds.sr.ht/api.md).
[procedure] (jobs) => crud
[Retrieves a list of jobs][get-apijobs].
This endpoint is subject to [pagination](#Pagination).
[get-apijobs]: https://man.sr.ht/builds.sr.ht/api.md#get-apijobs
[procedure] (job number) => crud
[procedure] (job #!key argument ...) => crud
In the first form, [fetches a job by ID][get-apijobsid].
In the second form, [creates a new job][post-apijobs].
`number` should be a job resource ID.
[get-apijobsid]: https://man.sr.ht/builds.sr.ht/api.md#get-apijobsid
[post-apijobs]: https://man.sr.ht/builds.sr.ht/api.md#post-apijobs
[procedure] (manifest number) => crud
[Retrieves a job's manifest][get-apijobsidmanifest].
`number` should be a job resource ID.
[get-apijobsidmanifest]: https://man.sr.ht/builds.sr.ht/api.md#get-apijobsidmanifest
[procedure] (start number) => crud
[Starts a job][post-apijobsidstart] that was created with `execute: #f`.
`number` should be a job resource ID.
[post-apijobsidstart]: https://man.sr.ht/builds.sr.ht/api.md#post-apijobsidstart
#### git
The `(sourcehut git)` library provides request builders for
[git.sr.ht](https://man.sr.ht/git.sr.ht/api.md).
[procedure] (log string) => crud
[procedure] (log string string) => crud
In the first form, [retrieves a list of references in the given
repository belonging to the active user][get-apireposnamerefs].
In the second form, [retrieves references for a repository of the given
user][get-apiusernamereposnamerefs].
This endpoint is subject to [pagination](#Pagination).
[get-apireposnamerefs]: https://man.sr.ht/git.sr.ht/api.md#get-apireposnamerefs
[get-apiusernamereposnamerefs]: https://man.sr.ht/git.sr.ht/api.md#get-apiusernamereposnamerefs
[procedure] (log string) => crud
[procedure] (log string string) => crud
In the first form, [retrieves a list of the latest commits on the
default branch of the given repository belonging to the active
user][get-apireposnamelog].
In the second form, [retrieves commits for a repository of the given
user][get-apiusernamereposnamelog].
This endpoint is subject to [pagination](#Pagination).
[get-apireposnamelog]: https://man.sr.ht/git.sr.ht/api.md#get-apireposnamelog
[get-apiusernamereposnamelog]: https://man.sr.ht/git.sr.ht/api.md#get-apiusernamereposnamelog
[procedure] (artifact string string #!key file filename) => crud
[procedure] (artifact string string string #!key file filename) => crud
In the first form, [attaches an artifact to a reference in the given
repository belonging to the active user][post-apireposnameartifactsref].
In the second form, [attaches an artifact to a reference in a repository
of the given user][post-apiusernamereposnameartifactsref].
`file` should either be an input port or a string containing the file
contents. A `filename` can also be given, to specify a name for the
file. If not given, the name of the input port will be used.
[post-apireposnameartifactsref]: https://man.sr.ht/git.sr.ht/api.md#post-apireposnameartifactsref
[post-apiusernamereposnameartifactsref]: https://man.sr.ht/git.sr.ht/api.md#post-apiusernamereposnameartifactsref
#### lists
The `(sourcehut lists)` library provides request builders for
[lists.sr.ht](https://man.sr.ht/lists.sr.ht/api.md).
[procedure] (user string) => crud
[Retrieves a user][get-apiuserusername].
`string` should be a username or email address.
[get-apiuserusername]: https://man.sr.ht/lists.sr.ht/api.md#get-apiuserusername
[procedure] (subscriptions) => crud
[Retrieves the active user's mailing list subscriptions][get-apisubscriptions].
[get-apisubscriptions]: https://man.sr.ht/lists.sr.ht/api.md#get-apisubscriptions
[procedure] (subscription number) => crud
[procedure] (subscription #!key list) => crud
In the first form, [retrieves a subscription by ID][get-apisubscriptionssub-id].
In the second form, [subscribes to a mailing list][post-apisubscriptions].
`number` should be a subscription resource ID.
[get-apisubscriptionssub-id]: https://man.sr.ht/lists.sr.ht/api.md#get-apisubscriptionssub-id
[post-apisubscriptions]: https://man.sr.ht/lists.sr.ht/api.md#post-apisubscriptions
[procedure] (emails) => crud
[procedure] (emails string) => crud
Retrieves emails sent by the [active user][get-apiemails], or by the
[given user][get-apiuserusernameemails].
`string` should be a username or email address.
This endpoint is subject to [pagination](#Pagination).
[get-apiemails]: https://man.sr.ht/lists.sr.ht/api.md#get-apiemails
[get-apiuserusernameemails]: https://man.sr.ht/lists.sr.ht/api.md#get-apiuserusernameemails
[procedure] (email number) => crud
[Retrieves an email][get-apiemailsemail-id].
`number` should be an email resource ID.
[get-apiemailsemail-id]: https://man.sr.ht/lists.sr.ht/api.md#get-apiemailsemail-id
[procedure] (thread number) => crud
[Retrieves an email thread][get-apithreademail-id].
`number` should be an email resource ID.
This endpoint is *not* subject to pagination. Rather, the result is a
vector containing all emails in the thread.
[get-apithreademail-id]: https://man.sr.ht/lists.sr.ht/api.md#get-apithreademail-id
[procedure] (mailing-lists) => crud
[procedure] (mailing-lists string) => crud
Retrieves mailing lists belonging to the [active user][get-apilists], or
to the [given user][get-apiuserusernamelists].
`string` should be a username or email address.
This endpoint is subject to [pagination](#Pagination).
[get-apilists]: https://man.sr.ht/lists.sr.ht/api.md#get-apilists
[get-apiuserusernamelists]: https://man.sr.ht/lists.sr.ht/api.md#get-apiuserusernamelists
[procedure] (mailing-list string string) => crud
[procedure] (mailing-list string #!key description) => crud
[procedure] (mailing-list #!key name description) => crud
In the first form, [retrieves a subscription by ID][get-apiuserusernamelistslist-name].
In the second form, [updates a mailing list][put-apilistslist-name].
In the third form, [creates a mailing list][post-apilists].
The `string` arguments should be user and list names.
[get-apiuserusernamelistslist-name]: https://man.sr.ht/lists.sr.ht/api.md#get-apiuserusernamelistslist-name
[put-apilistslist-name]: https://man.sr.ht/lists.sr.ht/api.md#put-apilistslist-name
[post-apilists]: https://man.sr.ht/lists.sr.ht/api.md#post-apilists
[procedure] (posts string) => crud
[procedure] (posts string string) => crud
Retrieves posts to a mailing list, given a [list name][get-apilistslist-namepost]
or [list owner and name][get-apiuserusernamelistslist-nameposts].
In the first form, the `string` argument should be a mailing list name,
where the list belongs to the active user.
In the second form, the arguments should be a username (the list owner)
and mailing list name.
This endpoint is subject to [pagination](#Pagination).
[get-apilistslist-namepost]: https://man.sr.ht/lists.sr.ht/api.md#get-apilistslist-nameposts
[get-apiuserusernamelistslist-nameposts]: https://man.sr.ht/lists.sr.ht/api.md#get-apiuserusernamelistslist-nameposts
#### meta
The `(sourcehut meta)` library provides request builders for
[meta.sr.ht](https://man.sr.ht/meta.sr.ht/api.md).
[procedure] (profile) => crud
[procedure] (profile #!key argument ...) => crud
In the first form, [fetches the active user's profile][get-apiuserprofile].
In the second form, [updates the user's profile][put-apiuserprofile].
[get-apiuserprofile]: https://man.sr.ht/meta.sr.ht/user-api.md#get-apiuserprofile
[put-apiuserprofile]: https://man.sr.ht/meta.sr.ht/user-api.md#put-apiuserprofile
[procedure] (audit-log) => crud
[Retrieves the active user's audit log][get-apiuseraudit-log].
This endpoint is subject to [pagination](#Pagination).
[get-apiuseraudit-log]: https://man.sr.ht/meta.sr.ht/user-api.md#get-apiuseraudit-log
[procedure] (ssh-keys) => crud
[Retrieves the active user's SSH keys][get-apiuserssh-keys].
This endpoint is subject to [pagination](#Pagination).
[get-apiuserssh-keys]: https://man.sr.ht/meta.sr.ht/user-api.md#get-apiuserssh-keys
[procedure] (ssh-key number) => crud
[procedure] (ssh-key #!key ssh-key) => crud
In the first form, [fetches an SSH key by ID][get-apiuserssh-keysid].
In the second form, [creates a new SSH key][post-apiuserssh-keys].
`number` should be a key resource ID.
[get-apiuserssh-keysid]: https://man.sr.ht/meta.sr.ht/user-api.md#get-apiuserssh-keysid
[post-apiuserssh-keys]: https://man.sr.ht/meta.sr.ht/user-api.md#post-apiuserssh-keys
[procedure] (pgp-keys) => crud
[Retrieves the active user's PGP keys][get-apiuserpgp-keys].
This endpoint is subject to [pagination](#Pagination).
[get-apiuserpgp-keys]: https://man.sr.ht/meta.sr.ht/user-api.md#get-apiuserpgp-keys
[procedure] (pgp-key number) => crud
[procedure] (pgp-key #!key pgp-key) => crud
In the first form, [fetches a PGP key by ID][get-apiuserpgp-keysid].
In the second form, [creates a new PGP key][post-apiuserpgp-keys].
`number` should be a key resource ID.
[get-apiuserpgp-keysid]: https://man.sr.ht/meta.sr.ht/user-api.md#get-apiuserpgp-keysid
[post-apiuserpgp-keys]: https://man.sr.ht/meta.sr.ht/user-api.md#post-apiuserpgp-keys
#### paste
The `(sourcehut paste)` library provides request builders for
[paste.sr.ht](https://man.sr.ht/paste.sr.ht/api.md).
[procedure] (paste string) => crud
[procedure] (paste #!key contents filename visibility) => crud
In the first form, [fetches a paste by ID][get-apipastessha].
In the second form, [creates a new paste][post-apipastes].
`string` should be a paste SHA.
[get-apipastessha]: https://man.sr.ht/paste.sr.ht/api.md#get-apipastessha
[post-apipastes]: https://man.sr.ht/paste.sr.ht/api.md#post-apipastes
[procedure] (blob string) => crud
[Fetches a blob][get-apiblobssha].
`string` should be a blob SHA.
[get-apiblobssha]: https://man.sr.ht/paste.sr.ht/api.md#get-apiblobssha
[procedure] (pastes) => crud
[Retrieves a list of pastes][get-apipastes].
This endpoint is subject to [pagination](#Pagination).
[get-apipastes]: https://man.sr.ht/paste.sr.ht/api.md#get-apipastes
#### todo
The `(sourcehut todo)` library provides request builders for
[todo.sr.ht](https://man.sr.ht/todo.sr.ht/api.md).
[procedure] (trackers) => crud
[procedure] (trackers string) => crud
Retrieves trackers belonging to the [active user][get-apitrackers], or
to the [given user][get-apiuserusernametrackers].
`string` should be a username.
This endpoint is subject to [pagination](#Pagination).
[get-apitrackers]: https://man.sr.ht/todo.sr.ht/api.md#get-apitrackers
[get-apiuserusernametrackers]: https://man.sr.ht/todo.sr.ht/api.md#get-apiuserusernametrackers
[procedure] (tracker string) => crud
[procedure] (tracker string string) => crud
[procedure] (tracker string #!key description) => crud
[procedure] (tracker #!key name description) => crud
In the first form, [retrieves a tracker belonging to the active user by name][get-apitrackerstracker-name].
In the second form, [retrieves a tracker belonging to the given user by name][get-apiuserusernametrackerstracker-name].
In the third form, [updates a tracker][put-apitrackerstracker-name].
In the fourth form, [creates a tracker][post-apitrackers].
The `string` arguments should be user or tracker names.
[get-apitrackerstracker-name]: https://man.sr.ht/todo.sr.ht/api.md#get-apitrackerstracker-name
[get-apiuserusernametrackerstracker-name]: https://man.sr.ht/todo.sr.ht/api.md#get-apiuserusernametrackerstracker-name
[put-apitrackerstracker-name]: https://man.sr.ht/todo.sr.ht/api.md#put-apitrackerstracker-name
[post-apitrackers]: https://man.sr.ht/todo.sr.ht/api.md#post-apitrackers
[procedure] (tickets string) => crud
[procedure] (tickets string string) => crud
Retrieves tickets in the tracker belonging to the [active user][get-apitrackerstracker-nameticketsticket-id],
or to the [given user][get-apiuserusernametrackerstracker-nameticketsticket-id].
In the first form, the `string` argument should be a tracker name, where
the tracker belongs to the active user.
In the second form, the arguments should be a username (the tracker
owner) and tracker name.
This endpoint is subject to [pagination](#Pagination).
[get-apitrackerstracker-nametickets]: https://man.sr.ht/todo.sr.ht/api.md#get-apitrackerstracker-nametickets
[get-apiuserusernametrackerstracker-nametickets]: https://man.sr.ht/todo.sr.ht/api.md#get-apiuserusernametrackerstracker-nametickets
[procedure] (ticket string number) => crud
[procedure] (ticket string string number) => crud
In the first form, [retrieves a ticket in a tracker belonging to the active user][get-apitrackerstracker-nameticketsticket-id].
In the second form, [retrieves a ticket in a tracker belonging to the given user][get-apiuserusernametrackerstracker-nameticketsticket-id].
In both cases, the numeric argument should be a ticket ID.
[get-apitrackerstracker-nameticketsticket-id]: https://man.sr.ht/todo.sr.ht/api.md#get-apitrackerstracker-nameticketsticket-id
[get-apiuserusernametrackerstracker-nameticketsticket-id]: https://man.sr.ht/todo.sr.ht/api.md#get-apiuserusernametrackerstracker-nameticketsticket-id
[procedure] (events string number) => crud
[procedure] (events string string number) => crud
Retrieves events in a ticket for the tracker belonging to the [active user][get-apitrackerstracker-nameticketsticket-idevents],
or to the [given user][get-apiuserusernametrackerstracker-nameticketsticket-idevents].
In the first form, the `string` argument should be a tracker name and
`number` should be a ticket ID.
In the second form, the `string` arguments should be a username (the tracker
owner) and tracker name, and `number` should be a ticket ID.
This endpoint is subject to [pagination](#Pagination).
[get-apitrackerstracker-nameticketsticket-idevents]: https://man.sr.ht/todo.sr.ht/api.md#get-apitrackerstracker-nameticketsticket-idevents
[get-apiuserusernametrackerstracker-nameticketsticket-idevents]: https://man.sr.ht/todo.sr.ht/api.md#get-apiuserusernametrackerstracker-nameticketsticket-idevents
## Links
* Sources:
* Issues:
* Documentation:
This extension was named [topham][] prior to version 0.2.0.
[topham]: https://wiki.call-cc.org/eggref/5/topham
## License
This extension is licensed under the [3-clause BSD license][license].
[license]: https://opensource.org/licenses/BSD-3-Clause