~singpolyma/sgx-jmp

67609fee446efdbf8875997407d432d304d8e79a — Christopher Vollick 8 months ago 3162f5f
Undo and Undoable Command Harness

It's a little weird to start with Undo when there's nothing to Undo yet,
but it's laying the groundwork for what's to come.

This gives me a harness I can use here that gets the action, performs
it, gets the result of that performed action, and then persists that to
the log and reports success or failure.

And the first such action I have just grabs the most recent item and
undoes it.
3 files changed, 71 insertions(+), 9 deletions(-)

M forms/admin_menu.rb
M lib/admin_command.rb
M test/test_admin_command.rb
M forms/admin_menu.rb => forms/admin_menu.rb +2 -1
@@ 11,6 11,7 @@ field(
		{ value: "info", label: "Customer Info" },
		{ value: "financial", label: "Customer Billing Information" },
		{ value: "bill_plan", label: "Bill Customer" },
		{ value: "cancel_account", label: "Cancel Customer" }
		{ value: "cancel_account", label: "Cancel Customer" },
		{ value: "undo", label: "Undo" }
	]
)

M lib/admin_command.rb => lib/admin_command.rb +68 -8
@@ 1,5 1,6 @@
# frozen_string_literal: true

require_relative "admin_action_repo"
require_relative "admin_actions/cancel"
require_relative "admin_actions/financial"
require_relative "bill_plan_command"


@@ 8,21 9,26 @@ require_relative "financial_info"
require_relative "form_template"

class AdminCommand
	def self.for(target_customer, customer_repo)
	def self.for(
		target_customer,
		customer_repo,
		admin_action_repo=AdminActionRepo.new
	)
		if target_customer
			new(target_customer, customer_repo)
			new(target_customer, customer_repo, admin_action_repo)
		else
			Command.reply { |reply|
				reply.allowed_actions = [:next, :complete]
				reply.note_type = :error
				reply.note_text = "Customer Not Found"
			}.then { NoUser.new(customer_repo) }
			}.then { NoUser.new(customer_repo, admin_action_repo) }
		end
	end

	class NoUser
		def initialize(customer_repo)
		def initialize(customer_repo, admin_action_repo=AdminActionRepo.new)
			@customer_repo = customer_repo
			@admin_action_repo = admin_action_repo
		end

		def start


@@ 32,14 38,20 @@ class AdminCommand
			}.then { |response|
				CustomerInfoForm.new(@customer_repo).find_customer(response)
			}.then { |customer|
				AdminCommand.for(customer, @customer_repo).then(&:start)
				AdminCommand.for(customer, @customer_repo, @admin_action_repo)
					.then(&:start)
			}
		end
	end

	def initialize(target_customer, customer_repo)
	def initialize(
		target_customer,
		customer_repo,
		admin_action_repo=AdminActionRepo.new
	)
		@target_customer = target_customer
		@customer_repo = customer_repo
		@admin_action_repo = admin_action_repo
	end

	def start


@@ 76,7 88,8 @@ class AdminCommand
	def new_context(q)
		CustomerInfoForm.new(@customer_repo)
			.parse_something(q).then do |new_customer|
				AdminCommand.for(new_customer, @customer_repo).then(&:start)
				AdminCommand.for(new_customer, @customer_repo, @admin_action_repo)
					.then(&:start)
			end
	end



@@ 89,6 102,40 @@ class AdminCommand
		BillPlanCommand.for(@target_customer).call
	end

	class Undoable
		def initialize(klass)
			@klass = klass
		end

		def call(customer, admin_action_repo:, **)
			@klass.for(customer, reply: method(:reply)).then { |action|
				Command.customer.then { |actor|
					action.with(actor_id: actor.customer_id).perform.then do |performed|
						admin_action_repo.create(performed)
					end
				}
			}.then(method(:success), method(:failure))
		end

		def reply(form=nil, note_type: nil, note_text: nil)
			Command.reply { |reply|
				reply.allowed_actions = [:next, :complete]
				reply.command << form if form
				reply.note_type = note_type if note_type
				reply.note_text = note_text if note_text
			}
		end

		def success(action)
			reply(note_type: :info, note_text: "Action #{action.id}: #{action}")
		end

		def failure(err)
			LOG.error "Action Failure", err
			reply(note_type: :error, note_text: "Action Failed: #{err}")
		end
	end

	class Simple
		def initialize(klass)
			@klass = klass


@@ 112,9 159,22 @@ class AdminCommand
		end
	end

	class Undo
		def self.for(target_customer, **)
			AdminActionRepo.new
				.find(1, customer_id: target_customer.customer_id)
				.then { |actions|
					raise "No actions found" if actions.empty?

					actions.first.undo
				}
		end
	end

	[
		[:cancel_account, Simple.new(AdminAction::CancelCustomer)],
		[:financial, Simple.new(AdminAction::Financial)]
		[:financial, Simple.new(AdminAction::Financial)],
		[:undo, Undoable.new(Undo)]
	].each do |action, handler|
		define_method("action_#{action}") do
			handler.call(

M test/test_admin_command.rb => test/test_admin_command.rb +1 -0
@@ 4,6 4,7 @@ require "admin_command"

BackendSgx::IQ_MANAGER = Minitest::Mock.new
Customer::BLATHER = Minitest::Mock.new
AdminActionRepo::REDIS = Minitest::Mock.new

class AdminCommandTest < Minitest::Test
	def admin_command(tel="+15556667777")