aboutsummaryrefslogtreecommitdiff
path: root/example/lazybones-test.lisp
blob: 3c040650856fb324cca9e0b4a71fd2b710ff55cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
(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 :o~%" (lzb:request-path))) ; can use request functiosn

(defun custom-403 ()
  "You, in particular, can't do that. :P ")

(defun custom-500 ()
  "Bah. Error.")

(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 'custom-500 "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 testing 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'."
  (print (lzb:request-body)) ; dummy implementation, prints post body to stdout
  (setf (lzb:response-cookie "testappsession") "coolsessionbro")
  (http-ok "true"))

(defendpoint* :get "/hello/:who:" () ()
  "Echos back Hello WHO"
  (http-ok (format nil "Hello ~a~%" who)))

(defendpoint* :post "/hello/:who:" ()  (:auth t)  ; use the default authorizor for the app
  "Echo's back 'Hello WHO, I got your message BODY' where BODY is the post body."
  (print (lzb:request-header :content-type))
  (let ((body (lzb:request-body)))
    (http-ok (format nil "Hello ~a, I got your message ~a~%"
                     who body))))

;; Some helpers, these are used to parse url variables and query
;; parameters. Their docstrings are used in the API documentation

(defun int (string)
  "An Integer"
  (parse-integer string))

(defun str (string)
  "A String"
  string)

(defendpoint* :get "/search" ((name str) (age int)) ()
  "Echo the search parameters in a nice list."
  (http-ok (format nil "Name: ~a~%age: ~a~%" name age)))

(defun crapshoot-authorizer ()
  "Randomly decides that the request is authorized" 
  (< 5 (random 10)))

(defendpoint* :post "/crapshoot" () (:auth 'crapshoot-authorizer)  ; use custom authorizer
  "Echos back 'You made it' if the request was authorized"
  (http-ok "You made it"))

;; Route variables can accept parsers / preformatters
;; these will parse a value and supply it to the argument of the handler.

(defendpoint* :get "/random/:lo int:/:hi int:" () ()
  "Echo back a random number between lo and hi"
  (if (< lo hi) 
      (http-ok 
       (format nil "The number is: ~a~%"
               (+ lo (random (- hi lo)))))
      (http-err 404))) ; Can't find a number X such that LO >= HI and LO < X < HI

(defun person-by-id (id)
  "ID of a person"
  ;; 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)))