aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Okay <okay@toyful.space>2022-02-08 15:11:02 -0600
committerColin Okay <okay@toyful.space>2022-02-08 15:11:02 -0600
commit904caeeeb292f56067a545a19a326f77e1cd9ec9 (patch)
tree6e66a6995b4795ff2ddba2154228bc4dfc7c923c
parent5b77a6c97198d7993eda76c763cff0fd999d94a7 (diff)
update readme about documentationg enerator; added example
-rw-r--r--README.md25
-rw-r--r--example/lazybones-test-docs.md69
-rw-r--r--example/lazybones-test.lisp121
3 files changed, 212 insertions, 3 deletions
diff --git a/README.md b/README.md
index 4cf2ba0..3476b51 100644
--- a/README.md
+++ b/README.md
@@ -65,9 +65,6 @@ The following is quick example showing a few things that `lazybones` can do.
(lzb:set-canned-response *server* 403 'custom-403 "text/plain" )
(lzb:set-canned-response *server* 500 #p"/path/to/500error.txt" "text/plain" )
-
-
-
;; PPROVISION-APP makes an app. You can supply an optional name, a symbol.
;; In lieu of a supplied name, the name of the package is used as the app's name.
(lzb:provision-app ()
@@ -169,6 +166,28 @@ The following is quick example showing a few things that `lazybones` can do.
```
+## Generate Documentation
+
+Generate a markdown file documenting the endpoints of an APP / API like so:
+
+```
+
+;; continuing from above
+(in-package :lazybones-test)
+
+(alexandria:write-string-into-file
+ (lzb:generate-app-documentation (lzb:app))
+ #p"lazybones-test-docs.md"
+ :if-exists :supersede)
+
+```
+
+Then use your favorite markdown processor to generate HTML or whatever
+your preferred documentation distribution format is. See
+`example/lazybones-test-docs.md` for an example of the documentation
+generator output.
+
+
## Backends
**WARNING** Users can mostly ignore thissection. The API for alternate backends is in
diff --git a/example/lazybones-test-docs.md b/example/lazybones-test-docs.md
new file mode 100644
index 0000000..e7133f1
--- /dev/null
+++ b/example/lazybones-test-docs.md
@@ -0,0 +1,69 @@
+# Lazybones Demo App - v0.0.0
+
+Just an API that defines some endpoints. These
+ endpoints aren't meant to accomplish anything. merely to test out
+ the lazybones HTTP routing framework.
+
+## Endpoints
+
+### POST /hello/:who:
+*text/plain*
+
+Route Variables:
+
+- WHO
+
+Authorization Required:
+
+> Request is authorized if it contains the right TESTAPPSESSION
+> cookie. Obtain such a cookie by posting to the /login endpoint.
+
+Post something to hello who
+
+### GET /hello/:who:
+*text/plain*
+
+Route Variables:
+
+- WHO
+
+Just says hello to WHO
+
+### POST /login
+*text/plain*
+
+Dummy login endpoint for returning a session cookie. Always returns
+ the "true" and sends a set-cookie header, setting 'testappsession'
+ to 'coolsessionbro'.
+
+### GET /person/:person person-by-id:
+*application/json*
+
+Route Variables:
+
+- PERSON: A Person Instance
+
+Returns a json representation of the person.
+
+### POST /search
+*text/plain*
+
+Authorization Required:
+
+> Randomly decides that the request is authorized
+
+Echo the search parameters in a nice list, but also has a post-body
+
+### GET /search
+*text/plain*
+
+Echo the search parameters in a nice list.
+
+### GET /search/:category to-int:
+*text/plain*
+
+Route Variables:
+
+- CATEGORY: An Integer
+
+Echo the search back, but in a specific category \ No newline at end of file
diff --git a/example/lazybones-test.lisp b/example/lazybones-test.lisp
new file mode 100644
index 0000000..5c7a878
--- /dev/null
+++ b/example/lazybones-test.lisp
@@ -0,0 +1,121 @@
+(asdf:load-system "lazybones-hunchentoot")
+
+(defpackage #:lazybones-test
+ (:use #:cl)
+ (:local-nicknames (#:lzb #:lazybones))
+ (:import-from #:lazybones
+ #:defendpoint*
+ #:http-ok
+ #:http-err))
+
+(in-package :lazybones-test)
+
+;; first make a server and add some custom error responses
+
+(defvar *server* (lzb:create-server :port 8888))
+
+(defun custom-404 ()
+ (format nil "~a wasn't found :(" (lzb:request-path))) ; can use request functiosn
+
+(defun custom-403 ()
+ "You, in particular, can't do that. :P ")
+
+(lzb:set-canned-response *server* 404 'custom-404 "text/plain" )
+(lzb:set-canned-response *server* 403 'custom-403 "text/plain" )
+(lzb:set-canned-response *server* 500 #p"/path/to/500error.txt" "text/plain" )
+
+;; PPROVISION-APP makes an app. You can supply an optional name, a symbol.
+;; In lieu of a supplied name, the name of the package is used as the app's name.
+(lzb:provision-app ()
+ :title "Lazybones Demo App"
+ :version "0.0.0"
+ :description "Just an API that defines some endpoints. These
+ endpoints aren't meant to accomplish anything. merely to test out
+ the lazybones HTTP routing framework."
+
+ :content-type "text/plain" ; default content type of server responses.
+ :auth 'post-authorizer) ; default authorizor for requests that need it
+
+(defun post-authorizer ()
+ "Request is authorized if it contains the right TESTAPPSESSION
+ cookie. Obtain such a cookie by posting to the /login endpoint."
+ (string-equal "coolsessionbro" (lzb:request-cookie "testappsession")))
+
+;; now we install the app to the server
+(lzb:install-app *server* (lzb:app)) ; (app) is the default app for this package
+
+;; DEFENDPOINT* is a macro to define an endpoint and install it into the
+;; app whose name is the current package anme. DEFENDPOINT (without the *)
+;; allows you to explictly specify the app where the endpoint is installed.
+
+(defendpoint* :post "/login" ()
+ "Dummy login endpoint for returning a session cookie. Always returns
+ the \"true\" and sends a set-cookie header, setting 'testappsession'
+ to 'coolsessionbro'."
+ (setf (lzb:response-cookie "testappsession") "coolsessionbro")
+ (http-ok "true"))
+
+(defendpoint* :get "/hello/:who:" ()
+ "Just says hello to WHO"
+ (http-ok (format nil "Hello ~a" who)))
+
+(defendpoint* :post "/hello/:who:" (:auth t) ; use the default authorizor for the app
+ "Post something to hello who"
+ (print (lzb:request-header :content-type))
+ (let ((body (lzb:request-body)))
+ (http-ok (format nil "Hello ~a, I got your message ~a"
+ who body))))
+
+(defendpoint* :get "/search" ()
+ "Echo the search parameters in a nice list."
+ (http-ok (format nil "Query Was:~%~{~a is ~a~%~}~%"
+ (loop for (x . y) in (lzb:request-parameters)
+ collect x
+ collect y))))
+
+(defun crapshoot-authorizer ()
+ "Randomly decides that the request is authorized"
+ (< 5 (random 10)))
+
+(defendpoint* :post "/search" (:auth 'crapshoot-authorizer) ; use custom authorizer
+ "Echo the search parameters in a nice list, but also has a post-body"
+ (http-ok
+ (with-output-to-string (out)
+ (format out "Query Was:~%~{~a is ~a~%~}~%"
+ (loop for (x . y) in (lzb:request-parameters)
+ collect x
+ collect y))
+ (terpri)
+ (format out "Decoded Post Body: ~s~%" (lzb:request-body)))))
+
+
+(defun to-int (string)
+ "An Integer"
+ (parse-integer string))
+
+;; route variables can accept parsers / preformatters
+;; these will parse a value and supply it to the argument of the handler.
+;; int eh following CATEGORY is an int
+
+(defendpoint* :get "/search/:category to-int:" ()
+ "Echo the search back, but in a specific category"
+ (assert (typep category 'integer)) ; just to show you.
+ (http-ok
+ (format nil "Searching in ~a with parameters:~%~{~a = ~a~%~}~%"
+ category
+ (loop for (x . y) in (lzb:request-parameters)
+ collect x
+ collect y))))
+
+(defun person-by-id (id)
+ "A Person Instance"
+ ;; The real thing might perform some database operation here. If the
+ ;; operation failed, an error could be signalled, in which case a
+ ;; 500 response would be sent to the client.
+ (list :name "Colin" :occupation "Macrologist" :id (parse-integer id)))
+
+(defendpoint* :get "/person/:person person-by-id:" (:content-type "application/json")
+ "Returns a json representation of the person."
+ (http-ok
+ (jonathan:to-json person)))
+