sfctl 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require_relative '../../starfish'
5
+ require 'pastel'
6
+ require 'tty-spinner'
7
+
8
+ module Sfctl
9
+ module Commands
10
+ class Auth
11
+ class Init < Sfctl::Command
12
+ def initialize(access_token, options)
13
+ @access_token = access_token
14
+ @options = options
15
+
16
+ @pastel = Pastel.new(enabled: !@options['no-color'])
17
+ end
18
+
19
+ def execute(output: $stdout)
20
+ spinner = ::TTY::Spinner.new('[:spinner] Checking token ...')
21
+ spinner.auto_spin
22
+ token_valid? ? update_config!(spinner, output) : render_error(spinner, output)
23
+ end
24
+
25
+ private
26
+
27
+ def token_valid?
28
+ Starfish.check_authorization(@options['starfish-host'], @access_token)
29
+ end
30
+
31
+ def token_accepted_message
32
+ @pastel.green('Credentials are accepted.')
33
+ end
34
+
35
+ def wrong_token_message
36
+ @pastel.red('Token is not accepted, please make sure you copy and paste it correctly.')
37
+ end
38
+
39
+ def update_config!(spinner, output)
40
+ config.set :access_token, value: @access_token
41
+ save_config!
42
+ spinner.success
43
+ output.puts token_accepted_message
44
+ end
45
+
46
+ def render_error(spinner, output)
47
+ spinner.error
48
+ output.puts wrong_token_message
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ require 'thor'
2
+
3
+ module Sfctl
4
+ module Commands
5
+ class Time < Thor
6
+ namespace :time
7
+
8
+ desc 'sync', 'Synchronize data with providers.'
9
+ method_option :help, aliases: '-h', type: :boolean,
10
+ desc: 'Display usage information'
11
+ method_option :dry_run, aliases: '-dry-run', type: :boolean, default: false,
12
+ desc: 'Check the data first respectively prevent data from being overwritten'
13
+ method_option :touchy, aliases: '-touchy', type: :boolean, default: false,
14
+ desc: 'The synchronizsation will be skipped if there is preexisting data.'
15
+ long_desc <<~HEREDOC
16
+ It will gets for each assignment the next reporting segment from starfish.team
17
+ and loads the corresponding time reports from the provider.
18
+ HEREDOC
19
+ def sync(*)
20
+ if options[:help]
21
+ invoke :help, ['sync']
22
+ else
23
+ require_relative 'time/sync'
24
+ Sfctl::Commands::Time::Sync.new(options).execute
25
+ end
26
+ end
27
+
28
+ desc 'init',
29
+ 'You can use the following command to create a .sflink file that will store your project configuration.'
30
+ long_desc <<~HEREDOC
31
+ You can use the following command to create a .sflink file that will store your project configuration.\n
32
+ Although sensitive data is stored in the main .sfctl directory
33
+ we'd like to recommend to not add the .sflink file to your version control system.
34
+ HEREDOC
35
+ method_option :help, aliases: '-h', type: :boolean
36
+ def init(*)
37
+ if options[:help]
38
+ invoke :help, ['init']
39
+ else
40
+ require_relative 'time/init'
41
+ Sfctl::Commands::Time::Init.new(options).execute
42
+ end
43
+ end
44
+
45
+ require_relative 'time/providers'
46
+ register Sfctl::Commands::Time::Providers, 'providers', 'providers [SUBCOMMAND]', 'Manage providers.'
47
+
48
+ require_relative 'time/connections'
49
+ register Sfctl::Commands::Time::Connections, 'connections', 'connections [SUBCOMMAND]', 'Manage connections.'
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ require 'thor'
2
+
3
+ module Sfctl
4
+ module Commands
5
+ class Time
6
+ class Connections < Thor
7
+ namespace :connections
8
+
9
+ desc 'add', 'This command will add a connection between a provider and an assignment.'
10
+ method_option :help, aliases: '-h', type: :boolean
11
+ def add(*)
12
+ if options[:help]
13
+ invoke :help, ['add']
14
+ else
15
+ require_relative 'connections/add'
16
+ Sfctl::Commands::Time::Connections::Add.new(options).execute
17
+ end
18
+ end
19
+
20
+ desc 'get', 'List all known connections in that project.'
21
+ method_option :help, aliases: '-h', type: :boolean
22
+ def get(*)
23
+ if options[:help]
24
+ invoke :help, ['get']
25
+ else
26
+ require_relative 'connections/get'
27
+ Sfctl::Commands::Time::Connections::Get.new(options).execute
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,84 @@
1
+ require 'pastel'
2
+ require 'tty-prompt'
3
+ require_relative '../../../command'
4
+ require_relative '../../../starfish'
5
+
6
+ module Sfctl
7
+ module Commands
8
+ class Time
9
+ class Connections
10
+ class Add < Sfctl::Command
11
+ def initialize(options)
12
+ @options = options
13
+ @pastel = Pastel.new(enabled: !@options['no-color'])
14
+ @prompt = ::TTY::Prompt.new
15
+ end
16
+
17
+ def execute(output: $stdout) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
18
+ return if !config_present?(output) || !link_config_present?(output)
19
+
20
+ ltoken = access_token
21
+ config.delete(:access_token)
22
+ success, data = Starfish.account_assignments(@options['starfish-host'], @options['all'], ltoken)
23
+ unless success
24
+ output.puts @pastel.red('Something went wrong. Unable to fetch assignments')
25
+ return
26
+ end
27
+
28
+ assignments = filter_assignments(data['assignments'])
29
+ if assignments.length.zero?
30
+ output.puts @pastel.yellow('All assignments already added.')
31
+ return
32
+ end
33
+
34
+ provider = @prompt.select('Select provider:', PROVIDERS_LIST)
35
+
36
+ assignment_name = select_assignment(assignments)
37
+
38
+ case provider
39
+ when TOGGL_PROVIDER
40
+ setup_toggl_connection!(assignment_name)
41
+ end
42
+
43
+ save_link_config!
44
+
45
+ output.puts @pastel.green('Connection successfully added.')
46
+ end
47
+
48
+ private
49
+
50
+ def select_assignment(assignments)
51
+ @prompt.select('Select assignment:') do |menu|
52
+ assignments.each.with_index do |asmnt, i|
53
+ menu.choice name: "#{i + 1}. #{asmnt['name']} / #{asmnt['service']}", value: asmnt['name']
54
+ end
55
+ end
56
+ end
57
+
58
+ def filter_assignments(list)
59
+ return list if config.fetch(:connections).nil?
60
+
61
+ added_assignments_name = config.fetch(:connections).keys
62
+ list.delete_if { |h| added_assignments_name.include?(h['name']) }
63
+ list
64
+ end
65
+
66
+ def setup_toggl_connection!(assignment_name)
67
+ workspace_id = @prompt.ask('Workspace ID (required):', required: true)
68
+ project_ids = @prompt.ask('Project IDs (required / comma separated):', required: true)
69
+ task_ids = @prompt.ask('Task IDs (optional / comma separated):') || ''
70
+ billable = @prompt.select('Billable? (required)', %w[yes no both])
71
+ rounding = @prompt.select('Rounding? (required)', %w[on off])
72
+
73
+ config.set("connections.#{assignment_name}.provider", value: TOGGL_PROVIDER)
74
+ config.set("connections.#{assignment_name}.workspace_id", value: workspace_id)
75
+ config.set("connections.#{assignment_name}.project_ids", value: project_ids)
76
+ config.set("connections.#{assignment_name}.task_ids", value: task_ids)
77
+ config.set("connections.#{assignment_name}.billable", value: billable)
78
+ config.set("connections.#{assignment_name}.rounding", value: rounding)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,53 @@
1
+ require 'pastel'
2
+ require_relative '../../../command'
3
+
4
+ module Sfctl
5
+ module Commands
6
+ class Time
7
+ class Connections
8
+ class Get < Sfctl::Command
9
+ def initialize(options)
10
+ @options = options
11
+ @pastel = Pastel.new(enabled: !@options['no-color'])
12
+ end
13
+
14
+ def execute(output: $stdout)
15
+ read_link_config
16
+
17
+ if config.fetch(:connections).nil?
18
+ output.puts @pastel.yellow('You have no connections. Please add them before continue.')
19
+ return
20
+ end
21
+
22
+ print_connections(output)
23
+ rescue TTY::Config::ReadError
24
+ error_message = 'Please initialize time before continue and ensure that your account authenticated.'
25
+ output.puts @pastel.yellow(error_message)
26
+ end
27
+
28
+ private
29
+
30
+ def print_connections(output)
31
+ config.fetch(:connections).each_key do |assignment_name|
32
+ case config.fetch(:connections, assignment_name, :provider)
33
+ when TOGGL_PROVIDER
34
+ print_toggl_connection!(output, assignment_name)
35
+ end
36
+ end
37
+ end
38
+
39
+ def print_toggl_connection!(output, assignment_name)
40
+ output.puts "Connection: #{assignment_name}"
41
+ output.puts " provider: #{TOGGL_PROVIDER}"
42
+ output.puts " workspace_id: #{config.fetch(:connections, assignment_name, :workspace_id)}"
43
+ output.puts " project_ids: #{config.fetch(:connections, assignment_name, :project_ids)}"
44
+ output.puts " task_ids: #{config.fetch(:connections, assignment_name, :task_ids)}"
45
+ output.puts " billable: #{config.fetch(:connections, assignment_name, :billable)}"
46
+ output.puts " rounding: #{config.fetch(:connections, assignment_name, :rounding)}"
47
+ output.puts
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ require_relative '../../command'
2
+
3
+ module Sfctl
4
+ module Commands
5
+ class Time
6
+ class Init < Sfctl::Command
7
+ def initialize(options)
8
+ @options = options
9
+
10
+ @pastel = Pastel.new(enabled: !@options['no-color'])
11
+ end
12
+
13
+ def execute(output: $stdout)
14
+ read_link_config
15
+ output.puts @pastel.yellow('.sflink is already created.')
16
+ rescue ::TTY::Config::ReadError
17
+ save_link_config!
18
+ output.puts @pastel.green('.sflink successfully created.')
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,44 @@
1
+ require 'thor'
2
+
3
+ module Sfctl
4
+ module Commands
5
+ class Time
6
+ class Providers < Thor
7
+ namespace :providers
8
+
9
+ desc 'set', 'Set the configuration required for the provider to authenticate a call to their API.'
10
+ method_option :help, aliases: '-h', type: :boolean
11
+ def set(*)
12
+ if options[:help]
13
+ invoke :help, ['set']
14
+ else
15
+ require_relative 'providers/set'
16
+ Sfctl::Commands::Time::Providers::Set.new(options).execute
17
+ end
18
+ end
19
+
20
+ desc 'unset', 'Unset the configuration of a provider.'
21
+ method_option :help, aliases: '-h', type: :boolean
22
+ def unset(*)
23
+ if options[:help]
24
+ invoke :help, ['unset']
25
+ else
26
+ require_relative 'providers/unset'
27
+ Sfctl::Commands::Time::Providers::Unset.new(options).execute
28
+ end
29
+ end
30
+
31
+ desc 'get', 'Read which providers are configured on your system.'
32
+ method_option :help, aliases: '-h', type: :boolean
33
+ def get(*)
34
+ if options[:help]
35
+ invoke :help, ['get']
36
+ else
37
+ require_relative 'providers/get'
38
+ Sfctl::Commands::Time::Providers::Get.new(options).execute
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,41 @@
1
+ require 'pastel'
2
+ require_relative '../../../command'
3
+
4
+ module Sfctl
5
+ module Commands
6
+ class Time
7
+ class Providers
8
+ class Get < Sfctl::Command
9
+ def initialize(options)
10
+ @options = options
11
+ @pastel = Pastel.new(enabled: !@options['no-color'])
12
+ end
13
+
14
+ def execute(output: $stdout)
15
+ read_link_config
16
+
17
+ PROVIDERS_LIST.each do |provider|
18
+ read(provider, output)
19
+ end
20
+ rescue TTY::Config::ReadError
21
+ output.puts @pastel.yellow('Please initialize time before continue.')
22
+ end
23
+
24
+ private
25
+
26
+ def read(provider, output)
27
+ info = config.fetch("providers.#{provider}")
28
+ if info.nil?
29
+ output.puts @pastel.yellow("Provider #{provider} is not set.")
30
+ else
31
+ output.puts "Provider: #{@pastel.cyan(provider)}"
32
+ info.each_key do |k|
33
+ output.puts " #{k.upcase}: #{info[k]}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ require 'pastel'
2
+ require 'tty-prompt'
3
+ require_relative '../../../command'
4
+
5
+ module Sfctl
6
+ module Commands
7
+ class Time
8
+ class Providers
9
+ class Set < Sfctl::Command
10
+ def initialize(options)
11
+ @options = options
12
+ @pastel = Pastel.new(enabled: !@options['no-color'])
13
+ end
14
+
15
+ def execute(output: $stdout)
16
+ read_link_config
17
+
18
+ prompt = ::TTY::Prompt.new
19
+ provider = prompt.select('Setting up:', PROVIDERS_LIST)
20
+
21
+ !ask_for_replace(output, prompt) && return unless config.fetch("providers.#{TOGGL_PROVIDER}").nil?
22
+
23
+ case provider
24
+ when TOGGL_PROVIDER
25
+ setup_toggl_provider!(output, prompt)
26
+ end
27
+ rescue TTY::Config::ReadError
28
+ output.puts @pastel.yellow('Please initialize time before continue.')
29
+ end
30
+
31
+ private
32
+
33
+ def ask_for_replace(output, prompt)
34
+ output.puts @pastel.yellow('You already have a configuration for this provider.')
35
+ prompt.yes?('Do you want to replace it?')
36
+ end
37
+
38
+ def save_toggl_config!(output, access_token)
39
+ config.set("providers.#{TOGGL_PROVIDER}.access_token", value: access_token)
40
+ save_link_config!
41
+ output.puts @pastel.green('Everything saved.')
42
+ end
43
+
44
+ def setup_toggl_provider!(output, prompt)
45
+ output.puts
46
+ access_token = prompt.ask("Your access token at [#{@pastel.green(TOGGL_PROVIDER)}]:", required: true)
47
+ is_correct = prompt.yes?('Is that information correct?')
48
+ if is_correct
49
+ save_toggl_config!(output, access_token)
50
+ else
51
+ setup_toggl_provider!(output, prompt)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end