fed-sx-m2: Step 4a — per-actor HTTP sub-paths + 17 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 30s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 30s
Per design §16.1 each actor has /outbox /inbox /followers /following sub-paths. New split_first_slash/1 helper lets the GET /actors/... dispatch arm fan out on the sub-segment: GET /actors/<id> actor doc (M1 — unchanged) GET /actors/<id>/outbox outbox stub (4a) GET /actors/<id>/inbox inbox stub (4a) GET /actors/<id>/followers follower stub (4a) GET /actors/<id>/following following stub (4a) POST /actors/<id>/inbox 202 Accepted stub (4a; Step 5 real) Four new content-negotiated response functions mirror the existing actor_doc_response_for/2 shape (text / json / activity_json / sx variants): actor_outbox_response_for/2 actor_inbox_get_response_for/2 actor_followers_response_for/2 actor_following_response_for/2 POST returns 202 via new accepted_response/1 + actor_inbox_post_response/0. Unknown sub-paths under /actors/<id>/ return 404. Bare /actors/<id> preserves the M1 actor-doc arm so http_route + http_post_format regression suites stay green. 4b-4e (token map, route/3 kernel access, per-actor outbox listing from log entries, real inbox pipeline) layer on top of this dispatch in subsequent iterations. 17/17 in next/tests/http_multi_actor.sh covering: - split_first_slash sanity (no slash / id+sub / trailing slash) - all four GET sub-paths return 200 with stub bodies - POST inbox returns 202 + 'accepted' - unknown sub-paths return 404 (GET and POST) - empty /actors/ returns 404 - body carries the actor id - content negotiation: outbox JSON, inbox SX, followers JSON Conformance 761/761. 120/120 across 10 Step-4-adjacent suites (http_route, http_publish, http_post_format, http_marshal, http_publish_fold, http_listen_bif, http_server_start, nx_kernel_multi, actor_state_pure, bootstrap_start).
This commit is contained in:
@@ -16,7 +16,11 @@
|
||||
content_type_for/1, ok_response/2,
|
||||
cid_response_for/2, post_activity_response_for/1,
|
||||
actor_doc_response_for/2, artifact_response_for/2,
|
||||
projection_response_for/2, projections_list_response_for/1]).
|
||||
projection_response_for/2, projections_list_response_for/1,
|
||||
actor_outbox_response_for/2, actor_inbox_get_response_for/2,
|
||||
actor_followers_response_for/2, actor_following_response_for/2,
|
||||
actor_inbox_post_response/0, accepted_response/1,
|
||||
split_first_slash/1]).
|
||||
|
||||
%% HTTP request router per design §16.1.
|
||||
%%
|
||||
@@ -94,11 +98,11 @@ dispatch(<<71, 69, 84>>,
|
||||
%% prefix clause because the bare path has no trailing slash.
|
||||
dispatch(<<71, 69, 84>>, <<47,112,114,111,106,101,99,116,105,111,110,115>>, F) ->
|
||||
projections_list_response_for(F);
|
||||
%% GET /actors/{id} or /artifacts/{cid} or /projections/{name}
|
||||
%% GET /actors/{id}[/sub] or /artifacts/{cid} or /projections/{name}
|
||||
dispatch(<<71, 69, 84>>, Path, F) ->
|
||||
case match_prefix(actors_prefix(), Path) of
|
||||
{ok, Id} when byte_size(Id) > 0 ->
|
||||
actor_doc_response_for(Id, F);
|
||||
{ok, Rest} when byte_size(Rest) > 0 ->
|
||||
actor_get(Rest, F);
|
||||
_ ->
|
||||
case match_prefix(artifacts_prefix(), Path) of
|
||||
{ok, Cid} when byte_size(Cid) > 0 ->
|
||||
@@ -112,9 +116,71 @@ dispatch(<<71, 69, 84>>, Path, F) ->
|
||||
end
|
||||
end
|
||||
end;
|
||||
%% POST /actors/{id}/inbox — peer-side delivery (Step 4a returns
|
||||
%% 202 Accepted stub; Step 5 lands the real ingestion pipeline).
|
||||
dispatch(<<80, 79, 83, 84>>, Path, _F) ->
|
||||
case match_prefix(actors_prefix(), Path) of
|
||||
{ok, Rest} when byte_size(Rest) > 0 ->
|
||||
actor_post(Rest);
|
||||
_ ->
|
||||
not_found_response()
|
||||
end;
|
||||
dispatch(_, _, _) ->
|
||||
not_found_response().
|
||||
|
||||
%% actor_get/2 — Rest is the part after "/actors/". If it has no
|
||||
%% inner slash, it's the bare actor doc. Otherwise dispatch on the
|
||||
%% sub-segment.
|
||||
|
||||
actor_get(Rest, F) ->
|
||||
case split_first_slash(Rest) of
|
||||
{Id, <<>>} -> actor_doc_response_for(Id, F);
|
||||
{Id, Sub} -> actor_subresource_get(Id, Sub, F);
|
||||
Id -> actor_doc_response_for(Id, F)
|
||||
end.
|
||||
|
||||
%% 111 117 116 98 111 120 = "outbox"
|
||||
actor_subresource_get(Id, <<111,117,116,98,111,120>>, F) ->
|
||||
actor_outbox_response_for(Id, F);
|
||||
%% 105 110 98 111 120 = "inbox"
|
||||
actor_subresource_get(Id, <<105,110,98,111,120>>, F) ->
|
||||
actor_inbox_get_response_for(Id, F);
|
||||
%% 102 111 108 108 111 119 101 114 115 = "followers"
|
||||
actor_subresource_get(Id, <<102,111,108,108,111,119,101,114,115>>, F) ->
|
||||
actor_followers_response_for(Id, F);
|
||||
%% 102 111 108 108 111 119 105 110 103 = "following"
|
||||
actor_subresource_get(Id, <<102,111,108,108,111,119,105,110,103>>, F) ->
|
||||
actor_following_response_for(Id, F);
|
||||
actor_subresource_get(_, _, _) ->
|
||||
not_found_response().
|
||||
|
||||
actor_post(Rest) ->
|
||||
case split_first_slash(Rest) of
|
||||
{_Id, <<105,110,98,111,120>>} ->
|
||||
actor_inbox_post_response();
|
||||
_ ->
|
||||
not_found_response()
|
||||
end.
|
||||
|
||||
%% split_first_slash/1 — split a binary on the first slash. Returns
|
||||
%% {Before, After} where After omits the slash itself. If no slash
|
||||
%% is present, returns just Before. 47 = "/".
|
||||
%%
|
||||
%% <<"alice">> -> <<"alice">>
|
||||
%% <<"alice/">> -> {<<"alice">>, <<>>}
|
||||
%% <<"alice/inbox">> -> {<<"alice">>, <<"inbox">>}
|
||||
%% <<"alice/inbox/x">> -> {<<"alice">>, <<"inbox/x">>}
|
||||
|
||||
split_first_slash(Bin) ->
|
||||
split_first_slash(Bin, <<>>).
|
||||
|
||||
split_first_slash(<<>>, Acc) ->
|
||||
Acc;
|
||||
split_first_slash(<<47, Rest/binary>>, Acc) ->
|
||||
{Acc, Rest};
|
||||
split_first_slash(<<B, Rest/binary>>, Acc) ->
|
||||
split_first_slash(Rest, <<Acc/binary, B>>).
|
||||
|
||||
%% "fed-sx kernel m1\n" — 17 bytes, hand-spelled.
|
||||
%% f e d - s x _ k e r n e l _ m 1 \n
|
||||
welcome_body() ->
|
||||
@@ -538,6 +604,105 @@ actor_doc_response_for(Id, cbor) ->
|
||||
actor_doc_response_for(Id, _) ->
|
||||
actor_doc_response(Id).
|
||||
|
||||
%% ── Step 4a: per-actor sub-resource stubs ──────────────────────
|
||||
%% Per design §16.1 each actor has /outbox /inbox /followers
|
||||
%% /following routes. v1 returns text-stub bodies so route resolution
|
||||
%% can be tested end-to-end; real serialisation of per-actor outbox
|
||||
%% listings (Step 4d) + follower-graph projection bodies (Step 6+)
|
||||
%% layer on top of these dispatch arms.
|
||||
|
||||
%% "outbox: " — 8 bytes
|
||||
actor_outbox_response_for(Id, text) ->
|
||||
Pre = <<111,117,116,98,111,120,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>);
|
||||
actor_outbox_response_for(Id, json) ->
|
||||
Pre = <<123,34,111,117,116,98,111,120,34,58,34>>, % '{"outbox":"'
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, json);
|
||||
actor_outbox_response_for(Id, activity_json) ->
|
||||
Pre = <<123,34,111,117,116,98,111,120,34,58,34>>,
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, activity_json);
|
||||
actor_outbox_response_for(Id, sx) ->
|
||||
Pre = <<40,111,117,116,98,111,120,32,34>>, % '(outbox "'
|
||||
Suf = <<34,41,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, sx);
|
||||
actor_outbox_response_for(Id, _) ->
|
||||
Pre = <<111,117,116,98,111,120,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>).
|
||||
|
||||
%% "inbox: " — 7 bytes
|
||||
actor_inbox_get_response_for(Id, text) ->
|
||||
Pre = <<105,110,98,111,120,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>);
|
||||
actor_inbox_get_response_for(Id, json) ->
|
||||
Pre = <<123,34,105,110,98,111,120,34,58,34>>, % '{"inbox":"'
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, json);
|
||||
actor_inbox_get_response_for(Id, activity_json) ->
|
||||
Pre = <<123,34,105,110,98,111,120,34,58,34>>,
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, activity_json);
|
||||
actor_inbox_get_response_for(Id, sx) ->
|
||||
Pre = <<40,105,110,98,111,120,32,34>>, % '(inbox "'
|
||||
Suf = <<34,41,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, sx);
|
||||
actor_inbox_get_response_for(Id, _) ->
|
||||
Pre = <<105,110,98,111,120,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>).
|
||||
|
||||
%% "followers: " — 11 bytes
|
||||
actor_followers_response_for(Id, text) ->
|
||||
Pre = <<102,111,108,108,111,119,101,114,115,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>);
|
||||
actor_followers_response_for(Id, json) ->
|
||||
Pre = <<123,34,102,111,108,108,111,119,101,114,115,34,58,34>>,
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, json);
|
||||
actor_followers_response_for(Id, activity_json) ->
|
||||
Pre = <<123,34,102,111,108,108,111,119,101,114,115,34,58,34>>,
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, activity_json);
|
||||
actor_followers_response_for(Id, sx) ->
|
||||
Pre = <<40,102,111,108,108,111,119,101,114,115,32,34>>,
|
||||
Suf = <<34,41,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, sx);
|
||||
actor_followers_response_for(Id, _) ->
|
||||
Pre = <<102,111,108,108,111,119,101,114,115,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>).
|
||||
|
||||
%% "following: " — 11 bytes
|
||||
actor_following_response_for(Id, text) ->
|
||||
Pre = <<102,111,108,108,111,119,105,110,103,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>);
|
||||
actor_following_response_for(Id, json) ->
|
||||
Pre = <<123,34,102,111,108,108,111,119,105,110,103,34,58,34>>,
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, json);
|
||||
actor_following_response_for(Id, activity_json) ->
|
||||
Pre = <<123,34,102,111,108,108,111,119,105,110,103,34,58,34>>,
|
||||
Suf = <<34,125,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, activity_json);
|
||||
actor_following_response_for(Id, sx) ->
|
||||
Pre = <<40,102,111,108,108,111,119,105,110,103,32,34>>,
|
||||
Suf = <<34,41,10>>,
|
||||
ok_response(<<Pre/binary, Id/binary, Suf/binary>>, sx);
|
||||
actor_following_response_for(Id, _) ->
|
||||
Pre = <<102,111,108,108,111,119,105,110,103,58,32>>,
|
||||
ok_response(<<Pre/binary, Id/binary, 10>>).
|
||||
|
||||
%% POST /actors/<id>/inbox stub — 202 Accepted with body "accepted\n".
|
||||
%% Real ingestion pipeline (sig verify + envelope:get_field + log
|
||||
%% append on the receiving actor's inbox bucket) lands in Step 5.
|
||||
|
||||
actor_inbox_post_response() ->
|
||||
%% "accepted\n" — 9 bytes
|
||||
Body = <<97,99,99,101,112,116,101,100,10>>,
|
||||
accepted_response(Body).
|
||||
|
||||
accepted_response(Body) ->
|
||||
[{status, 202}, {headers, []}, {body, Body}].
|
||||
|
||||
%% artifact_response — text body `artifact: <cid>\n`.
|
||||
|
||||
artifact_response_for(Cid, text) ->
|
||||
|
||||
Reference in New Issue
Block a user