|
@@ -9,189 +9,18 @@ using ClankerSaddle.Services;
|
|
|
namespace ClankerSaddle.Api {
|
|
namespace ClankerSaddle.Api {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * `POST /api/saddle/{id}/update` — the ingest channel for a saddle
|
|
|
|
|
- * daemon, authorised by its `Authorization: Bearer <token>` header
|
|
|
|
|
- * (see {@link SaddleService.validate_token}). The body carries a type
|
|
|
|
|
- * discriminator, the session id and a type-specific `data` object:
|
|
|
|
|
- *
|
|
|
|
|
- * ```json
|
|
|
|
|
- * {"type": "output", "session_id": 12, "data": {
|
|
|
|
|
- * "command": "ls -la", "output": "…", "exit_code": 0, "type": "command",
|
|
|
|
|
- * "started_at": "…", "completed_at": "…", "duration_ms": 41}}
|
|
|
|
|
- * ```
|
|
|
|
|
- *
|
|
|
|
|
- * Handled types: `output` (a single entry or an `entries` array; both
|
|
|
|
|
- * `data` shapes store `session_commands` rows), `comment`, `block`
|
|
|
|
|
- * (question/rfc/review with optional `options` and markdown `body`;
|
|
|
|
|
- * returns the new `block_id`), `progress`, `review`, `done`
|
|
|
|
|
- * (completes the task), `failed` (fails the task with the
|
|
|
|
|
- * explanation) and `status` (working|blocked). Conversation-level
|
|
|
|
|
- * types also append a typed entry to the session's terminal log.
|
|
|
|
|
- * The plan-12 deletions: `subtask` and `plan` are GONE (the goal
|
|
|
|
|
- * agent's task fan-out lives on the task callbacks and its plan on
|
|
|
|
|
- * the goal) — a legacy daemon posting them gets a 400 naming the
|
|
|
|
|
- * removed verb.
|
|
|
|
|
|
|
+ * Static helpers of the `/api/saddle/*` wire (formerly the late
|
|
|
|
|
+ * SaddleUpdateEndpoint's internals — the update/poll pair died with
|
|
|
|
|
+ * the poll era, plan 16 R3 / arch 19): JSON body reading and the
|
|
|
|
|
+ * ok/error response envelopes every saddle endpoint shares.
|
|
|
*/
|
|
*/
|
|
|
- public class SaddleUpdateEndpoint : Object, Endpoint {
|
|
|
|
|
-
|
|
|
|
|
- protected TaskService tasks = inject<TaskService>();
|
|
|
|
|
- protected SaddleService saddles = inject<SaddleService>();
|
|
|
|
|
- protected InvercargillSql.Orm.OrmSession db = inject<InvercargillSql.Orm.OrmSession>();
|
|
|
|
|
-
|
|
|
|
|
- public virtual async HttpResult handle_request(HttpContext http_context,
|
|
|
|
|
- RouteContext route_context) throws Error {
|
|
|
|
|
- string id_text = "";
|
|
|
|
|
- route_context.mapped_parameters.try_get("id", out id_text);
|
|
|
|
|
- int64 saddle_id = int64.parse(id_text);
|
|
|
|
|
-
|
|
|
|
|
- if ((yield resolve_bearer(http_context, saddles, saddle_id)) == null) {
|
|
|
|
|
- return json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Json.Object body;
|
|
|
|
|
- try {
|
|
|
|
|
- body = yield read_json_body(http_context);
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- return json_error(400, e.message);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- string type = body.has_member("type") ? body.get_string_member_with_default("type", "") : "";
|
|
|
|
|
- int64 session_id = body.has_member("session_id") ? body.get_int_member("session_id") : 0;
|
|
|
|
|
- var data = body.has_member("data") && body.get_member("data").get_node_type() == Json.NodeType.OBJECT
|
|
|
|
|
- ? body.get_object_member("data") : new Json.Object();
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- var response = yield handle_update(saddle_id, session_id, type, data);
|
|
|
|
|
- return json_ok(response);
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- return json_error(400, e.message);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private async Json.Object handle_update(int64 saddle_id, int64 session_id,
|
|
|
|
|
- string type, Json.Object data) throws Error {
|
|
|
|
|
- var task_id = yield task_id_for_session(session_id);
|
|
|
|
|
-
|
|
|
|
|
- switch (type) {
|
|
|
|
|
- case "output":
|
|
|
|
|
- if (data.has_member("entries") && data.get_member("entries").get_node_type() == Json.NodeType.ARRAY) {
|
|
|
|
|
- foreach (var element in data.get_array_member("entries").get_elements()) {
|
|
|
|
|
- if (element.get_node_type() == Json.NodeType.OBJECT) {
|
|
|
|
|
- yield store_output(session_id, element.get_object());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- yield store_output(session_id, data);
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case "comment":
|
|
|
|
|
- string body_text = EntryJson.get_string_or(data, "body", EntryJson.get_string_or(data, "text", ""));
|
|
|
|
|
- yield tasks.add_comment(task_id, "agent", body_text);
|
|
|
|
|
- yield tasks.log_session_entry(task_id, "comment", "saddle comment", body_text);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case "block":
|
|
|
|
|
- var block = yield tasks.add_block(task_id,
|
|
|
|
|
- EntryJson.get_string_or(data, "question", ""),
|
|
|
|
|
- EntryJson.get_string_or(data, "type", "question"),
|
|
|
|
|
- EntryJson.string_array_member(data, "options", "block options"),
|
|
|
|
|
- EntryJson.get_string_or(data, "body", null));
|
|
|
|
|
- yield tasks.log_session_entry(task_id, "block",
|
|
|
|
|
- @"block: $(block.question)", block.body);
|
|
|
|
|
- var block_response = new Json.Object();
|
|
|
|
|
- block_response.set_int_member("block_id", block.id);
|
|
|
|
|
- return block_response;
|
|
|
|
|
-
|
|
|
|
|
- case "subtask":
|
|
|
|
|
- case "plan":
|
|
|
|
|
- // Plan 12: the subtask and task-plan machinery is
|
|
|
|
|
- // deleted — the daemon's goal agent fans tasks out
|
|
|
|
|
- // through the /api/saddle/task/* callbacks and
|
|
|
|
|
- // plans ride the goal. Surface the removal so a
|
|
|
|
|
- // legacy daemon fails loudly instead of silently.
|
|
|
|
|
- throw new Error(Quark.from_string("clanker-saddle"), 90,
|
|
|
|
|
- @"The '$(type)' update type is gone (plan 12) — use the task callbacks / the goal's plan flow");
|
|
|
|
|
-
|
|
|
|
|
- case "progress":
|
|
|
|
|
- string summary = EntryJson.get_string_or(data, "summary",
|
|
|
|
|
- EntryJson.get_string_or(data, "text", ""));
|
|
|
|
|
- yield tasks.add_progress(task_id, summary);
|
|
|
|
|
- yield tasks.log_session_entry(task_id, "progress", "saddle progress-update", summary);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case "review":
|
|
|
|
|
- string review_summary = EntryJson.get_string_or(data, "summary", "");
|
|
|
|
|
- var review_block = yield tasks.request_review(task_id, review_summary);
|
|
|
|
|
- yield tasks.log_session_entry(task_id, "review", "saddle review", review_summary);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case "done":
|
|
|
|
|
- yield tasks.complete_task(task_id,
|
|
|
|
|
- EntryJson.get_string_or(data, "summary", null),
|
|
|
|
|
- EntryJson.get_string_or(data, "working_branch", null), OUTCOME_DONE);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case "failed":
|
|
|
|
|
- string failure = EntryJson.get_string_or(data, "summary",
|
|
|
|
|
- EntryJson.get_string_or(data, "text", ""));
|
|
|
|
|
- yield tasks.fail_task(task_id, failure.length > 0 ? failure : null);
|
|
|
|
|
- yield tasks.log_session_entry(task_id, "failed", "saddle failure", failure);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case "status":
|
|
|
|
|
- string state = EntryJson.get_string_or(data, "state", "");
|
|
|
|
|
- if (state == "working" || state == "blocked") {
|
|
|
|
|
- yield tasks.set_status(task_id, state);
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- default:
|
|
|
|
|
- throw new Error(Quark.from_string("clanker-saddle"), 20,
|
|
|
|
|
- @"Unknown update type '$(type)'");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return new Json.Object();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private async void store_output(int64 session_id, Json.Object entry) throws Error {
|
|
|
|
|
- var command = new SessionCommand() {
|
|
|
|
|
- session_id = session_id,
|
|
|
|
|
- command_text = EntryJson.get_string_or(entry, "command", ""),
|
|
|
|
|
- output_text = EntryJson.get_string_or(entry, "output", null),
|
|
|
|
|
- exit_code = entry.has_member("exit_code")
|
|
|
|
|
- && entry.get_member("exit_code").get_value_type() == typeof(int64)
|
|
|
|
|
- ? entry.get_int_member("exit_code") : 0,
|
|
|
|
|
- entry_type = EntryJson.get_string_or(entry, "type", "command"),
|
|
|
|
|
- piped_input = EntryJson.get_string_or(entry, "input", null) != null
|
|
|
|
|
- && EntryJson.get_string_or(entry, "input", "").length > 0
|
|
|
|
|
- ? EntryJson.get_string_or(entry, "input", null) : null,
|
|
|
|
|
- started_at = EntryJson.parse_iso(entry, "started_at"),
|
|
|
|
|
- completed_at = EntryJson.parse_iso(entry, "completed_at"),
|
|
|
|
|
- duration_ms = entry.has_member("duration_ms")
|
|
|
|
|
- && entry.get_member("duration_ms").get_value_type() == typeof(int64)
|
|
|
|
|
- ? entry.get_int_member("duration_ms") : 0
|
|
|
|
|
- };
|
|
|
|
|
- yield db.insert_async<SessionCommand>(command);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private async int64 task_id_for_session(int64 session_id) throws Error {
|
|
|
|
|
- var session = yield db.query<ClankerSaddle.Data.Session>()
|
|
|
|
|
- .where(Invercargill.Expressions.expr("id == $0",
|
|
|
|
|
- Invercargill.Expressions.elem<int64?>(session_id)))
|
|
|
|
|
- .first_async();
|
|
|
|
|
- if (session == null) {
|
|
|
|
|
- throw new Error(Quark.from_string("clanker-saddle"), 21,
|
|
|
|
|
- @"No session exists with id $(session_id)");
|
|
|
|
|
- }
|
|
|
|
|
- return ((!)session).task_id;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ internal class SaddleWire : Object {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Reads and parses the request body as a JSON object; an empty body
|
|
* Reads and parses the request body as a JSON object; an empty body
|
|
|
* parses as an empty object.
|
|
* parses as an empty object.
|
|
|
*/
|
|
*/
|
|
|
- internal static async Json.Object read_json_body(HttpContext http_context) throws Error {
|
|
|
|
|
|
|
+ public static async Json.Object read_json_body(HttpContext http_context) throws Error {
|
|
|
var bytes = yield http_context.request.request_body.read_all();
|
|
var bytes = yield http_context.request.request_body.read_all();
|
|
|
// to_raw_string on an empty body is not safe; guard the count.
|
|
// to_raw_string on an empty body is not safe; guard the count.
|
|
|
string text = "";
|
|
string text = "";
|
|
@@ -226,7 +55,7 @@ namespace ClankerSaddle.Api {
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- internal static HttpResult json_error(uint16 status_code, string message) {
|
|
|
|
|
|
|
+ public static HttpResult json_error(uint16 status_code, string message) {
|
|
|
var body = new Json.Object();
|
|
var body = new Json.Object();
|
|
|
body.set_boolean_member("ok", false);
|
|
body.set_boolean_member("ok", false);
|
|
|
body.set_string_member("error", message);
|
|
body.set_string_member("error", message);
|
|
@@ -237,71 +66,6 @@ namespace ClankerSaddle.Api {
|
|
|
result.set_header("Content-Type", "application/json");
|
|
result.set_header("Content-Type", "application/json");
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * `GET /api/saddle/{id}/poll` — the outbound channel for a saddle
|
|
|
|
|
- * daemon, authorised by its `Authorization: Bearer <token>` header:
|
|
|
|
|
- * answered blocks and undelivered human comments for the saddle's
|
|
|
|
|
- * current task, each handed over exactly once.
|
|
|
|
|
- *
|
|
|
|
|
- * ```json
|
|
|
|
|
- * {"answers": [{"block_id": 4, "answer": "Use the flange tool"}],
|
|
|
|
|
- * "comments": [{"comment_id": 9, "author": "user:admin",
|
|
|
|
|
- * "body": "…", "quote": "…"}]}
|
|
|
|
|
- * ```
|
|
|
|
|
- */
|
|
|
|
|
- public class SaddlePollEndpoint : Object, Endpoint {
|
|
|
|
|
-
|
|
|
|
|
- private SaddleService saddles = inject<SaddleService>();
|
|
|
|
|
- private TaskService tasks = inject<TaskService>();
|
|
|
|
|
-
|
|
|
|
|
- public virtual async HttpResult handle_request(HttpContext http_context,
|
|
|
|
|
- RouteContext route_context) throws Error {
|
|
|
|
|
- string id_text = "";
|
|
|
|
|
- route_context.mapped_parameters.try_get("id", out id_text);
|
|
|
|
|
- int64 saddle_id = int64.parse(id_text);
|
|
|
|
|
-
|
|
|
|
|
- if ((yield resolve_bearer(http_context, saddles, saddle_id)) == null) {
|
|
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var answers = new Json.Array();
|
|
|
|
|
- var comments = new Json.Array();
|
|
|
|
|
- try {
|
|
|
|
|
- bool advanced = false;
|
|
|
|
|
- bool advanced_known = false;
|
|
|
|
|
- foreach (var pending in yield saddles.take_pending_answers(saddle_id)) {
|
|
|
|
|
- if (!advanced_known) {
|
|
|
|
|
- advanced_known = true;
|
|
|
|
|
- var saddle_row = yield saddles.get_saddle(saddle_id);
|
|
|
|
|
- advanced = saddle_row != null
|
|
|
|
|
- && yield tasks.branch_advanced_since(((!)saddle_row).current_task_id);
|
|
|
|
|
- }
|
|
|
|
|
- var element = new Json.Object();
|
|
|
|
|
- element.set_int_member("block_id", pending.block_id);
|
|
|
|
|
- element.set_string_member("answer", pending.answer);
|
|
|
|
|
- element.set_boolean_member("branch_advanced", advanced);
|
|
|
|
|
- answers.add_object_element(element);
|
|
|
|
|
- }
|
|
|
|
|
- foreach (var pending in yield saddles.take_pending_comments(saddle_id)) {
|
|
|
|
|
- var element = new Json.Object();
|
|
|
|
|
- element.set_int_member("comment_id", pending.comment_id);
|
|
|
|
|
- element.set_string_member("author", pending.author);
|
|
|
|
|
- element.set_string_member("body", pending.body);
|
|
|
|
|
- element.set_string_member("quote", pending.quote);
|
|
|
|
|
- comments.add_object_element(element);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var body = new Json.Object();
|
|
|
|
|
- body.set_array_member("answers", answers);
|
|
|
|
|
- body.set_array_member("comments", comments);
|
|
|
|
|
- return SaddleUpdateEndpoint.json_ok(body);
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -321,7 +85,7 @@ namespace ClankerSaddle.Api {
|
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
string project_text = "";
|
|
string project_text = "";
|
|
@@ -357,9 +121,9 @@ namespace ClankerSaddle.Api {
|
|
|
}
|
|
}
|
|
|
var body = new Json.Object();
|
|
var body = new Json.Object();
|
|
|
body.set_array_member("tasks", array);
|
|
body.set_array_member("tasks", array);
|
|
|
- return SaddleUpdateEndpoint.json_ok(body);
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(body);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -380,7 +144,7 @@ namespace ClankerSaddle.Api {
|
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
string id_text = "";
|
|
string id_text = "";
|
|
@@ -390,7 +154,7 @@ namespace ClankerSaddle.Api {
|
|
|
try {
|
|
try {
|
|
|
var detail = yield tasks.get_task(task_id);
|
|
var detail = yield tasks.get_task(task_id);
|
|
|
if (detail == null) {
|
|
if (detail == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(404,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(404,
|
|
|
@"No task exists with id $(task_id)");
|
|
@"No task exists with id $(task_id)");
|
|
|
}
|
|
}
|
|
|
var task = ((!)detail).task;
|
|
var task = ((!)detail).task;
|
|
@@ -412,9 +176,9 @@ namespace ClankerSaddle.Api {
|
|
|
element.set_string_member("goal_title", goal_title);
|
|
element.set_string_member("goal_title", goal_title);
|
|
|
var body = new Json.Object();
|
|
var body = new Json.Object();
|
|
|
body.set_object_member("task", element);
|
|
body.set_object_member("task", element);
|
|
|
- return SaddleUpdateEndpoint.json_ok(body);
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(body);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -437,7 +201,7 @@ namespace ClankerSaddle.Api {
|
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -464,9 +228,9 @@ namespace ClankerSaddle.Api {
|
|
|
}
|
|
}
|
|
|
var body = new Json.Object();
|
|
var body = new Json.Object();
|
|
|
body.set_array_member("tags", tags);
|
|
body.set_array_member("tags", tags);
|
|
|
- return SaddleUpdateEndpoint.json_ok(body);
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(body);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -493,14 +257,14 @@ namespace ClankerSaddle.Api {
|
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
public virtual async HttpResult handle_request(HttpContext http_context,
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
if ((yield resolve_bearer(http_context, saddles)) == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Json.Object body;
|
|
Json.Object body;
|
|
|
try {
|
|
try {
|
|
|
- body = yield SaddleUpdateEndpoint.read_json_body(http_context);
|
|
|
|
|
|
|
+ body = yield SaddleWire.read_json_body(http_context);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
string project_name = EntryJson.get_string_or(body, "project", "");
|
|
string project_name = EntryJson.get_string_or(body, "project", "");
|
|
@@ -509,19 +273,19 @@ namespace ClankerSaddle.Api {
|
|
|
&& body.get_member("task_id").get_value_type() == typeof(int64)
|
|
&& body.get_member("task_id").get_value_type() == typeof(int64)
|
|
|
? body.get_int_member("task_id") : 0;
|
|
? body.get_int_member("task_id") : 0;
|
|
|
if (note_body.strip().length == 0) {
|
|
if (note_body.strip().length == 0) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, "A note body is required");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, "A note body is required");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
var project = yield projects.get_project_by_name(project_name);
|
|
var project = yield projects.get_project_by_name(project_name);
|
|
|
if (project == null) {
|
|
if (project == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"No project exists named '$(project_name)'");
|
|
@"No project exists named '$(project_name)'");
|
|
|
}
|
|
}
|
|
|
yield projects.add_note(((!)project).id, task_id, note_body);
|
|
yield projects.add_note(((!)project).id, task_id, note_body);
|
|
|
- return SaddleUpdateEndpoint.json_ok(new Json.Object());
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(new Json.Object());
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -604,9 +368,9 @@ namespace ClankerSaddle.Api {
|
|
|
public async HttpResult create(HttpContext http_context, Saddle saddle) throws Error {
|
|
public async HttpResult create(HttpContext http_context, Saddle saddle) throws Error {
|
|
|
Json.Object body;
|
|
Json.Object body;
|
|
|
try {
|
|
try {
|
|
|
- body = yield SaddleUpdateEndpoint.read_json_body(http_context);
|
|
|
|
|
|
|
+ body = yield SaddleWire.read_json_body(http_context);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -617,15 +381,15 @@ namespace ClankerSaddle.Api {
|
|
|
string title = EntryJson.get_string_or(body, "title", "");
|
|
string title = EntryJson.get_string_or(body, "title", "");
|
|
|
string mandate = EntryJson.get_string_or(body, "mandate", "");
|
|
string mandate = EntryJson.get_string_or(body, "mandate", "");
|
|
|
if (title.length == 0) {
|
|
if (title.length == 0) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, "A title is required");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, "A title is required");
|
|
|
}
|
|
}
|
|
|
if (mandate.length == 0) {
|
|
if (mandate.length == 0) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
"A mandate is required (heredoc body of `task create`)");
|
|
"A mandate is required (heredoc body of `task create`)");
|
|
|
}
|
|
}
|
|
|
string mode = EntryJson.get_string_or(body, "mode", "implement");
|
|
string mode = EntryJson.get_string_or(body, "mode", "implement");
|
|
|
if (!is_valid_mode(mode)) {
|
|
if (!is_valid_mode(mode)) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"Unknown mode '$(mode)' — valid modes: "
|
|
@"Unknown mode '$(mode)' — valid modes: "
|
|
|
+ string.joinv(", ", TASK_MODES));
|
|
+ string.joinv(", ", TASK_MODES));
|
|
|
}
|
|
}
|
|
@@ -635,7 +399,7 @@ namespace ClankerSaddle.Api {
|
|
|
if (body.has_member("capabilities")) {
|
|
if (body.has_member("capabilities")) {
|
|
|
var member = body.get_member("capabilities");
|
|
var member = body.get_member("capabilities");
|
|
|
if (member.get_node_type() != Json.NodeType.ARRAY) {
|
|
if (member.get_node_type() != Json.NodeType.ARRAY) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
"capabilities must be a JSON array of tag names");
|
|
"capabilities must be a JSON array of tag names");
|
|
|
}
|
|
}
|
|
|
foreach (var element in member.get_array().get_elements()) {
|
|
foreach (var element in member.get_array().get_elements()) {
|
|
@@ -665,7 +429,7 @@ namespace ClankerSaddle.Api {
|
|
|
foreach (var capability in yield capabilities.list_capabilities()) {
|
|
foreach (var capability in yield capabilities.list_capabilities()) {
|
|
|
valid.add(capability.name);
|
|
valid.add(capability.name);
|
|
|
}
|
|
}
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"Unknown capability tag(s): $(string.joinv(", ", unknown.to_array()))"
|
|
@"Unknown capability tag(s): $(string.joinv(", ", unknown.to_array()))"
|
|
|
+ @" — valid tags: $(string.joinv(", ", valid.to_array()))");
|
|
+ @" — valid tags: $(string.joinv(", ", valid.to_array()))");
|
|
|
}
|
|
}
|
|
@@ -679,9 +443,9 @@ namespace ClankerSaddle.Api {
|
|
|
}
|
|
}
|
|
|
var response = new Json.Object();
|
|
var response = new Json.Object();
|
|
|
response.set_int_member("task_id", task.id);
|
|
response.set_int_member("task_id", task.id);
|
|
|
- return SaddleUpdateEndpoint.json_ok(response);
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(response);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -693,9 +457,9 @@ namespace ClankerSaddle.Api {
|
|
|
public async HttpResult delete(HttpContext http_context, Saddle saddle) throws Error {
|
|
public async HttpResult delete(HttpContext http_context, Saddle saddle) throws Error {
|
|
|
Json.Object body;
|
|
Json.Object body;
|
|
|
try {
|
|
try {
|
|
|
- body = yield SaddleUpdateEndpoint.read_json_body(http_context);
|
|
|
|
|
|
|
+ body = yield SaddleWire.read_json_body(http_context);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -708,21 +472,21 @@ namespace ClankerSaddle.Api {
|
|
|
? body.get_int_member("task_id") : 0;
|
|
? body.get_int_member("task_id") : 0;
|
|
|
var detail = yield tasks.get_task(task_id);
|
|
var detail = yield tasks.get_task(task_id);
|
|
|
if (detail == null) {
|
|
if (detail == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"No task exists with id $(task_id)");
|
|
@"No task exists with id $(task_id)");
|
|
|
}
|
|
}
|
|
|
if (((!)detail).task.goal_id != goal_id) {
|
|
if (((!)detail).task.goal_id != goal_id) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"Task $(task_id) does not belong to your goal");
|
|
@"Task $(task_id) does not belong to your goal");
|
|
|
}
|
|
}
|
|
|
if (((!)detail).task.status != STATUS_DRAFT) {
|
|
if (((!)detail).task.status != STATUS_DRAFT) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
"Only draft tasks can be deleted (cancel handles the rest)");
|
|
"Only draft tasks can be deleted (cancel handles the rest)");
|
|
|
}
|
|
}
|
|
|
yield tasks.delete_task(task_id);
|
|
yield tasks.delete_task(task_id);
|
|
|
- return SaddleUpdateEndpoint.json_ok(new Json.Object());
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(new Json.Object());
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -736,9 +500,9 @@ namespace ClankerSaddle.Api {
|
|
|
public async HttpResult enqueue(HttpContext http_context, Saddle saddle) throws Error {
|
|
public async HttpResult enqueue(HttpContext http_context, Saddle saddle) throws Error {
|
|
|
Json.Object body;
|
|
Json.Object body;
|
|
|
try {
|
|
try {
|
|
|
- body = yield SaddleUpdateEndpoint.read_json_body(http_context);
|
|
|
|
|
|
|
+ body = yield SaddleWire.read_json_body(http_context);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -751,17 +515,17 @@ namespace ClankerSaddle.Api {
|
|
|
? body.get_int_member("task_id") : 0;
|
|
? body.get_int_member("task_id") : 0;
|
|
|
var detail = yield tasks.get_task(task_id);
|
|
var detail = yield tasks.get_task(task_id);
|
|
|
if (detail == null) {
|
|
if (detail == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"No task exists with id $(task_id)");
|
|
@"No task exists with id $(task_id)");
|
|
|
}
|
|
}
|
|
|
if (((!)detail).task.goal_id != goal_id) {
|
|
if (((!)detail).task.goal_id != goal_id) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"Task $(task_id) does not belong to your goal");
|
|
@"Task $(task_id) does not belong to your goal");
|
|
|
}
|
|
}
|
|
|
yield tasks.enqueue_task(task_id, CREATOR_GOAL);
|
|
yield tasks.enqueue_task(task_id, CREATOR_GOAL);
|
|
|
- return SaddleUpdateEndpoint.json_ok(new Json.Object());
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(new Json.Object());
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -783,7 +547,7 @@ namespace ClankerSaddle.Api {
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
|
if (saddle == null) {
|
|
if (saddle == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
var callbacks = new SaddleTaskCallbacks(saddles, tasks, goals, capabilities, db);
|
|
var callbacks = new SaddleTaskCallbacks(saddles, tasks, goals, capabilities, db);
|
|
|
return yield callbacks.create(http_context, (!)saddle);
|
|
return yield callbacks.create(http_context, (!)saddle);
|
|
@@ -806,7 +570,7 @@ namespace ClankerSaddle.Api {
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
|
if (saddle == null) {
|
|
if (saddle == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
var callbacks = new SaddleTaskCallbacks(saddles, tasks, goals, capabilities, db);
|
|
var callbacks = new SaddleTaskCallbacks(saddles, tasks, goals, capabilities, db);
|
|
|
return yield callbacks.delete(http_context, (!)saddle);
|
|
return yield callbacks.delete(http_context, (!)saddle);
|
|
@@ -829,7 +593,7 @@ namespace ClankerSaddle.Api {
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
|
if (saddle == null) {
|
|
if (saddle == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
var callbacks = new SaddleTaskCallbacks(saddles, tasks, goals, capabilities, db);
|
|
var callbacks = new SaddleTaskCallbacks(saddles, tasks, goals, capabilities, db);
|
|
|
return yield callbacks.enqueue(http_context, (!)saddle);
|
|
return yield callbacks.enqueue(http_context, (!)saddle);
|
|
@@ -866,7 +630,7 @@ namespace ClankerSaddle.Api {
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
|
if (saddle == null) {
|
|
if (saddle == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// An ask from an offline saddle is proof of life: the daemon
|
|
// An ask from an offline saddle is proof of life: the daemon
|
|
@@ -893,7 +657,7 @@ namespace ClankerSaddle.Api {
|
|
|
}
|
|
}
|
|
|
poll_state.mark_ask_result(((!)saddle).id, assignment != null);
|
|
poll_state.mark_ask_result(((!)saddle).id, assignment != null);
|
|
|
if (error_text.length > 0) {
|
|
if (error_text.length > 0) {
|
|
|
- return SaddleUpdateEndpoint.json_error(500,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(500,
|
|
|
@"Allocation failed: $(error_text)");
|
|
@"Allocation failed: $(error_text)");
|
|
|
}
|
|
}
|
|
|
if (assignment == null) {
|
|
if (assignment == null) {
|
|
@@ -935,14 +699,14 @@ namespace ClankerSaddle.Api {
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
|
if (saddle == null) {
|
|
if (saddle == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Json.Object body;
|
|
Json.Object body;
|
|
|
try {
|
|
try {
|
|
|
- body = yield SaddleUpdateEndpoint.read_json_body(http_context);
|
|
|
|
|
|
|
+ body = yield SaddleWire.read_json_body(http_context);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int64 session_id = body.has_member("session_id")
|
|
int64 session_id = body.has_member("session_id")
|
|
@@ -950,13 +714,13 @@ namespace ClankerSaddle.Api {
|
|
|
? body.get_int_member("session_id") : 0;
|
|
? body.get_int_member("session_id") : 0;
|
|
|
var session = yield session_owned_by(db, session_id, (!)saddle);
|
|
var session = yield session_owned_by(db, session_id, (!)saddle);
|
|
|
if (session == null) {
|
|
if (session == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"No session $(session_id) exists on saddle $(((!)saddle).id)");
|
|
@"No session $(session_id) exists on saddle $(((!)saddle).id)");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (!(body.has_member("chunks")
|
|
if (!(body.has_member("chunks")
|
|
|
&& body.get_member("chunks").get_node_type() == Json.NodeType.ARRAY)) {
|
|
&& body.get_member("chunks").get_node_type() == Json.NodeType.ARRAY)) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
"A chunks array is required");
|
|
"A chunks array is required");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -966,9 +730,9 @@ namespace ClankerSaddle.Api {
|
|
|
body.get_array_member("chunks"));
|
|
body.get_array_member("chunks"));
|
|
|
var response = new Json.Object();
|
|
var response = new Json.Object();
|
|
|
response.set_int_member("stored", stored);
|
|
response.set_int_member("stored", stored);
|
|
|
- return SaddleUpdateEndpoint.json_ok(response);
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(response);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1024,14 +788,14 @@ namespace ClankerSaddle.Api {
|
|
|
RouteContext route_context) throws Error {
|
|
RouteContext route_context) throws Error {
|
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
var saddle = yield resolve_bearer(http_context, saddles);
|
|
|
if (saddle == null) {
|
|
if (saddle == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(401, "Missing or invalid saddle token");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(401, "Missing or invalid saddle token");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Json.Object body;
|
|
Json.Object body;
|
|
|
try {
|
|
try {
|
|
|
- body = yield SaddleUpdateEndpoint.read_json_body(http_context);
|
|
|
|
|
|
|
+ body = yield SaddleWire.read_json_body(http_context);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int64 session_id = body.has_member("session_id")
|
|
int64 session_id = body.has_member("session_id")
|
|
@@ -1039,13 +803,13 @@ namespace ClankerSaddle.Api {
|
|
|
? body.get_int_member("session_id") : 0;
|
|
? body.get_int_member("session_id") : 0;
|
|
|
var session = yield SaddleChunksEndpoint.session_owned_by(db, session_id, (!)saddle);
|
|
var session = yield SaddleChunksEndpoint.session_owned_by(db, session_id, (!)saddle);
|
|
|
if (session == null) {
|
|
if (session == null) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400,
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400,
|
|
|
@"No session $(session_id) exists on saddle $(((!)saddle).id)");
|
|
@"No session $(session_id) exists on saddle $(((!)saddle).id)");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
string state = EntryJson.get_string_or(body, "state", "");
|
|
string state = EntryJson.get_string_or(body, "state", "");
|
|
|
if (state.length == 0) {
|
|
if (state.length == 0) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, "A state is required");
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, "A state is required");
|
|
|
}
|
|
}
|
|
|
Json.Object? block = body.has_member("block")
|
|
Json.Object? block = body.has_member("block")
|
|
|
&& body.get_member("block").get_node_type() == Json.NodeType.OBJECT
|
|
&& body.get_member("block").get_node_type() == Json.NodeType.OBJECT
|
|
@@ -1063,9 +827,9 @@ namespace ClankerSaddle.Api {
|
|
|
}
|
|
}
|
|
|
yield saddles.apply_result_state((!)saddle, (!)session, state, block,
|
|
yield saddles.apply_result_state((!)saddle, (!)session, state, block,
|
|
|
summary, tasks);
|
|
summary, tasks);
|
|
|
- return SaddleUpdateEndpoint.json_ok(response);
|
|
|
|
|
|
|
+ return SaddleWire.json_ok(response);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
- return SaddleUpdateEndpoint.json_error(400, e.message);
|
|
|
|
|
|
|
+ return SaddleWire.json_error(400, e.message);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -1086,24 +850,14 @@ namespace ClankerSaddle.Api {
|
|
|
/**
|
|
/**
|
|
|
* The ONE bearer resolution for the `/api/saddle/*` wire (plan 16
|
|
* The ONE bearer resolution for the `/api/saddle/*` wire (plan 16
|
|
|
* R2, arch item 26): the `Authorization: Bearer` credential
|
|
* R2, arch item 26): the `Authorization: Bearer` credential
|
|
|
- * resolved to its saddle — null fails the call. The two auth
|
|
|
|
|
- * styles live here together: the {id}-anchored routes (update,
|
|
|
|
|
- * poll) pin the credential to their route's saddle
|
|
|
|
|
- * ({@link SaddleService.validate_token} — the token must be THAT
|
|
|
|
|
- * saddle's); the id-less routes accept whichever registered
|
|
|
|
|
- * saddle owns the token ({@link SaddleService.saddle_for_token}
|
|
|
|
|
- * — the row token first, then any legacy api_tokens row; removing
|
|
|
|
|
- * the saddle revokes both).
|
|
|
|
|
|
|
+ * resolved to its saddle — null fails the call. The {id}-anchored
|
|
|
|
|
+ * poll-era routes (update, poll) died with arch 19, so every
|
|
|
|
|
+ * remaining route is id-less and accepts whichever registered
|
|
|
|
|
+ * saddle owns the token ({@link SaddleService.saddle_for_token};
|
|
|
|
|
+ * removing the saddle revokes it).
|
|
|
*/
|
|
*/
|
|
|
private static async Saddle? resolve_bearer(HttpContext http_context,
|
|
private static async Saddle? resolve_bearer(HttpContext http_context,
|
|
|
- SaddleService saddles, int64 anchored_saddle_id = 0) throws Error {
|
|
|
|
|
- string token = bearer_token(http_context);
|
|
|
|
|
- if (anchored_saddle_id > 0) {
|
|
|
|
|
- if (yield saddles.validate_token(anchored_saddle_id, token)) {
|
|
|
|
|
- return yield saddles.get_saddle(anchored_saddle_id);
|
|
|
|
|
- }
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- return yield saddles.saddle_for_token(token);
|
|
|
|
|
|
|
+ SaddleService saddles) throws Error {
|
|
|
|
|
+ return yield saddles.saddle_for_token(bearer_token(http_context));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|