~singpolyma/sgx-jmp

ref: 67609fee446efdbf8875997407d432d304d8e79a sgx-jmp/lib/admin_command.rb -rw-r--r-- 4.4 KiB
67609feeChristopher Vollick Undo and Undoable Command Harness 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# frozen_string_literal: true

require_relative "admin_action_repo"
require_relative "admin_actions/cancel"
require_relative "admin_actions/financial"
require_relative "bill_plan_command"
require_relative "customer_info_form"
require_relative "financial_info"
require_relative "form_template"

class AdminCommand
	def self.for(
		target_customer,
		customer_repo,
		admin_action_repo=AdminActionRepo.new
	)
		if target_customer
			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, admin_action_repo) }
		end
	end

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

		def start
			Command.reply { |reply|
				reply.allowed_actions = [:next]
				reply.command << FormTemplate.render("customer_picker")
			}.then { |response|
				CustomerInfoForm.new(@customer_repo).find_customer(response)
			}.then { |customer|
				AdminCommand.for(customer, @customer_repo, @admin_action_repo)
					.then(&:start)
			}
		end
	end

	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
		@target_customer.admin_info.then { |info|
			reply(info.form)
		}.then { menu_or_done }
	end

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

	def menu_or_done(command_action=:execute)
		return Command.finish("Done") if command_action == :complete

		reply(FormTemplate.render("admin_menu")).then do |response|
			if response.form.field("action")
				handle(response.form.field("action").value, response.action)
			end
		end
	end

	def handle(action, command_action)
		if respond_to?("action_#{action}")
			send("action_#{action}")
		else
			new_context(action)
		end.then { menu_or_done(command_action) }
	end

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

	def action_info
		# Refresh the data
		new_context(@target_customer.customer_id)
	end

	def action_bill_plan
		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
		end

		def call(customer_id, customer_repo:, **)
			@klass.call(
				customer_id,
				reply: method(:reply),
				customer_repo: customer_repo
			)
		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
	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)],
		[:undo, Undoable.new(Undo)]
	].each do |action, handler|
		define_method("action_#{action}") do
			handler.call(
				@target_customer,
				admin_action_repo: @admin_action_repo,
				customer_repo: @customer_repo
			)
		end
	end
end