Skip to content

Feat/new api #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Variables
PYTHONPATH := $(shell pwd)

linter: format
poetry run bandit . && poetry run flake8 . && poetry run black --check .

format:
poetry run isort . && poetry run black .

build:
poetry build

install:
poetry env use python3
poetry lock
poetry install

test: clean
@PYTHONPATH="${PYTHONPATH}" ENVIRONMENT=unittest poetry run -vvv coverage run -vvv -m pytest

test-all:
@PYTHONPATH="${PYTHONPATH}" ENVIRONMENT=local python -m pytest tests

update-poetry-and-all-dependencies:
poetry self update
poetry self add poetry-plugin-up
poetry up --latest
opentelemetry-bootstrap -a install

clean:
@find . | egrep '.pyc|.pyo|pycache' | xargs rm -rf
@find . | egrep '.pyc|.pyo|pycache|pytest_cache' | xargs rm -rf
@rm -rf ./htmlcov
@rm -rf ./pycache
@rm -rf ./pycache
@rm -rf ./.pytest_cache
@rm -rf ./.mypy_cache
@find . -name 'unit_test.db' -exec rm -r -f {} +
@find . -name '.coverage' -exec rm -r -f {} +
54 changes: 29 additions & 25 deletions example/joe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@
Copyright 2022 Eigr.
Licensed under the Apache License, Version 2.0.
"""
from dataclasses import dataclass
from domain.domain_pb2 import JoeState, Request, Reply
from spawn.entity import ActorContext, ActorEntity, ActorInit, ActorParams, Value
from spawn.eigr.functions.actors.api.actor import Actor
from spawn.eigr.functions.actors.api.settings import ActorSettings
from spawn.eigr.functions.actors.api.context import Context
from spawn.eigr.functions.actors.api.metadata import Metadata
from spawn.eigr.functions.actors.api.value import Value
from spawn.eigr.functions.actors.api.workflows.broadcast import Broadcast
from spawn.eigr.functions.actors.api.workflows.effect import Effect

actor = Actor(settings=ActorSettings(name="joe", stateful=True))

@dataclass
class JoeActor(ActorInit):

# TODO: Remove this because it´s a bad design. Correct is extract this to superior Class
def init() -> ActorParams:
return ActorParams(
name="joe",
state_type=JoeState,
snapshot_timeout=10000,
deactivate_timeout=120000,
)
@actor.timer_action(every=1000)
def hi(ctx: Context) -> Value:
new_state = None
if not ctx.state:
new_state = JoeState()
new_state.languages.append("portuguese")
else:
new_state = ctx.state

entity = ActorEntity(init)
return Value()\
.of("test")\
.state(new_state)\
.reply()

@entity.command("getActualState")
def get_actual_state(self, ctx: ActorContext) -> Value:
current_state = ctx.state
new_value = current_state
return Value(current_state, new_value)

@entity.command("setLanguage")
def set_language(self, ctx: ActorContext) -> Value:
reply = Reply()
reply.response = "elf"

new_state = ctx.state.languages.extend("elf")
return Value(new_state, reply)
@actor.action("setLanguage")
def set_language(request: Request, ctx: Context) -> Value:
return Value()\
.of("test")\
.broadcast(Broadcast())\
.effect(Effect())\
.metada(Metadata())\
.state({})\
.reply()
8 changes: 4 additions & 4 deletions example/spawn_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
Copyright 2022 Eigr.
Licensed under the Apache License, Version 2.0.
"""
from spawn.sdk import Spawn
from example.joe import JoeActor
from spawn.eigr.functions.actors.api.sdk import Spawn
from example.joe import actor as joe_actor
from example.domain.domain_pb2 import Reply, Request

if __name__ == "__main__":
request = Request()
request.language = "erlang"

spawn = Spawn()
spawn.port("8091").register_actor(JoeActor.entity).start()
spawn.invoke("vijay", "setLanguage", request, Reply)
spawn.port(8091).proxy_port(9003).actor_system(
"spawn-system").add_actor(joe_actor).start()
1,225 changes: 1,225 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

46 changes: 23 additions & 23 deletions protobuf/eigr/functions/protocol/actors/actor.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,27 @@ message TimeoutStrategy {
int64 timeout = 1;
}

// A command represents an action that the user can perform on an Actor.
// Commands in supporting languages are represented by functions or methods.
// An Actor command has nothing to do with the semantics of Commands in a CQRS/EventSourced system.
// A action represents an action that the user can perform on an Actor.
// Actions in supporting languages are represented by functions or methods.
// An Actor action has nothing to do with the semantics of Actions in a CQRS/EventSourced system.
// It just represents an action that supporting languages can invoke.
message Command {
message Action {

// The name of the function or method in the supporting language that has been registered in Ator.
string name = 1;
}

// A FixedTimerCommand is similar to a regular Command, its main differences are that it is scheduled to run at regular intervals
// A FixedTimerAction is similar to a regular Action, its main differences are that it is scheduled to run at regular intervals
// and only takes the actor's state as an argument.
// Timer Commands are good for executing loops that manipulate the actor's own state.
// Timer Actions are good for executing loops that manipulate the actor's own state.
// In Elixir or other languages in BEAM it would be similar to invoking Process.send_after(self(), atom, msg, timeout)
message FixedTimerCommand {
message FixedTimerAction {

// The time to wait until the command is triggered
// The time to wait until the action is triggered
int32 seconds = 1;

// See Command description Above
Command command = 2;
// See Action description Above
Action action = 2;
}

message ActorState {
Expand All @@ -68,7 +68,7 @@ message ActorState {

// TODO doc here
message Metadata {
// A channel group represents a way to send commands to various actors
// A channel group represents a way to send actions to various actors
// that belong to a certain semantic group.
string channel_group = 1;

Expand All @@ -79,17 +79,17 @@ message Metadata {
// Regardless of the type of actor it is important that
// all actors are registered during the proxy and host initialization phase.
enum Kind {
// When no type is informed, the default to be assumed will be the Singleton pattern.
// When no type is informed, the default to be assumed will be the Named pattern.
UNKNOW_KIND = 0;

// Abstract actors are used to create children of this based actor at runtime
ABSTRACT = 1;
// NAMED actors are used to create children of this based actor at runtime
NAMED = 1;

// Singleton actors as the name suggests have only one real instance of themselves running
// during their entire lifecycle. That is, they are the opposite of the Abstract type Actors.
SINGLETON = 2;
// UNAMED actors as the name suggests have only one real instance of themselves running
// during their entire lifecycle. That is, they are the opposite of the NAMED type Actors.
UNAMED = 2;

// Pooled Actors are similar to abstract actors, but unlike them,
// Pooled Actors are similar to Unamed actors, but unlike them,
// their identifying name will always be the one registered at the system initialization stage.
// The great advantage of Pooled actors is that they have multiple instances of themselves
// acting as a request service pool.
Expand Down Expand Up @@ -134,7 +134,7 @@ message ActorId {
// Name of a ActorSystem
string system = 2;

// When the Actor is of the Abstract type,
// When the Actor is of the Unamed type,
// the name of the parent Actor must be informed here.
string parent = 3;
}
Expand All @@ -152,9 +152,9 @@ message Actor {
// Actor settings.
ActorSettings settings = 3;

// The commands registered for an actor
repeated Command commands = 4;
// The actions registered for an actor
repeated Action actions = 4;

// The registered timer commands for an actor.
repeated FixedTimerCommand timer_commands = 5;
// The registered timer actions for an actor.
repeated FixedTimerAction timer_actions = 5;
}
51 changes: 29 additions & 22 deletions protobuf/eigr/functions/protocol/actors/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
//
// Actors are usually created at the beginning of the SDK's communication flow with the Proxy by the registration step described above.
// However, some use cases require that Actors can be created ***on the fly***.
// In other words, Spawn is used to bring to life Actors previously registered as Abstracts, giving them a name and thus creating a concrete instance
// In other words, Spawn is used to bring to life Actors previously registered as Unameds, giving them a name and thus creating a concrete instance
// at runtime for that Actor. Actors created with the Spawn feature are generally used when you want to share a behavior while maintaining
// the isolation characteristics of the actors.
// For these situations we have the Spawning flow described below.
Expand Down Expand Up @@ -135,7 +135,7 @@
// ║ ║ ║ ║
// ║ ║ ╠───┐ ║
// ║ ║ ║ │Handle request, ║
// ║ ║ ║ │execute command
// ║ ║ ║ │execute action
// ║ ║ ║◀──┘ ║
// ║ ║ ║ Reply with the ║
// ║ ║ ╠────────────result and the ────────▶║
Expand Down Expand Up @@ -167,7 +167,8 @@ option go_package = "github.com/eigr/go-support/eigr/protocol;protocol";
//
// Params:
// * state: Actor state passed back and forth between proxy and user function.
// * metadata: Meta information or headers
// * metadata: Meta information that comes in invocations
// * tags: Meta information stored in the actor
// * caller: ActorId of who is calling target actor
// * self: ActorId of itself
message Context {
Expand All @@ -176,6 +177,8 @@ message Context {

map<string, string> metadata = 4;

map<string, string> tags = 5;

// Who is calling target actor
eigr.functions.protocol.actors.ActorId caller = 2;

Expand All @@ -189,6 +192,12 @@ message Context {
// he does not care about the input value only with the state.
message Noop {}

// JSON is an alternative that some SDKs can opt in
// it will bypass any type validation in spawn actors state / payloads
message JSONType {
string content = 1;
}

message RegistrationRequest {

ServiceInfo service_info = 1;
Expand Down Expand Up @@ -268,8 +277,8 @@ message Broadcast {
// Channel of target Actors
string channel_group = 1;

// Command. Only Actors that have this command will run successfully
string command_name = 2;
// Action. Only Actors that have this action will run successfully
string action_name = 2;

// Payload
oneof payload {
Expand All @@ -278,26 +287,26 @@ message Broadcast {
}
}

// Sends the output of a command of an Actor to the input of another command of an Actor
// Sends the output of a action of an Actor to the input of another action of an Actor
// Useful for handle `pipes` pattern:
// https://www.enterpriseintegrationpatterns.com/patterns/messaging/PipesAndFilters.html
message Pipe {
// Target Actor
string actor = 1;

// Command.
string command_name = 2;
// Action.
string action_name = 2;
}

// Sends the input of a command of an Actor to the input of another command of an Actor
// Sends the input of a action of an Actor to the input of another action of an Actor
// Useful for handle `content-basead router` pattern
// https://www.enterpriseintegrationpatterns.com/patterns/messaging/ContentBasedRouter.html
message Forward {
// Target Actor
string actor = 1;

// Command.
string command_name = 2;
// Action.
string action_name = 2;
}

// Container for archicetural message patterns
Expand All @@ -316,22 +325,22 @@ message Workflow {
// The user function when it wants to send a message to an Actor uses the InvocationRequest message type.
//
// Params:
// * system: See ActorStstem message.
// * system: See ActorSystem message.
// * actor: The target Actor, i.e. the one that the user function is calling to perform some computation.
// * caller: The caller Actor
// * command_name: The function or method on the target Actor that will receive this request
// * action_name: The function or method on the target Actor that will receive this request
// and perform some useful computation with the sent data.
// * value: This is the value sent by the user function to be computed by the request's target Actor command.
// * async: Indicates whether the command should be processed synchronously, where a response should be sent back to the user function,
// or whether the command should be processed asynchronously, i.e. no response sent to the caller and no waiting.
// * value: This is the value sent by the user function to be computed by the request's target Actor action.
// * async: Indicates whether the action should be processed synchronously, where a response should be sent back to the user function,
// or whether the action should be processed asynchronously, i.e. no response sent to the caller and no waiting.
// * metadata: Meta information or headers
message InvocationRequest {

eigr.functions.protocol.actors.ActorSystem system = 1;

eigr.functions.protocol.actors.Actor actor = 2;

string command_name = 3;
string action_name = 3;

oneof payload {
google.protobuf.Any value = 4;
Expand All @@ -354,17 +363,17 @@ message InvocationRequest {
//
// Params:
// * actor: The ActorId handling the InvocationRequest request, also called the target Actor.
// * command_name: The function or method on the target Actor that will receive this request
// * action_name: The function or method on the target Actor that will receive this request
// and perform some useful computation with the sent data.
// * current_context: The current Context with current state value of the target Actor.
// That is, the same as found via matching in %Actor{name: target_actor, state: %ActorState{state: value} = actor_state}.
// In this case, the Context type will contain in the value attribute the same `value` as the matching above.
// * payload: The value to be passed to the function or method corresponding to command_name.
// * payload: The value to be passed to the function or method corresponding to action_name.
message ActorInvocation {

eigr.functions.protocol.actors.ActorId actor = 1;

string command_name = 2;
string action_name = 2;

Context current_context = 3;

Expand Down Expand Up @@ -439,5 +448,3 @@ message RequestStatus {

string message = 2;
}


Loading