gitlab-ce/scripts/internal_events/cli/usage_viewer.rb

387 lines
11 KiB
Ruby
Executable File

# frozen_string_literal: true
require_relative './helpers'
module InternalEventsCli
class UsageViewer
include Helpers
IDENTIFIER_EXAMPLES = {
%w[namespace project user] => { "namespace" => "project.namespace" },
%w[namespace user] => { "namespace" => "group" }
}.freeze
PROPERTY_EXAMPLES = {
'label' => "'string'",
'property' => "'string'",
'value' => '72'
}.freeze
attr_reader :cli, :event
def initialize(cli, event_path = nil, event = nil)
@cli = cli
@event = event
@selected_event_path = event_path
end
def run
prompt_for_eligible_event
prompt_for_usage_location
end
def prompt_for_eligible_event
return if event
event_details = events_by_filepath
@selected_event_path = cli.select(
"Show examples for which event?",
get_event_options(event_details),
**select_opts,
**filter_opts
)
@event = event_details[@selected_event_path]
end
def prompt_for_usage_location(default = 'ruby/rails')
choices = [
{ name: 'ruby/rails', value: :rails },
{ name: 'rspec', value: :rspec },
{ name: 'javascript (vue)', value: :vue },
{ name: 'javascript (plain)', value: :js },
{ name: 'vue template', value: :vue_template },
{ name: 'haml', value: :haml },
{ name: 'Manual testing in GDK', value: :gdk },
{ name: 'View examples for a different event', value: :other_event },
{ name: 'Exit', value: :exit }
]
usage_location = cli.select(
'Select a use-case to view examples for:',
choices,
**select_opts,
per_page: 10
) do |menu|
menu.enum '.'
menu.default default
end
case usage_location
when :rails
rails_examples
prompt_for_usage_location('ruby/rails')
when :rspec
rspec_examples
prompt_for_usage_location('rspec')
when :haml
haml_examples
prompt_for_usage_location('haml')
when :js
js_examples
prompt_for_usage_location('javascript (plain)')
when :vue
vue_examples
prompt_for_usage_location('javascript (vue)')
when :vue_template
vue_template_examples
prompt_for_usage_location('vue template')
when :gdk
gdk_examples
prompt_for_usage_location('Manual testing in GDK')
when :other_event
self.class.new(cli).run
when :exit
cli.say(Text::FEEDBACK_NOTICE)
end
end
def rails_examples
identifier_args = identifiers.map do |identifier|
" #{identifier}: #{identifier_examples[identifier]}"
end
property_args = format_additional_properties do |property, value, description|
" #{property}: #{value}, # #{description}"
end
if property_args.any?
# remove trailing comma after last arg but keep any other commas
property_args.last.sub!(',', '')
property_arg = " additional_properties: {\n#{property_args.join("\n")}\n }"
end
args = ["'#{action}'", *identifier_args, property_arg].compact.join(",\n")
args = "\n #{args}\n" if args.lines.count > 1
cli.say format_warning <<~TEXT
#{divider}
#{format_help('# RAILS')}
include Gitlab::InternalEventsTracking
track_internal_event(#{args})
#{divider}
TEXT
end
def rspec_examples
identifier_args = identifiers.map do |identifier|
" let(:#{identifier}) { #{identifier_examples[identifier]} }\n"
end.join('')
property_args = format_additional_properties do |property, value, _|
" let(:#{property}) { #{value} }\n"
end.join('')
args = [*identifier_args, *property_args].join('')
cli.say format_warning <<~TEXT
#{divider}
#{format_help('# RSPEC')}
it_behaves_like 'internal event tracking' do
let(:event) { '#{action}' }
#{args}end
#{divider}
TEXT
end
def haml_examples
property_args = format_additional_properties do |property, value, _|
"event_#{property}: #{value}"
end
args = ["event_tracking: '#{action}'", *property_args].join(', ')
cli.say <<~TEXT
#{divider}
#{format_help('# HAML -- ON-CLICK')}
.inline-block{ #{format_warning("data: { #{args} }")} }
= _('Important Text')
#{divider}
#{format_help('# HAML -- COMPONENT ON-CLICK')}
= render Pajamas::ButtonComponent.new(button_options: { #{format_warning("data: { #{args} }")} })
#{divider}
#{format_help('# HAML -- COMPONENT ON-LOAD')}
= render Pajamas::ButtonComponent.new(button_options: { #{format_warning("data: { event_tracking_load: true, #{args} }")} })
#{divider}
TEXT
cli.say("Want to see the implementation details? See app/assets/javascripts/tracking/internal_events.js\n\n")
end
def vue_template_examples
on_click_args = template_formatted_args('data-event-tracking', indent: 2)
on_load_args = template_formatted_args('data-event-tracking-load', indent: 2)
cli.say <<~TEXT
#{divider}
#{format_help('// VUE TEMPLATE -- ON-CLICK')}
<script>
import { GlButton } from '@gitlab/ui';
export default {
components: { GlButton }
};
</script>
<template>
<gl-button#{on_click_args}
Click Me
</gl-button>
</template>
#{divider}
#{format_help('// VUE TEMPLATE -- ON-LOAD')}
<script>
import { GlButton } from '@gitlab/ui';
export default {
components: { GlButton }
};
</script>
<template>
<gl-button#{on_load_args}
Click Me
</gl-button>
</template>
#{divider}
TEXT
cli.say("Want to see the implementation details? See app/assets/javascripts/tracking/internal_events.js\n\n")
end
def js_examples
args = js_formatted_args(indent: 2)
cli.say <<~TEXT
#{divider}
#{format_help('// FRONTEND -- RAW JAVASCRIPT')}
#{format_warning("import { InternalEvents } from '~/tracking';")}
export const performAction = () => {
#{format_warning("InternalEvents.trackEvent#{args}")}
return true;
};
#{divider}
TEXT
# https://docs.snowplow.io/docs/understanding-your-pipeline/schemas/
cli.say("Want to see the implementation details? See app/assets/javascripts/tracking/internal_events.js\n\n")
end
def vue_examples
args = js_formatted_args(indent: 6)
cli.say <<~TEXT
#{divider}
#{format_help('// VUE')}
<script>
#{format_warning("import { InternalEvents } from '~/tracking';")}
import { GlButton } from '@gitlab/ui';
#{format_warning('const trackingMixin = InternalEvents.mixin();')}
export default {
#{format_warning('mixins: [trackingMixin]')},
components: { GlButton },
methods: {
performAction() {
#{format_warning("this.trackEvent#{args}")}
},
},
};
</script>
<template>
<gl-button @click=performAction>Click Me</gl-button>
</template>
#{divider}
TEXT
cli.say("Want to see the implementation details? See app/assets/javascripts/tracking/internal_events.js\n\n")
end
private
def action
event['action']
end
def identifiers
Array(event['identifiers'])
end
def additional_properties
Array(event['additional_properties'])
end
def identifier_examples
identifiers
.to_h { |identifier| [identifier, identifier] }
.merge(IDENTIFIER_EXAMPLES[identifiers.sort] || {})
end
def format_additional_properties
additional_properties.map do |property, details|
example_value = PROPERTY_EXAMPLES[property]
description = details['description'] || 'TODO'
yield(property, example_value, description)
end
end
def js_formatted_args(indent:)
return "('#{action}');" if additional_properties.none?
property_args = format_additional_properties do |property, value, description|
" #{property}: #{value}, // #{description}"
end
[
'(',
" '#{action}',",
' {',
*property_args,
' },',
');'
].join("\n#{' ' * indent}")
end
def template_formatted_args(data_attr, indent:)
return " #{data_attr}=\"#{action}\">" if additional_properties.none?
spacer = ' ' * indent
property_args = format_additional_properties do |property, value, _|
" data-event-#{property}=#{value.tr("'", '"')}"
end
args = [
'', # start args on next line
" #{data_attr}=\"#{action}\"",
*property_args
].join("\n#{spacer}")
"#{format_warning(args)}\n#{spacer}>"
end
def gdk_examples
key_paths = get_existing_metrics_for_events([event]).map(&:key_path)
cli.say <<~TEXT
#{divider}
#{format_help('# TERMINAL -- monitor events sent to snowplow & changes to service ping metrics as they occur')}
1. Configure gdk with snowplow micro https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/snowplow_micro.md
2. From `gitlab/` directory, run the monitor script:
#{format_warning("bin/rails runner scripts/internal_events/monitor.rb #{event.action}")}
3. View all snowplow events in the browser at http://localhost:9091/micro/all (or whichever hostname & port you configured)
#{divider}
#{format_help('# RAILS CONSOLE -- generate service ping payload, including most recent usage data')}
#{format_warning("require_relative 'spec/support/helpers/service_ping_helpers.rb'")}
#{format_help('# Get current value of a metric')}
#{
if key_paths.any?
key_paths.map { |key_path| format_warning("ServicePingHelpers.get_current_usage_metric_value('#{key_path}')") }.join("\n")
else
format_help("# Warning: There are no metrics for #{event.action} yet. When there are, replace <key_path> below.\n") +
format_warning('ServicePingHelpers.get_current_usage_metric_value(<key_path>)')
end
}
#{format_help('# View entire service ping payload')}
#{format_warning('ServicePingHelpers.get_current_service_ping_payload')}
#{divider}
Need to test something else? Check these docs:
- https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/local_setup_and_debugging.html
- https://docs.gitlab.com/ee/development/internal_analytics/service_ping/troubleshooting.html
- https://docs.gitlab.com/ee/development/internal_analytics/review_guidelines.html
TEXT
end
end
end