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.
--- 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: