Hi there! Sergio Pastor Pérez <[email protected]> skribis:
> Unfortunately I'm quite illiterate with regards to web related > things. Not sure how much it would help, but back in the day¹ I wrote the beginning of a client interface for the Data Service (attached here). Maybe this could serve to build useful tools? Ludo’. ¹ https://lists.gnu.org/archive/html/guix-devel/2021-06/msg00228.html
;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2021, 2022 Ludovic Courtès <[email protected]> ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix is free software; you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 3 of the License, or (at ;;; your option) any later version. ;;; ;;; GNU Guix is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. (define-module (guix data-service) #:use-module (json) #:use-module (web client) #:use-module (web response) #:use-module (web uri) #:use-module ((guix diagnostics) #:select (location)) #:use-module (srfi srfi-9) #:use-module (srfi srfi-19) #:use-module (srfi srfi-71) #:use-module (rnrs bytevectors) #:use-module (ice-9 binary-ports) #:use-module (ice-9 match) #:export (repository? repository-id repository-label repository-url repository-branches repository-revisions branch? branch-name branch-repository-id package-version? package-version-string package-version-branches package? package-name package-versions revision? revision-commit revision-date processed-revision? processed-revision-data-available? processed-revision-date processed-revision-commit build? build-server-id build-id build-time channel-instance? channel-instance-system channel-instance-derivation channel-instance-builds lint-warning? lint-warning-package lint-warning-package-version lint-warning-message lint-warning-location data-service-url open-data-service lookup-package lookup-repository known-repositories package-version-history revision-channel-instances revision-lint-warnings)) (define-json-mapping <repository> make-repository repository? json->repository (id repository-id) (label repository-label) (url repository-url) (revisions repository-branches "branches" (lambda (vector) ;; XXX: Not quite the same as <revision> since 'name' is ;; missing from <revision> and 'date' is missing from this ;; one. (map json->revision (vector->list vector))))) (define-json-mapping <branch> make-branch branch? json->branch (name branch-name) (repository-id branch-repository-id "git_repository_id")) (define-json-mapping <package-version> make-package-version package-version? json->package-version (string package-version-string "version") (branches package-version-branches "branches" (lambda (vector) (map json->branch (vector->list vector))))) (define-json-mapping <package> make-package package? json->package (name package-name) (versions package-versions "versions" (lambda (vector) (map json->package-version (vector->list vector))))) (define (utc-date date) "Return DATE with its timezone offset zeroed." (make-date (date-nanosecond date) (date-second date) (date-minute date) (date-hour date) (date-day date) (date-month date) (date-year date) 0)) (define (string->date* str) (utc-date (string->date str "~Y-~m-~d ~H:~M:~S"))) ;assume dates are UTC (define-json-mapping <revision> make-revision revision? json->revision (commit revision-commit) (date revision-date "datetime" string->date*)) (define-json-mapping <processed-revision> make-processed-revision processed-revision? json->processed-revision (data-available? processed-revision-data-available? "data_available") (commit processed-revision-commit "commit-hash") (date processed-revision-date "date" string->date*)) (define-json-mapping <package-version-range> make-package-version-range package-version-range? json->package-version-range (version package-version-range-version) (first-revision package-version-range-first-revision "first_revision" json->revision) (last-revision package-version-range-last-revision "last_revision" json->revision)) (define-json-mapping <build> make-build build? json->build (server-id build-server-id "build_server_id") (id build-id "build_server_build_id") (time build-time "timestamp" (lambda (str) (utc-date (string->date str "~Y-~m-~dT~H:~M:~S"))))) (define-json-mapping <channel-instance> make-channel-instance channel-instance? json->channel-instance (system channel-instance-system) (derivation channel-instance-derivation) (builds channel-instance-builds "builds" (lambda (vector) (map json->build (vector->list vector))))) (define (json->location alist) (location (assoc-ref alist "file") (assoc-ref alist "line-number") (assoc-ref alist "column-number"))) (define-json-mapping <lint-warning> make-lint-warning lint-warning? json->lint-warning (package lint-warning-package "package" (lambda (alist) (assoc-ref alist "name"))) (package-version lint-warning-package-version "package" (lambda (alist) (assoc-ref alist "version"))) (message lint-warning-message) (location lint-warning-location "location" json->location)) ;;; ;;; Calling the Guix Data Service. ;;; ;; Connection to an instance of the Data Service. (define-record-type <data-service> (data-service socket uri) data-service? (socket data-service-socket) (uri data-service-uri)) (define data-service-url (make-parameter "https://data.guix.gnu.org")) (define* (open-data-service #:optional (url (data-service-url))) "Open a connection to the Guix Data Service instance at URL." (let ((uri (string->uri url))) (data-service (open-socket-for-uri uri) uri))) (define (make-data-service-uri service path) (build-uri (uri-scheme (data-service-uri service)) #:host (uri-host (data-service-uri service)) #:port (uri-port (data-service-uri service)) #:path path)) (define (discard port n) "Read N bytes from PORT and discard them." (define bv (make-bytevector 4096)) (let loop ((n n)) (unless (zero? n) (match (get-bytevector-n! port bv 0 (min n (bytevector-length bv))) ((? eof-object?) #t) (read (loop (- n read))))))) (define (call service path) (let* ((uri (make-data-service-uri service path)) (response port (http-get uri #:port (data-service-socket service) #:keep-alive? #t #:headers '((Accept . "application/json")) #:streaming? #t))) (unless (= 200 (response-code response)) (when (response-content-length response) (discard port (response-content-length response))) (throw 'data-service-client-error uri response)) port)) (define (lookup-package service name) "Lookup package NAME and return a package record." (json->package (call service (string-append "/package/" name)))) (define (known-repositories service) "Return the list of repositories known to SERVICE." (map json->repository (let ((data (json->scm (call service "/repositories")))) (vector->list (assoc-ref data "repositories"))))) (define (lookup-repository service id) "Lookup the repository with the given ID, an integer, and return it." (json->repository (call service (string-append "/repository/" (number->string id))))) (define (repository-processed-revisions service repository branch) "Return the list of revisions processed for BRANCH in REPOSITORY." (map json->processed-revision (let ((data (json->scm (call service (string-append "/repository/" (repository-id repository) "/branch/" branch))))) (vector->list (assoc-ref data "revisions"))))) (define (package-version-history service branch package) "Return a list of package version ranges for PACKAGE, a string, on BRANCH, a <branch> record." ;; http://data.guix.gnu.org/repository/1/branch/master/package/emacs.json (map json->package-version-range (let ((result (json->scm (call service (string-append "/repository/" (number->string (branch-repository-id branch)) "/branch/" (branch-name branch) "/package/" package))))) (vector->list (assoc-ref result "versions"))))) (define (revision-channel-instances service commit) "Return the channel instances for COMMIT." (let ((result (json->scm (call service (string-append "/revision/" commit "/channel-instances"))))) (map json->channel-instance (vector->list (assoc-ref result "channel_instances"))))) (define* (revision-lint-warnings service commit #:optional linter) "Return lint warnings for COMMIT. If LINTER is given, only show warnings for the given linter--e.g., 'description'." (let ((result (json->scm (call service (string-append "/revision/" commit "/lint-warnings" (if linter (string-append "?linter=" linter) "")))))) (map json->lint-warning (vector->list (assoc-ref result "lint_warnings")))))
