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
|
# frozen_string_literal: true
require_relative './helpers'
module InternalEventsCli
class EventDefiner
include Helpers
STEPS = [
'New Event',
'Description',
'Name',
'Context',
'URL',
'Group',
'Tiers',
'Save files'
].freeze
IDENTIFIER_OPTIONS = {
%w[project namespace user] => 'Use case: For project-level user actions ' \
'(ex - issue_assignee_changed) [MOST COMMON]',
%w[namespace user] => 'Use case: For namespace-level user actions (ex - epic_assigned_to_milestone)',
%w[user] => 'Use case: For user-only actions (ex - admin_impersonated_user)',
%w[project namespace] => 'Use case: For project-level events without user interaction ' \
'(ex - service_desk_request_received)',
%w[namespace] => 'Use case: For namespace-level events without user interaction ' \
'(ex - stale_runners_cleaned_up)',
%w[] => "Use case: For instance-level events without user interaction [LEAST COMMON]"
}.freeze
IDENTIFIER_FORMATTING_BUFFER = "[#{IDENTIFIER_OPTIONS.keys.max_by(&:length).join(', ')}]".length
attr_reader :cli, :event
def initialize(cli)
@cli = cli
@event = Event.new(milestone: MILESTONE)
end
def run
prompt_for_description
prompt_for_action
prompt_for_identifiers
prompt_for_url
prompt_for_product_ownership
prompt_for_tier
outcome = create_event_file
display_result(outcome)
prompt_for_next_steps
end
private
def prompt_for_description
new_page!(1, 7, STEPS)
cli.say Text::EVENT_DESCRIPTION_INTRO
event.description = cli.ask("Describe what the event tracks: #{input_required_text}", **input_opts) do |q|
q.required true
q.modify :trim
q.messages[:required?] = Text::EVENT_DESCRIPTION_HELP
end
end
def prompt_for_action
new_page!(2, 7, STEPS)
cli.say Text::EVENT_ACTION_INTRO
event.action = cli.ask("Define the event name: #{input_required_text}", **input_opts) do |q|
q.required true
q.validate ->(input) { input =~ /\A[a-z1-9_]+\z/ && !events_by_filepath.values.map(&:action).include?(input) } # rubocop:disable Rails/NegateInclude -- Not rails
q.modify :trim
q.messages[:valid?] = format_warning("Invalid event name. Only lowercase/numbers/underscores allowed. " \
"Ensure %{value} is not an existing event.")
q.messages[:required?] = Text::EVENT_ACTION_HELP
end
end
def prompt_for_identifiers
new_page!(3, 7, STEPS)
cli.say Text::EVENT_IDENTIFIERS_INTRO % event.action
identifiers = prompt_for_array_selection(
'Which identifiers are available when the event occurs?',
IDENTIFIER_OPTIONS.keys
) { |choice| format_identifier_choice(choice) }
event.identifiers = identifiers if identifiers.any?
end
def format_identifier_choice(choice)
formatted_choice = choice.empty? ? 'None' : "[#{choice.sort.join(', ')}]"
buffer = IDENTIFIER_FORMATTING_BUFFER - formatted_choice.length
"#{formatted_choice}#{' ' * buffer} -- #{IDENTIFIER_OPTIONS[choice]}"
end
def prompt_for_url
new_page!(4, 7, STEPS)
event.introduced_by_url = prompt_for_text('Which MR URL will merge the event definition?')
end
def prompt_for_product_ownership
new_page!(5, 7, STEPS)
ownership = prompt_for_group_ownership({
product_section: 'Which section will own the event?',
product_stage: 'Which stage will own the event?',
product_group: 'Which group will own the event?'
})
event.bulk_assign(ownership)
end
def prompt_for_tier
new_page!(6, 7, STEPS)
event.tiers = prompt_for_array_selection(
'Which tiers will the event be recorded on?',
[%w[free premium ultimate], %w[premium ultimate], %w[ultimate]]
)
event.distributions = event.tiers.include?('free') ? %w[ce ee] : %w[ee]
end
def create_event_file
new_page!(7, 7, STEPS)
prompt_to_save_file(event.file_path, event.formatted_output)
end
def display_result(outcome)
new_page!
cli.say <<~TEXT
#{divider}
#{format_info('Done with event definition!')}
#{outcome || ' No files saved.'}
#{divider}
Want to have data reported in Snowplow/Sisense/ServicePing? Add a new metric for your event!
TEXT
end
def prompt_for_next_steps
next_step = cli.select("How would you like to proceed?", **select_opts) do |menu|
menu.enum "."
if File.exist?(event.file_path)
menu.choice "Create Metric -- define a new metric using #{event.action}.yml", :add_metric
else
menu.choice "Save & Create Metric -- save #{event.action}.yml and define a matching metric", :save_and_add
end
menu.choice "View Usage -- look at code examples for #{event.action}.yml", :view_usage
menu.choice 'Exit', :exit
end
case next_step
when :add_metric
MetricDefiner.new(cli, event.file_path).run
when :save_and_add
write_to_file(event.file_path, event.formatted_output, 'create')
MetricDefiner.new(cli, event.file_path).run
when :view_usage
UsageViewer.new(cli, event.file_path, event).run
when :exit
cli.say Text::FEEDBACK_NOTICE
end
end
end
end
|