]> gitweb.factorcode.org Git - factor.git/commitdiff
furnace: Implement DELETE request
authorMichael Raitza <spacefrogg-git@spacefrogg.net>
Tue, 16 Jan 2024 17:16:25 +0000 (18:16 +0100)
committerJohn Benediktsson <mrjbq7@gmail.com>
Tue, 16 Jan 2024 17:23:28 +0000 (09:23 -0800)
basis/furnace/actions/actions-docs.factor
basis/furnace/actions/actions.factor
basis/furnace/redirection/redirection.factor

index f97270ffe8d404abe14ee0f0e0c28b6572926a43..361232c938bc1fd46cd2a52ceac7080a9964e041 100644 (file)
@@ -93,13 +93,14 @@ ARTICLE: "furnace.actions.config" "Furnace action configuration"
 "Actions have the following slots:"
 { $slots
     { "rest" { "A parameter name to map the rest of the URL, after the action name, to. If this is not set, then navigating to a URL where the action is not the last path component will return to the client with an error. A more general facility can be found in the " { $vocab-link "http.server.rewrite" } " vocabulary." } }
-    { "init" { "A quotation called at the beginning of a GET or HEAD request. Typically this quotation configures " { $link "html.forms" } " and parses query parameters." } }
-    { "authorize" { "A quotation called at the beginning of a GET, HEAD or POST request. In GET requests, it is called after the " { $slot "init" } " quotation; in POST requests, it is called after the " { $slot "validate" } " quotation. By convention, this quotation performs custom authorization checks which depend on query parameters or POST parameters." } }
+    { "init" { "A quotation called at the beginning of a GET, HEAD or DELETE request. Typically this quotation configures " { $link "html.forms" } " and parses query parameters." } }
+    { "authorize" { "A quotation called at the beginning of all implemented requests. In GET, HEAD and DELETE requests, it is called after the " { $slot "init" } " quotation; in PATCH, POST and PUT requests, it is called after the " { $slot "validate" } " quotation. By convention, this quotation performs custom authorization checks which depend on query parameters or POST parameters." } }
     { "display" { "A quotation called after the " { $slot "init" } " quotation in a GET request. This quotation must return an HTTP " { $link response } "." } }
     { "validate" { "A quotation called at the beginning of a POST request to validate POST parameters." } }
     { "submit" { "A quotation called after the " { $slot "validate" } " quotation in a POST request. This quotation must return an HTTP " { $link response } "." } }
     { "replace" { "A quotation called after the " { $slot "validate" } " quotation in a PUT request. This quotation must return an HTTP " { $link response } "." } }
     { "update" { "A quotation called after the " { $slot "validate" } " quotation in a PATCH request. This quotation must return an HTTP " { $link response } "." } }
+    { "delete" { "A quotation called after the " { $slot "init" } " quotation in a DELETE request. This quotation must return an HTTP " { $link response } "." } }
 }
 "At least one of the " { $slot "display" } " and " { $slot "submit" } " slots must be set, otherwise the action will be useless." ;
 
@@ -125,6 +126,22 @@ ARTICLE: "furnace.actions.lifecycle" "Furnace action lifecycle"
 "Any one of the above steps can perform validation; if " { $link validation-failed } " is called during a GET request, the client receives a " { $link <400> } " error."
 { $heading "HEAD request lifecycle" }
 "A HEAD request proceeds exactly like a GET request. The only difference is that the " { $slot "body" } " slot of the " { $link response } " object is never rendered."
+
+{ $heading "DELETE request lifecycle" }
+"A DELETE request is supposed to act on a whole resources, i.e. a URL sans query parameters. "
+"Nevertheless, " { $vocab-link "http.server" } " accepts query parameters that may be used for authorization. "
+"A DELETE request results in the following sequence of events:"
+{ $list
+    { "The " { $snippet "init" } " quotation is called." }
+    { "The " { $snippet "authorize" } " quotation is called." }
+    { "The " { $snippet "delete" } " quotation is called; it is expected to output an HTTP " { $link response } " on the stack. "
+      "By convention, this response should be either a " { $link <redirect> } " (which generates a " { $link <303> } " response) or, "
+      "when successful, one of " { $link <200> } ", " { $link <202> } " or " { $link <204> } " responses."
+    }
+}
+"Any one of the above steps can perform validation; if " { $link validation-failed } " is called during a DELETE request, "
+"the client is sent back to the originating page with validation errors passed in a " { $link "furnace.conversations" } "."
+
 { $heading "POST request lifecycle" }
 "A POST request results in the following sequence of events:"
 { $list
index e60138259fe3b704a36940043d2195f1c53332f6..9e639fe7aef24c3c8a83e69436360954a9b1fbb6 100644 (file)
@@ -1,15 +1,14 @@
 ! Copyright (C) 2008, 2009 Slava Pestov.
 ! See https://factorcode.org/license.txt for BSD license.
-USING: accessors assocs combinators furnace.conversations
-furnace.utilities html.forms html.templates.chloe http
-http.server http.server.responses kernel namespaces sequences
+USING: accessors assocs combinators furnace.conversations furnace.utilities
+html.forms http http.server http.server.responses kernel namespaces sequences
 splitting urls validators ;
 FROM: html.templates.chloe => <chloe> ;
 IN: furnace.actions
 
 SYMBOL: rest
 
-TUPLE: action rest init authorize display validate submit update replace ;
+TUPLE: action rest init authorize display validate submit update replace delete ;
 
 : new-action ( class -- action )
     new [ ] >>init [ ] >>validate [ ] >>authorize ; inline
@@ -55,7 +54,8 @@ CONSTANT: revalidate-url-key "__u"
     dup [ >url ensure-port [ same-host? ] keep and ] when ;
 
 : validation-failed ( -- * )
-    post-request? revalidate-url and [
+    post-request? "DELETE" method= or
+    revalidate-url and [
         begin-conversation
         nested-forms-key param split-words harvest nested-forms cset
         form get form cset
@@ -93,6 +93,16 @@ CONSTANT: revalidate-url-key "__u"
         ] [ drop <400> ] if
     ] with-exit-continuation ;
 
+: handle-delete ( action -- response )
+    '[
+        _ dup delete>> [
+            [ init>> call( -- ) ]
+            [ authorize>> call( -- ) ]
+            [ delete>> call( -- response ) ]
+            tri
+        ] [ drop <400> ] if
+    ] with-exit-continuation ;
+
 : handle-rest ( path action -- )
     rest>> [ [ "/" join ] dip set-param ] [ drop ] if* ;
 
@@ -108,6 +118,7 @@ M: action call-responder*
         { "POST"  [ handle-post ] }
         { "PUT"   [ handle-put ] }
         { "PATCH" [ handle-patch ] }
+        { "DELETE" [ handle-delete ] }
         [ 2drop <405> ]
     } case ;
 
index a27ea31f693bb2f94f0f4a30096898363bd083bb..cfdb0c089782571b83b82874111eca4850b5ab0c 100644 (file)
@@ -11,6 +11,8 @@ IN: furnace.redirection
         { "GET" [ <temporary-redirect> ] }
         { "HEAD" [ <temporary-redirect> ] }
         { "POST" [ <permanent-redirect> ] }
+        { "DELETE" [ <303> <custom-redirect> ] }
+        [ 2drop <405> ]
     } case ;
 
 : >secure-url ( url -- url' )