summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Baines <mail@cbaines.net>2019-05-11 22:56:25 +0100
committerChristopher Baines <mail@cbaines.net>2019-05-11 22:56:25 +0100
commit658a1a20b20760cbe11d6442e3a15c7f27ae568e (patch)
tree569fe03ebc9a59e5e1bf047c2ab725bb4e0feab3
parent640fb8a2ad262e06b138deb975f92e6acb3a423b (diff)
downloaddata-service-658a1a20b20760cbe11d6442e3a15c7f27ae568e.tar
data-service-658a1a20b20760cbe11d6442e3a15c7f27ae568e.tar.gz
Improve the content negotiation handling in general
Previously, the routing layer handled the content negotiation, and the Accept header was ignored. Now, the extension if one is provided in the URL is still used, and more widely than before, but the Accept header is also taken in to account. This all now happens before the routing decisions are made, so the routing is now pretty much extension independant (with the exception of the /gnu/store/... routes).
-rw-r--r--guix-data-service/web/controller.scm243
-rw-r--r--guix-data-service/web/server.scm12
-rw-r--r--guix-data-service/web/util.scm52
3 files changed, 163 insertions, 144 deletions
diff --git a/guix-data-service/web/controller.scm b/guix-data-service/web/controller.scm
index afe98cb..4142a83 100644
--- a/guix-data-service/web/controller.scm
+++ b/guix-data-service/web/controller.scm
@@ -78,28 +78,30 @@
target-commit
(commit->revision-id conn target-commit))))
-(define (render-compare-unknown-commit content-type
+(define (render-compare-unknown-commit mime-types
conn
base-commit
base-revision-id
target-commit
target-revision-id)
- (cond
- ((eq? content-type 'json)
- (render-json
- '((unknown_commit . #t))))
- (else
- (apply render-html
- (compare-unknown-commit base-commit
- target-commit
- (if base-revision-id #t #f)
- (if target-revision-id #t #f)
- (select-job-for-commit conn
- base-commit)
- (select-job-for-commit conn
- target-commit))))))
+ (case (most-appropriate-mime-type
+ '(application/json text/html)
+ mime-types)
+ ((application/json)
+ (render-json
+ '((unknown_commit . #t))))
+ (else
+ (apply render-html
+ (compare-unknown-commit base-commit
+ target-commit
+ (if base-revision-id #t #f)
+ (if target-revision-id #t #f)
+ (select-job-for-commit conn
+ base-commit)
+ (select-job-for-commit conn
+ target-commit))))))
-(define (render-compare content-type
+(define (render-compare mime-types
conn
base-commit
base-revision-id
@@ -123,23 +125,25 @@
(derivation-changes
(package-data-derivation-changes base-packages-vhash
target-packages-vhash)))
- (cond
- ((eq? content-type 'json)
- (render-json
- `((new-packages . ,(list->vector new-packages))
- (removed-packages . ,(list->vector removed-packages))
- (version-changes . ,version-changes)
- (derivation-changes . ,derivation-changes))))
- (else
- (apply render-html
- (compare base-commit
- target-commit
- new-packages
- removed-packages
- version-changes
- derivation-changes)))))))
+ (case (most-appropriate-mime-type
+ '(application/json text/html)
+ mime-types)
+ ((application/json)
+ (render-json
+ `((new-packages . ,(list->vector new-packages))
+ (removed-packages . ,(list->vector removed-packages))
+ (version-changes . ,version-changes)
+ (derivation-changes . ,derivation-changes))))
+ (else
+ (apply render-html
+ (compare base-commit
+ target-commit
+ new-packages
+ removed-packages
+ version-changes
+ derivation-changes)))))))
-(define (render-compare/derivations content-type
+(define (render-compare/derivations mime-types
conn
query-parameters)
(define (derivations->alist derivations)
@@ -154,18 +158,20 @@
derivations))
(if (any-invalid-query-parameters? query-parameters)
- (cond
- ((eq? content-type 'json)
- (render-json
- '((error . "invalid query"))))
- (else
- (apply render-html
- (compare/derivations
- query-parameters
- (valid-systems conn)
- build-status-strings
- '()
- '()))))
+ (case (most-appropriate-mime-type
+ '(application/json text/html)
+ mime-types)
+ ((application/json)
+ (render-json
+ '((error . "invalid query"))))
+ (else
+ (apply render-html
+ (compare/derivations
+ query-parameters
+ (valid-systems conn)
+ build-status-strings
+ '()
+ '()))))
(let ((base-commit (assq-ref query-parameters 'base_commit))
(target-commit (assq-ref query-parameters 'target_commit))
@@ -192,27 +198,29 @@
systems
targets
build-statuses)))
- (cond
- ((eq? content-type 'json)
- (render-json
- `((base . ((commit . ,base-commit)
- (derivations . ,(list->vector
- (derivations->alist
- base-derivations)))))
- (target . ((commit . ,target-commit)
- (derivations . ,(list->vector
- (derivations->alist
- target-derivations))))))))
- (else
- (apply render-html
- (compare/derivations
- query-parameters
- (valid-systems conn)
- build-status-strings
- base-derivations
- target-derivations)))))))))
+ (case (most-appropriate-mime-type
+ '(application/json text/html)
+ mime-types)
+ ((application/json)
+ (render-json
+ `((base . ((commit . ,base-commit)
+ (derivations . ,(list->vector
+ (derivations->alist
+ base-derivations)))))
+ (target . ((commit . ,target-commit)
+ (derivations . ,(list->vector
+ (derivations->alist
+ target-derivations))))))))
+ (else
+ (apply render-html
+ (compare/derivations
+ query-parameters
+ (valid-systems conn)
+ build-status-strings
+ base-derivations
+ target-derivations)))))))))
-(define (render-compare/packages content-type
+(define (render-compare/packages mime-types
conn
base-commit
base-revision-id
@@ -233,24 +241,26 @@
(package-differences-data conn
base-revision-id
target-revision-id))))
- (cond
- ((eq? content-type 'json)
- (render-json
- `((base
- . ((commit . ,base-commit)
- (packages . ,(list->vector
- (package-data-vhash->json base-packages-vhash)))))
- (target
- . ((commit . ,target-commit)
- (packages . ,(list->vector
- (package-data-vhash->json target-packages-vhash))))))))
- (else
- (apply render-html
- (compare/packages
- base-commit
- target-commit
- base-packages-vhash
- target-packages-vhash))))))
+ (case (most-appropriate-mime-type
+ '(application/json text/html)
+ mime-types)
+ ((application/json)
+ (render-json
+ `((base
+ . ((commit . ,base-commit)
+ (packages . ,(list->vector
+ (package-data-vhash->json base-packages-vhash)))))
+ (target
+ . ((commit . ,target-commit)
+ (packages . ,(list->vector
+ (package-data-vhash->json target-packages-vhash))))))))
+ (else
+ (apply render-html
+ (compare/packages
+ base-commit
+ target-commit
+ base-packages-vhash
+ target-packages-vhash))))))
(define (render-derivation conn derivation-file-name)
(let ((derivation (select-derivation-by-file-name conn
@@ -302,14 +312,14 @@
(define (parse-build-status s)
s)
-(define (controller request body conn)
+(define (controller request method-and-path-components mime-types body conn)
(define query-parameters
(-> request
request-uri
uri-query
parse-query-string))
- (match-lambda
+ (match method-and-path-components
((GET)
(apply render-html
(index
@@ -392,38 +402,24 @@
#:before-date (assq-ref parsed-query-parameters
'before_date)))))))
((GET "gnu" "store" filename)
- (if (string-suffix? ".drv" filename)
- (render-derivation conn (string-append "/gnu/store/" filename))
- (render-store-item conn (string-append "/gnu/store/" filename))))
+ ;; These routes are a little special, as the extensions aren't used for
+ ;; content negotiation, so just use the path from the request
+ (let ((path (uri-path (request-uri request))))
+ (if (string-suffix? ".drv" path)
+ (render-derivation conn path)
+ (render-store-item conn path))))
((GET "compare")
(with-base-and-target-commits
query-parameters conn
(lambda (base-commit base-revision-id target-commit target-revision-id)
(if (not (and base-revision-id target-revision-id))
- (render-compare-unknown-commit 'html
+ (render-compare-unknown-commit mime-types
conn
base-commit
base-revision-id
target-commit
target-revision-id)
- (render-compare 'html
- conn
- base-commit
- base-revision-id
- target-commit
- target-revision-id)))))
- ((GET "compare.json")
- (with-base-and-target-commits
- query-parameters conn
- (lambda (base-commit base-revision-id target-commit target-revision-id)
- (if (not (and base-revision-id target-revision-id))
- (render-compare-unknown-commit 'json
- conn
- base-commit
- base-revision-id
- target-commit
- target-revision-id)
- (render-compare 'json
+ (render-compare mime-types
conn
base-commit
base-revision-id
@@ -438,19 +434,7 @@
(system ,parse-system #:multi-value)
(target ,parse-system #:multi-value)
(build_status ,parse-build-status #:multi-value)))))
- (render-compare/derivations 'html
- conn
- parsed-query-parameters)))
- ((GET "compare" "derivations.json")
- (let* ((parsed-query-parameters
- (parse-query-parameters
- request
- `((base_commit ,(parse-commit conn) #:required)
- (target_commit ,(parse-commit conn) #:required)
- (system ,parse-system #:multi-value)
- (target ,parse-system #:multi-value)
- (build_status ,parse-build-status #:multi-value)))))
- (render-compare/derivations 'json
+ (render-compare/derivations mime-types
conn
parsed-query-parameters)))
((GET "compare" "packages")
@@ -458,30 +442,13 @@
query-parameters conn
(lambda (base-commit base-revision-id target-commit target-revision-id)
(if (not (and base-revision-id target-revision-id))
- (render-compare-unknown-commit 'html
- conn
- base-commit
- base-revision-id
- target-commit
- target-revision-id)
- (render-compare/packages 'html
- conn
- base-commit
- base-revision-id
- target-commit
- target-revision-id)))))
- ((GET "compare" "packages.json")
- (with-base-and-target-commits
- query-parameters conn
- (lambda (base-commit base-revision-id target-commit target-revision-id)
- (if (not (and base-revision-id target-revision-id))
- (render-compare-unknown-commit 'json
+ (render-compare-unknown-commit mime-types
conn
base-commit
base-revision-id
target-commit
target-revision-id)
- (render-compare/packages 'json
+ (render-compare/packages mime-types
conn
base-commit
base-revision-id
diff --git a/guix-data-service/web/server.scm b/guix-data-service/web/server.scm
index ba27c53..f390da1 100644
--- a/guix-data-service/web/server.scm
+++ b/guix-data-service/web/server.scm
@@ -18,6 +18,7 @@
(define-module (guix-data-service web server)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-11)
#:use-module (web http)
#:use-module (web request)
#:use-module (web uri)
@@ -30,9 +31,14 @@
(define (run-controller controller request body)
(with-postgresql-connection
(lambda (conn)
- ((controller request body conn)
- (cons (request-method request)
- (request-path-components request))))))
+ (let-values (((request-components mime-types)
+ (request->path-components-and-mime-type request)))
+ (controller request
+ (cons (request-method request)
+ request-components)
+ mime-types
+ body
+ conn)))))
(define (handler request body controller)
(format #t "~a ~a\n"
diff --git a/guix-data-service/web/util.scm b/guix-data-service/web/util.scm
index 574b29b..6e1abb7 100644
--- a/guix-data-service/web/util.scm
+++ b/guix-data-service/web/util.scm
@@ -22,15 +22,61 @@
#:use-module (srfi srfi-1)
#:use-module (web request)
#:use-module (web uri)
- #:export (request-path-components
+ #:export (most-appropriate-mime-type
+ request->path-components-and-mime-type
file-extension
directory?
hyphenate-words
underscore-join-words))
-(define (request-path-components request)
- (split-and-decode-uri-path (uri-path (request-uri request))))
+(define (most-appropriate-mime-type accepted-mime-types
+ supported-mime-types)
+ (or
+ ;; Pick the first supported mime-type
+ (find (lambda (accepted-mime-type)
+ (memq accepted-mime-type
+ supported-mime-types))
+ accepted-mime-types)
+ ;; Default to the first supported mime-type if none are accepted
+ (first supported-mime-types)))
+
+(define (request->path-components-and-mime-type request)
+ (define extensions-to-mime-types
+ '(("json" . application/json)
+ ("html" . text/html)))
+
+ (match (split-and-decode-uri-path (uri-path (request-uri request)))
+ (()
+ (values '()
+ (or (request-accept request)
+ (list 'text/html))))
+ ((single-component)
+ (match (string-split single-component #\.)
+ ((part)
+ (values (list single-component)
+ (or (request-accept request)
+ (list 'text/html))))
+ ((first-parts ... extension)
+ (values (string-join first-parts ".")
+ (or (cons
+ (or (assoc-ref extensions-to-mime-types extension)
+ 'text/html)
+ (request-accept request)))))))
+ ((first-components ... last-component)
+ (match (string-split last-component #\.)
+ ((part)
+ (values (append first-components
+ (list part))
+ (or (request-accept request)
+ (list 'text/html))))
+ ((first-parts ... extension)
+ (values (append first-components
+ (list (string-join first-parts ".")))
+ (or (cons
+ (or (assoc-ref extensions-to-mime-types extension)
+ 'text/html)
+ (request-accept request)))))))))
(define (file-extension file-name)
(last (string-split file-name #\.)))