From bf1667cf21edc6f08deba9ccc12b1606e6a20a36 Mon Sep 17 00:00:00 2001 From: Johnny Ruiz Date: Sun, 20 Aug 2023 10:16:53 -0400 Subject: [PATCH] Fix #7 Expose path-parameter function to get path parameter from URI --- README.org | 2 +- src/middleware/path-template.lisp | 24 ++++++++++++++++++---- t/tiny-routes-test.lisp | 34 +++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/README.org b/README.org index dc6bcce..b6cd071 100644 --- a/README.org +++ b/README.org @@ -83,7 +83,7 @@ (define-get "/" () ; (2) (ok "alive")) ; (3) (define-get "/accounts/:account-id" (request) ; (4) - (let ((account-id (path-param request :account-id))) ; (5) + (let ((account-id (path-parameter request :account-id))) ; (5) (ok (format nil "Your account id: ~a." account-id)))) ; (6) (define-any "*" () ; (7) (not-found "not-found"))) ; (8) diff --git a/src/middleware/path-template.lisp b/src/middleware/path-template.lisp index 214c49e..0d1bc07 100644 --- a/src/middleware/path-template.lisp +++ b/src/middleware/path-template.lisp @@ -10,10 +10,13 @@ #:scan-to-strings) (:import-from :tiny-routes.request #:request-append + #:request-get #:path-info) (:import-from :tiny-routes.util #:compose) - (:export #:wrap-request-path-info-matcher + (:export #:path-parameter + #:with-path-parameters + #:wrap-request-path-info-matcher #:wrap-request-matches-path-template)) (in-package :tiny-routes.middleware.path-template) @@ -87,15 +90,16 @@ then it is made available to the request under `:path-parameters'." "Wrap HANDLER such that it is called only if the request path matches the PATH-TEMPLATE. -If PATH-TEMPLATE is t, nil, or an empty string, then return HANDLER -unchanged. +If PATH-TEMPLATE is t, nil, the empty string, or \"*\", then return +HANDLER unchanged. If REGEX is non-nil, then interpret path-template as a regular expression." (check-type path-template (or symbol string)) (cond ((or (null path-template) (eq path-template t) - (string= path-template "")) + (string= path-template "") + (string= path-template "*")) handler) ;; If regex is non-nil, then interpret path-info as a regex (regex @@ -105,3 +109,15 @@ expression." (wrap-request-path-info-matcher handler (make-path-template-keyword-matcher path-template))) (t (wrap-request-path-info-matcher handler (make-path-template-exact-matcher path-template))))) + +(defun path-parameter (request path-parameter &optional default) + "Return the value mapped to PATH-PARAMETER from REQUEST or DEFAULT." + (getf (getf request :path-parameters) path-parameter default)) + +(defmacro with-path-parameters (vars path-parameters &body body) + "Bind the variables in VARS to the corresponding values present in +PATH-PARAMETERS." + (let ((gpath-parameters (gensym "path-parameters"))) + `(let* ((,gpath-parameters ,path-parameters)) + (destructuring-bind (&key ,@vars &allow-other-keys) ,gpath-parameters + ,@body)))) diff --git a/t/tiny-routes-test.lisp b/t/tiny-routes-test.lisp index 86dc4c1..f2d80a8 100644 --- a/t/tiny-routes-test.lisp +++ b/t/tiny-routes-test.lisp @@ -120,3 +120,37 @@ (is (null (funcall (define-post "/bar" () response) request))) (is (equalp response (funcall (define-post "/foo" () response) request))) (is (equalp response (funcall (define-post "/:foo" () response) request))))) + +(test route-matching1 + (let ((request (mock-request :get "/accounts/A123/users/U42")) + (ok-response (make-response :status 200 :body "OK")) + (expected (list "A123" "U42"))) + (is (equalp ok-response (funcall (define-any "*" () ok-response) request))) + (is (equalp expected + (funcall (define-get "/accounts/:account-id/users/:user-id" (request) + (let ((account-id (path-parameter request :account-id)) + (user-id (path-parameter request :user-id))) + (list account-id user-id))) + request))) + (is (equalp expected + (funcall (define-get "/accounts/:account-id/users/:user-id" (request) + (with-request (path-parameters) request + (with-path-parameters (account-id user-id) path-parameters + (list account-id user-id)))) + request))))) + +(test readme-example + (let ((app (routes + (define-get "/" () + (ok "alive")) + (define-get "/accounts/:account-id" (request) + (let ((account-id (path-parameter request :account-id))) + (ok (format nil "Your account id: ~a." account-id)))) + (define-any "*" () + (not-found "not-found"))))) + (is (equalp '(200 NIL ("alive")) + (funcall app (mock-request :get "/")))) + (is (equalp '(200 NIL ("Your account id: A123.")) + (funcall app (mock-request :get "/accounts/A123")))) + (is (equalp '(404 NIL ("not-found")) + (funcall app (mock-request :get "/unknown"))))))