Skip to content
This repository has been archived by the owner on Jun 11, 2023. It is now read-only.

Enable one Step per contact #382

Merged
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
4 changes: 3 additions & 1 deletion lib/account/websocket/channel/account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ channel Helix.Account.Websocket.Channel.Account do

alias Helix.Account.Websocket.Channel.Account.Join, as: AccountJoin
alias Helix.Account.Websocket.Requests.Bootstrap, as: BootstrapRequest
alias Helix.Account.Websocket.Requests.EmailReply, as: EmailReplyRequest
alias Helix.Account.Websocket.Requests.Logout, as: LogoutRequest
alias Helix.Client.Websocket.Requests.Setup, as: ClientSetupProxyRequest
alias Helix.Network.Websocket.Requests.Bounce.Create, as: BounceCreateRequest
alias Helix.Network.Websocket.Requests.Bounce.Update, as: BounceUpdateRequest
alias Helix.Network.Websocket.Requests.Bounce.Remove, as: BounceRemoveRequest
alias Helix.Story.Websocket.Requests.Email.Reply, as: EmailReplyRequest

@doc """
Joins the Account channel.
Expand Down Expand Up @@ -61,10 +61,12 @@ channel Helix.Account.Websocket.Channel.Account do

Params:
*reply_id: Reply identifier.
*contact_id: Which contact the reply is directed to.

Returns: :ok

Errors:
- "bad_contact" - The given contact is invalid.
- "not_in_step" - Player is not currently in any mission.
- "reply_not_found" - The given reply ID is not valid, may be locked or not
exist within the current step email.
Expand Down
46 changes: 0 additions & 46 deletions lib/account/websocket/requests/email_reply.ex

This file was deleted.

9 changes: 8 additions & 1 deletion lib/core/validator/validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Helix.Core.Validator do
:password
| :hostname
| :bounce_name
| :reply_id

@regex_hostname ~r/^[a-zA-Z0-9-_.@#]{1,20}$/

Expand All @@ -29,6 +30,9 @@ defmodule Helix.Core.Validator do
def validate_input(input, :bounce_name, _),
do: validate_bounce_name(input)

def validate_input(input, :reply_id, _),
do: validate_reply_id(input)

defp validate_hostname(v) when not is_binary(v),
do: :error
defp validate_hostname(v) do
Expand All @@ -39,9 +43,12 @@ defmodule Helix.Core.Validator do
end
end

def validate_password(input),
defp validate_password(input),
do: validate_hostname(input) # TODO

defp validate_bounce_name(v),
do: validate_hostname(v) # TODO

defp validate_reply_id(v),
do: validate_hostname(v) # TODO
end
30 changes: 30 additions & 0 deletions lib/story/action/flow/story.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Helix.Story.Action.Flow.Story do
alias Helix.Entity.Model.Entity
alias Helix.Story.Action.Flow.Context, as: ContextFlow
alias Helix.Story.Action.Story, as: StoryAction
alias Helix.Story.Query.Story, as: StoryQuery
alias Helix.Story.Model.Step
alias Helix.Story.Model.Steppable
alias Helix.Story.Model.Story
Expand Down Expand Up @@ -36,4 +37,33 @@ defmodule Helix.Story.Action.Flow.Story do
end
end
end

@spec send_reply(Entity.id, Step.contact, Step.reply_id) ::
:ok
| {:error, :bad_step}
| {:error, {:reply, :not_found}}
| {:error, :internal}
@doc """
Sends `reply_id` from `entity_id` to the `contact_id`.

Emits: StoryReplySentEvent.t
"""
def send_reply(entity_id, contact_id, reply_id) do
flowing do
with \
step = %{} <- StoryQuery.fetch_step(entity_id, contact_id) || :badstep,
{:ok, events} <-
StoryAction.send_reply(step.object, step.entry, reply_id),
on_success(fn -> Event.emit(events) end)
do
:ok
else
:badstep ->
{:error, :bad_step}

error ->
error
end
end
end
end
16 changes: 8 additions & 8 deletions lib/story/action/story.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ defmodule Helix.Story.Action.Story do
alias Helix.Story.Event.Reply.Sent, as: ReplySentEvent
alias Helix.Story.Event.Step.Proceeded, as: StepProceededEvent

@spec proceed_step(first_step :: Step.t(struct)) ::
@spec proceed_step(first_step :: Step.t) ::
{:ok, Story.Step.t}
| {:error, :internal}
@spec proceed_step(prev_step :: Step.t(struct), next_step :: Step.t(struct)) ::
@spec proceed_step(prev_step :: Step.t, next_step :: Step.t) ::
{:ok, Story.Step.t}
| {:error, :internal}
@doc """
Expand All @@ -28,7 +28,7 @@ defmodule Helix.Story.Action.Story do
def proceed_step(prev_step, next_step),
do: StepInternal.proceed(prev_step, next_step)

@spec update_step_meta(Step.t(struct)) ::
@spec update_step_meta(Step.t) ::
StepInternal.entry_step_repo_return
| no_return
@doc """
Expand All @@ -37,7 +37,7 @@ defmodule Helix.Story.Action.Story do
def update_step_meta(step),
do: StepInternal.update_meta(step)

@spec unlock_reply(Step.t(struct), Step.reply_id) ::
@spec unlock_reply(Step.t, Step.reply_id) ::
StepInternal.entry_step_repo_return
| no_return
@doc """
Expand All @@ -46,7 +46,7 @@ defmodule Helix.Story.Action.Story do
def unlock_reply(step, reply_id),
do: StepInternal.unlock_reply(step, reply_id)

@spec lock_reply(Step.t(struct), Step.reply_id) ::
@spec lock_reply(Step.t, Step.reply_id) ::
StepInternal.entry_step_repo_return
| no_return
@doc """
Expand All @@ -55,7 +55,7 @@ defmodule Helix.Story.Action.Story do
def lock_reply(step, reply_id),
do: StepInternal.lock_reply(step, reply_id)

@spec notify_step(Step.t(struct), Step.t(struct)) ::
@spec notify_step(Step.t, Step.t) ::
[StepProceededEvent.t]
@doc """
Generates the StepProceededEvent, used to notify the client about the progress
Expand All @@ -64,7 +64,7 @@ defmodule Helix.Story.Action.Story do
def notify_step(prev_step, next_step),
do: [StepProceededEvent.new(prev_step, next_step)]

@spec send_email(Step.t(struct), Step.email_id, Step.email_meta) ::
@spec send_email(Step.t, Step.email_id, Step.email_meta) ::
{:ok, [EmailSentEvent.t]}
| {:error, :internal}
@doc """
Expand All @@ -90,7 +90,7 @@ defmodule Helix.Story.Action.Story do
end)
end

@spec send_reply(Step.t(struct), Story.Step.t, Step.reply_id) ::
@spec send_reply(Step.t, Story.Step.t, Step.reply_id) ::
{:ok, [ReplySentEvent.t]}
| {:error, {:reply, :not_found}}
| {:error, :internal}
Expand Down
4 changes: 2 additions & 2 deletions lib/story/event/email.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ defmodule Helix.Story.Event.Email do
@type t ::
%__MODULE__{
entity_id: Entity.id,
step: Step.t(struct),
step: Step.t,
email: Story.Email.email
}

event_struct [:entity_id, :step, :email]

@spec new(Step.t(term), Story.Email.email) ::
@spec new(Step.t, Story.Email.email) ::
t
def new(step = %_{name: _, meta: _, entity_id: _}, email = %{id: _}) do
%__MODULE__{
Expand Down
23 changes: 14 additions & 9 deletions lib/story/event/handler/story.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ defmodule Helix.Story.Event.Handler.Story do

@doc """
Main step handler. Its first role is to figure out the entity that event
belongs to, and then fetching that entity's current step.
belongs to, and then fetching any steps that entity is assigned to.

If an step is found, we instantiate its object (Steppable data/struct), and
For each step found, we instantiate its object (Steppable data/struct), and
guide it through the StepFlow. See doc on `step_flow/1`

Emits:
Expand All @@ -35,16 +35,21 @@ defmodule Helix.Story.Event.Handler.Story do
def event_handler(event) do
with \
entity_id = %{} <- Step.get_entity(event),
step = %{} <- StoryQuery.fetch_current_step(entity_id)
steps = [_] <- StoryQuery.get_steps(entity_id)
do
step.object
|> Step.new(event)
|> step_flow()
Enum.each(steps, fn %{object: step} ->
step
|> Step.new(event)
|> step_flow
end)
end
end

def action_handler(event = %StepActionRequestedEvent{}) do
with %{object: step} <- StoryQuery.fetch_current_step(event.entity_id) do
with \
%{object: step} <-
StoryQuery.fetch_step(event.entity_id, event.contact_id)
do
step = Step.new(step, event)

handle_action(event.action, step)
Expand Down Expand Up @@ -74,7 +79,7 @@ defmodule Helix.Story.Event.Handler.Story do
end
end

@spec handle_action(Steppable.actions, Step.t(struct)) ::
@spec handle_action(Steppable.actions, Step.t) ::
term
docp """
When a step requests to be completed, we'll call `Steppable.complete/1`,
Expand Down Expand Up @@ -111,7 +116,7 @@ defmodule Helix.Story.Event.Handler.Story do
defp handle_action(:noop, _),
do: :noop

@spec update_next(Step.t(struct), Step.step_name) ::
@spec update_next(Step.t, Step.step_name) ::
term
docp """
Updates the database, so that the player gets moved to the next step.
Expand Down
4 changes: 2 additions & 2 deletions lib/story/event/reply.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ defmodule Helix.Story.Event.Reply do
@type t ::
%__MODULE__{
entity_id: Entity.id,
step: Step.t(struct),
step: Step.t,
reply_to: Step.email_id,
reply: Story.Email.email,
}

event_struct [:entity_id, :step, :reply_to, :reply]

@spec new(Step.t(struct), reply :: Story.Email.email, Step.email_id) ::
@spec new(Step.t, reply :: Story.Email.email, Step.email_id) ::
t
def new(step = %_{name: _, entity_id: _}, reply = %{id: _}, reply_to) do
%__MODULE__{
Expand Down
18 changes: 10 additions & 8 deletions lib/story/event/step.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ defmodule Helix.Story.Event.Step do
@type t ::
%__MODULE__{
entity_id: Entity.id,
previous_step: Step.t(struct),
next_step: Step.t(struct)
previous_step: Step.t,
next_step: Step.t
}

event_struct [:entity_id, :previous_step, :next_step]

@spec new(Step.t(struct), Step.t(struct)) ::
@spec new(Step.t, Step.t) ::
t
def new(prev_step = %_{entity_id: _}, next_step = %_{entity_id: _}) do
%__MODULE__{
Expand Down Expand Up @@ -65,20 +65,22 @@ defmodule Helix.Story.Event.Step do
alias Helix.Entity.Model.Entity
alias Helix.Story.Model.Step

event_struct [:action, :entity_id]
event_struct [:action, :entity_id, :contact_id]

@type t ::
%__MODULE__{
action: Step.callback_action,
entity_id: Entity.id
entity_id: Entity.id,
contact_id: Step.contact_id
}

@spec new(Step.callback_action, Entity.id) ::
@spec new(Step.callback_action, Entity.id, Step.contact_id) ::
t
def new(action, entity_id) do
def new(action, entity_id, contact_id) do
%__MODULE__{
action: action,
entity_id: entity_id
entity_id: entity_id,
contact_id: contact_id
}
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/story/internal/email.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ defmodule Helix.Story.Internal.Email do
|> Enum.map(&format/1)
end

@spec send_email(Step.t(struct), Step.email_id, Step.email_meta) ::
@spec send_email(Step.t, Step.email_id, Step.email_meta) ::
{:ok, Story.Email.t, Story.Email.email}
| :internal_error
@doc """
Expand All @@ -50,7 +50,7 @@ defmodule Helix.Story.Internal.Email do
def send_email(step, email_id, meta),
do: generic_send(step, email_id, :contact, meta)

@spec send_reply(Step.t(struct), Step.reply_id) ::
@spec send_reply(Step.t, Step.reply_id) ::
{:ok, Story.Email.t, Story.Email.email}
| :internal_error
@doc """
Expand Down
Loading