Sourcehut.pl - SWI-Prolog package for interacting with sourcehut via GraphQL

Hey all,
Last week I’ve implemented a little SWI-Prolog package called sourcehut.pl for interacting with the GraphQL API of sourcehut, building on top of library(http/http_client).
Sourcehut is a software forge, which some people use for hosting some projects, myself included.
GraphQL is a specification for APIs, which defines a dedicated query language, a dedicated schema language for defining the specific requests supported by a certain API, and a JSON-based format for API responses. So a GraphQL API author writes a schema in the schema language, a consumer of the API writes requests in the query language, and the API responds with a JSON.

sourcehut.pl can be used to query information about git repositories, attach arbitrary artifacts to specific git tags, submit and inspect the state of CI jobs, etc.
For interacting with the GraphQL API, sourcehut.pl implements a rather general GraphQL client in library(sourcehut/common). It exploits the SWI-Prolog block operators facility to allow a large portion of the GraphQL query language to be read directly as Prolog text, for example:

%% sourcehut_git_repository(+UserName, +RepoName, -Repo, +Options) is semidet.
%
%  Unifies Repo with a dict describing the Git repository RepoName
%  owned by the user UserName on a remote SourceHut instance.
%
%  Options are passed to sourcehut_graphql/4.
sourcehut_git_repository(UserName, RepoName, Repo, Options) :-
    sourcehut_graphql(
        query {
            user(username:UserName) {
                repository(name:RepoName) {
                    id,
                    name,
                    description,
                    visibility,
                    created,
                    updated,
                    'HEAD' {
                        name,
                        target
                    }
                }
            }
        },
        Dict,
        Options),
    get_dict(data, Dict, Data),
    get_dict(user, Data, User),
    get_dict(repository, User, Repo),
    Repo \= null.

Note that the first argument to sourcehut_graphql is a plain Prolog term, which means we can embed dynamic values directly as Prolog variables. It also allows for creating queries dynamically from scratch.
Compare the above Prolog term to the following GraphQL query in the actual GraphQL query language:

        query {
            user(username:"eshel") {
                repository(name:"sourcehut.pl" {
                    id,
                    name,
                    description,
                    visibility,
                    created,
                    updated,
                    HEAD {
                        name,
                        target
                    }
                }
            }
        }

Pretty similar! we could almost just write/1 the Prolog term to obtain a GraphQL query, but some care has to be taken, for example around GraphQL identifiers that begin with a capital letter (as HEAD above). sourcehut_graphql uses a DCG, graphql//1, for safely serializing the Prolog term that represents the query into a proper GraphQL query text.

As a fun fact, since sourcehut.pl is also hosted on sourcehut, its CI relies on sourcehut.pl itself to upload tagged versions as publicly downloadable zip files (like in this CI log).

2 Likes