changeset 4477:8df6cc648963

mod_rest: Add more REST-looking way to send stanzas Example: POST /rest/message/chat/juliet@example.net { body: "Hello" } Becomes equivalent to POST /rest { kind: "message", type: "chat", to: "juliet@example.net", body: "Hello" } Sending messages as plain/text also becomes more convenient. IQ stazas are still weird, but we'll do something special for those.
author Kim Alvefur <zash@zash.se>
date Sun, 28 Feb 2021 19:25:45 +0100
parents 125279f4a5b8
children 7ab0c423688a
files mod_rest/README.markdown mod_rest/mod_rest.lua mod_rest/openapi.yaml
diffstat 3 files changed, 68 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/mod_rest/README.markdown	Sun Feb 28 18:55:18 2021 +0100
+++ b/mod_rest/README.markdown	Sun Feb 28 19:25:45 2021 +0100
@@ -70,6 +70,18 @@
 
 The `Content-Type` header is important!
 
+### Parameters in path
+
+New alternative format with the parameters `kind`, `type`, and `to`
+embedded in the path:
+
+```
+curl https://prosody.example:5281/rest/message/chat/john@example.com \
+    --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \
+    -H 'Content-Type: text/plain' \
+    --data-binary 'Hello John!'
+```
+
 ### Replies
 
 A POST containing an `<iq>` stanza automatically wait for the reply,
--- a/mod_rest/mod_rest.lua	Sun Feb 28 18:55:18 2021 +0100
+++ b/mod_rest/mod_rest.lua	Sun Feb 28 19:25:45 2021 +0100
@@ -59,7 +59,19 @@
 	return nil;
 end
 
-local function parse(mimetype, data)
+-- (table, string) -> table
+local function amend_from_path(data, path)
+	local st_kind, st_type, st_to = path:match("^([mpi]%w+)/(%w+)/(.*)$");
+	if not st_kind then return; end
+	data.kind = st_kind;
+	data.type = st_type;
+	if st_to and st_to ~= "" then
+		data.to = st_to;
+	end
+	return data;
+end
+
+local function parse(mimetype, data, path) --> Stanza, error enum
 	mimetype = mimetype and mimetype:match("^[^; ]*");
 	if mimetype == "application/xmpp+xml" then
 		return xml.parse(data);
@@ -68,6 +80,7 @@
 		if not parsed then
 			return parsed, err;
 		end
+		if path and not amend_from_path(parsed, path) then return nil, "invalid-path"; end
 		return jsonmap.json2st(parsed);
 	elseif mimetype == "application/cbor" and have_cbor then
 		local parsed, err = cbor.decode(data);
@@ -83,9 +96,22 @@
 		for i = #parsed, 1, -1 do
 			parsed[i] = nil;
 		end
+		if path and not amend_from_path(parsed, path) then return nil, "invalid-path"; end
 		return jsonmap.json2st(parsed);
 	elseif mimetype == "text/plain" then
-		return st.message({ type = "chat" }, data);
+		if not path then
+			return st.message({ type = "chat" }, data);
+		end
+		local parsed = {};
+		if not amend_from_path(parsed, path) then return nil, "invalid-path"; end
+		if parsed.kind == "message" then
+			parsed.body = data;
+		elseif parsed.kind == "presence" then
+			parsed.show = data;
+		else
+			return nil, "invalid-path";
+		end
+		return jsonmap.json2st(parsed);
 	end
 	return nil, "unknown-payload-type";
 end
@@ -162,7 +188,7 @@
 	mediatype = { code = 415, condition = "bad-format", text = "Unsupported media type" },
 });
 
-local function handle_post(event)
+local function handle_post(event, path)
 	local request, response = event.request, event.response;
 	local from;
 	local origin;
@@ -177,7 +203,7 @@
 		end
 		from = jid.join(origin.username, origin.host, origin.resource);
 	end
-	local payload, err = parse(request.headers.content_type, request.body);
+	local payload, err = parse(request.headers.content_type, request.body, path);
 	if not payload then
 		-- parse fail
 		local ctx = { error = err, type = request.headers.content_type, data = request.body, };
@@ -267,6 +293,7 @@
 module:provides("http", {
 		route = {
 			POST = handle_post;
+			["POST /*"] = handle_post;
 		};
 	});
 
--- a/mod_rest/openapi.yaml	Sun Feb 28 18:55:18 2021 +0100
+++ b/mod_rest/openapi.yaml	Sun Feb 28 19:25:45 2021 +0100
@@ -62,6 +62,31 @@
             payload.
         415:
           description: Unsupported mediatype.
+  /rest/{kind}/{type}/{to}:
+    post:
+      responses:
+        200:
+          description: Okay
+      security:
+      - basic: []
+      - token: []
+      summary: Even more RESTful mapping.
+      parameters:
+      - name: kind
+        in: path
+        required: true
+        schema:
+          $ref: '#/components/schemas/kind'
+      - name: type
+        in: path
+        required: true
+        schema:
+          $ref: '#/components/schemas/type'
+      - name: to
+        in: path
+        required: true
+        schema:
+          $ref: '#/components/schemas/to'
 components:
   schemas:
     stanza: