fed-sx-m2: Step 2c — bootstrap_actor/4 + actor_lifecycle integration
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
New nx_kernel:bootstrap_actor/4(ActorId, Profile, KeySpec, State)
single-call entry that adds an actor bucket and immediately publishes
a Create{Person|Service|Group} envelope as the bucket's first activity:
- Profile carries :type, :name, :preferredUsername, :summary, :icon,
:public_keys. :type defaults to person if unset.
- Kernel AS proplist built from Profile's :public_keys (falls back
to []).
- Create object built from Profile fields (Step 2b actor_state
fold picks the same field set).
gen_server variant bootstrap_actor/3 for live-kernel use plus a new
handle_call branch.
15/15 in next/tests/actor_lifecycle.sh covering pure + gen_server +
actor_state projection capture for all three actor types:
- Pure: bootstrap_actor advances log_tip = 1, Create has
object.type = person
- Pure: two actors share a kernel with independent log tips
- Pure: duplicate bootstrap_actor -> already_present
- Pure: typeless profile defaults to person
- Pure: empty public_keys handled
- gen_server: bootstrap_actor/3 against a live registered kernel
- actor_state projection captures Person, Service, Group profiles
- profile carries :preferredUsername + :public_keys from the
Create object
Closes Step 2 (2a Person/Service/Group genesis files,
2b actor_state projection fold, 2c bootstrap_actor + integration).
Conformance 761/761. 146/146 across 10 Step-2-adjacent suites
(actor_lifecycle, actor_state_pure, nx_kernel_multi, nx_kernel_server,
bootstrap_start, smoke_app_pure, smoke_pin_pure, define_registry_pure,
projection_server, outbox_publish).
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
-export([new/0, new/3,
|
||||
add_actor/4, has_actor/2, actors/1, actor_count/1,
|
||||
publish/2, publish/3,
|
||||
bootstrap_actor/4,
|
||||
actor_id/1, log_state/1, log_tip/1,
|
||||
key_spec/1, actor_state/1, projections/1, next_published/1,
|
||||
actor_log_state/2, actor_log_tip/2,
|
||||
@@ -18,7 +19,8 @@
|
||||
with_projections/1, stop/0,
|
||||
add_actor/3, publish_to/2, log_tip_for/1,
|
||||
actors/0, state_for/1, bucket_for/1,
|
||||
with_projections_for/2]).
|
||||
with_projections_for/2,
|
||||
bootstrap_actor/3]).
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
|
||||
|
||||
%% Kernel orchestrator — the long-lived runtime state held by the
|
||||
@@ -116,6 +118,40 @@ publish(Request, State) ->
|
||||
[First | _] -> publish(First, Request, State)
|
||||
end.
|
||||
|
||||
%% bootstrap_actor/4 — register an actor bucket and immediately
|
||||
%% publish a Create{Person|Service|Group} as that actor's first
|
||||
%% activity. Profile carries the object fields plus :public_keys.
|
||||
%% Returns {ok, Result, NewState} where Result has the published
|
||||
%% Create's CID, or {error, Reason, State} on validation halt.
|
||||
|
||||
bootstrap_actor(ActorId, Profile, KeySpec, State) ->
|
||||
PublicKeys = case field(public_keys, Profile) of
|
||||
nil -> [];
|
||||
KS -> KS
|
||||
end,
|
||||
AS = [{public_keys, PublicKeys}],
|
||||
case add_actor(ActorId, KeySpec, AS, State) of
|
||||
{ok, State1} ->
|
||||
ActorType = case field(type, Profile) of
|
||||
nil -> person;
|
||||
T -> T
|
||||
end,
|
||||
Object = [{type, ActorType}] ++ collect_profile_fields(
|
||||
[name, preferredUsername, summary, icon, public_keys],
|
||||
Profile),
|
||||
Request = [{type, create}, {object, Object}],
|
||||
publish(ActorId, Request, State1);
|
||||
{error, Reason} ->
|
||||
{error, Reason, State}
|
||||
end.
|
||||
|
||||
collect_profile_fields([], _) -> [];
|
||||
collect_profile_fields([F | Rest], Profile) ->
|
||||
case field(F, Profile) of
|
||||
nil -> collect_profile_fields(Rest, Profile);
|
||||
V -> [{F, V} | collect_profile_fields(Rest, Profile)]
|
||||
end.
|
||||
|
||||
with_actor_projections(ActorId, Names, State) ->
|
||||
case actor_bucket(ActorId, State) of
|
||||
{error, no_actor} ->
|
||||
@@ -297,6 +333,9 @@ bucket_for(ActorId) ->
|
||||
with_projections_for(ActorId, Names) ->
|
||||
gen_server:call(nx_kernel, {set_projections_for, ActorId, Names}).
|
||||
|
||||
bootstrap_actor(ActorId, Profile, KeySpec) ->
|
||||
gen_server:call(nx_kernel, {bootstrap_actor, ActorId, Profile, KeySpec}).
|
||||
|
||||
%% gen_server callbacks
|
||||
|
||||
init([ActorId, KeySpec, AS]) ->
|
||||
@@ -337,6 +376,11 @@ handle_call({set_projections_for, ActorId, Names}, _From, State) ->
|
||||
case with_actor_projections(ActorId, Names, State) of
|
||||
{ok, NewState} -> {reply, ok, NewState};
|
||||
{error, Reason} -> {reply, {error, Reason}, State}
|
||||
end;
|
||||
handle_call({bootstrap_actor, ActorId, Profile, KeySpec}, _From, State) ->
|
||||
case bootstrap_actor(ActorId, Profile, KeySpec, State) of
|
||||
{ok, Result, NewState} -> {reply, {ok, Result}, NewState};
|
||||
{error, Reason, SameState} -> {reply, {error, Reason}, SameState}
|
||||
end.
|
||||
|
||||
handle_cast(_, S) -> {noreply, S}.
|
||||
|
||||
Reference in New Issue
Block a user