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,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sfctl"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib_path = File.expand_path('../lib', __dir__)
5
+ $:.unshift(lib_path) if !$:.include?(lib_path)
6
+ require 'sfctl/cli'
7
+
8
+ Signal.trap('INT') do
9
+ warn("\n#{caller.join("\n")}: interrupted")
10
+ exit(1)
11
+ end
12
+
13
+ begin
14
+ Sfctl::CLI.start
15
+ rescue Sfctl::CLI::Error => err
16
+ puts "ERROR: #{err.message}"
17
+ exit 1
18
+ end
@@ -0,0 +1,5 @@
1
+ require 'sfctl/version'
2
+
3
+ module Sfctl
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require 'pastel'
5
+ require 'tty-font'
6
+
7
+ module Sfctl
8
+ # Handle the application command line parsing
9
+ # and the dispatch to various command objects
10
+ #
11
+ # @api public
12
+ class CLI < Thor
13
+ # Error raised by this runner
14
+ Error = Class.new(StandardError)
15
+
16
+ def help(*args)
17
+ font = TTY::Font.new(:standard)
18
+ pastel = Pastel.new(enabled: !options['no-color'])
19
+ puts pastel.yellow(font.write('sfctl'))
20
+ super
21
+ end
22
+
23
+ class_option :"no-color", type: :boolean, default: false, desc: 'Disable colorization in output'
24
+ class_option :"starfish-host", type: :string, default: 'https://starfish.team',
25
+ desc: 'The starfish API endpoint',
26
+ banner: 'HOST'
27
+
28
+ desc 'version', 'sfctl version'
29
+ def version
30
+ require_relative 'version'
31
+ puts "v#{Sfctl::VERSION}"
32
+ end
33
+ map %w[--version -v] => :version
34
+
35
+ require_relative 'commands/time'
36
+ register Sfctl::Commands::Time, 'time', 'time [SUBCOMMAND]', 'Time reports'
37
+
38
+ require_relative 'commands/account'
39
+ register Sfctl::Commands::Account, 'account', 'account [SUBCOMMAND]', 'Account information for Starfish.team'
40
+
41
+ require_relative 'commands/auth'
42
+ register Sfctl::Commands::Auth, 'auth', 'auth [SUBCOMMAND]', 'Authentication with Starfish.team'
43
+ end
44
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'tty-config'
5
+
6
+ module Sfctl
7
+ class Command
8
+ extend Forwardable
9
+
10
+ CONFIG_FILENAME = '.sfctl'
11
+ CONFIG_PATH = "#{Dir.home}/#{CONFIG_FILENAME}"
12
+ LINK_CONFIG_FILENAME = '.sflink'
13
+ LINK_CONFIG_PATH = "#{Dir.home}/#{LINK_CONFIG_FILENAME}"
14
+
15
+ TOGGL_PROVIDER = 'toggl'
16
+ PROVIDERS_LIST = [
17
+ TOGGL_PROVIDER
18
+ ].freeze
19
+
20
+ def_delegators :command, :run
21
+
22
+ # Main configuration
23
+ # @api public
24
+ def config
25
+ @config ||= begin
26
+ config = TTY::Config.new
27
+ config.append_path Dir.home
28
+ config
29
+ end
30
+ end
31
+
32
+ def save_config!
33
+ config.write(CONFIG_PATH, format: :yaml, force: true)
34
+ end
35
+
36
+ def read_config
37
+ config.read(CONFIG_PATH, format: :yaml)
38
+ end
39
+
40
+ def access_token
41
+ read_config['access_token']
42
+ end
43
+
44
+ def config_present?(output)
45
+ read_config
46
+ rescue TTY::Config::ReadError
47
+ output.puts Pastel.new(enabled: !@options['no-color']).red('Please authentificate before continue.')
48
+ false
49
+ end
50
+
51
+ def read_link_config
52
+ config.read(LINK_CONFIG_PATH, format: :yaml)
53
+ end
54
+
55
+ def save_link_config!
56
+ config.write(LINK_CONFIG_PATH, format: :yaml, force: true)
57
+ end
58
+
59
+ def link_config_present?(output)
60
+ read_link_config
61
+ rescue TTY::Config::ReadError
62
+ output.puts Pastel.new(enabled: !@options['no-color']).red('Please initialize time before continue.')
63
+ false
64
+ end
65
+
66
+ # Execute this command
67
+ #
68
+ # @api public
69
+ def execute(*)
70
+ raise(
71
+ NotImplementedError,
72
+ "#{self.class}##{__method__} must be implemented"
73
+ )
74
+ end
75
+
76
+ # The external commands runner
77
+ #
78
+ # @see http://www.rubydoc.info/gems/tty-command
79
+ #
80
+ # @api public
81
+ def command(**options)
82
+ require 'tty-command'
83
+ TTY::Command.new(options)
84
+ end
85
+
86
+ # The cursor movement
87
+ #
88
+ # @see http://www.rubydoc.info/gems/tty-cursor
89
+ #
90
+ # @api public
91
+ # def cursor
92
+ # require 'tty-cursor'
93
+ # TTY::Cursor
94
+ # end
95
+
96
+ # Open a file or text in the user's preferred editor
97
+ #
98
+ # @see http://www.rubydoc.info/gems/tty-editor
99
+ #
100
+ # @api public
101
+ # def editor
102
+ # require 'tty-editor'
103
+ # TTY::Editor
104
+ # end
105
+
106
+ # Terminal output paging
107
+ #
108
+ # @see http://www.rubydoc.info/gems/tty-pager
109
+ #
110
+ # @api public
111
+ # def pager(**options)
112
+ # require 'tty-pager'
113
+ # TTY::Pager.new(options)
114
+ # end
115
+
116
+ # Terminal platform and OS properties
117
+ #
118
+ # @see http://www.rubydoc.info/gems/tty-pager
119
+ #
120
+ # @api public
121
+ # def platform
122
+ # require 'tty-platform'
123
+ # TTY::Platform.new
124
+ # end
125
+
126
+ # The interactive prompt
127
+ #
128
+ # @see http://www.rubydoc.info/gems/tty-prompt
129
+ #
130
+ # @api public
131
+ # def prompt(**options)
132
+ # require 'tty-prompt'
133
+ # TTY::Prompt.new(options)
134
+ # end
135
+
136
+ # Get terminal screen properties
137
+ #
138
+ # @see http://www.rubydoc.info/gems/tty-screen
139
+ #
140
+ # @api public
141
+ # def screen
142
+ # require 'tty-screen'
143
+ # TTY::Screen
144
+ # end
145
+
146
+ # The unix which utility
147
+ #
148
+ # @see http://www.rubydoc.info/gems/tty-which
149
+ #
150
+ # @api public
151
+ # def which(*args)
152
+ # require 'tty-which'
153
+ # TTY::Which.which(*args)
154
+ # end
155
+
156
+ # Check if executable exists
157
+ #
158
+ # @see http://www.rubydoc.info/gems/tty-which
159
+ #
160
+ # @api public
161
+ # def exec_exist?(*args)
162
+ # require 'tty-which'
163
+ # TTY::Which.exist?(*args)
164
+ # end
165
+ end
166
+ end
@@ -0,0 +1,34 @@
1
+ require 'thor'
2
+
3
+ module Sfctl
4
+ module Commands
5
+ class Account < Thor
6
+ namespace :account
7
+
8
+ desc 'assignments', 'This command will list all of your assignments that are currently active.'
9
+ method_option :help, aliases: '-h', type: :boolean,
10
+ desc: 'Display usage information'
11
+ method_option :all, aliases: '-a', type: :boolean, default: false,
12
+ desc: 'If you want to read all assignments you have to provide this flag'
13
+ def assignments(*)
14
+ if options[:help]
15
+ invoke :help, ['assignments']
16
+ else
17
+ require_relative 'account/assignments'
18
+ Sfctl::Commands::Account::Assignments.new(options).execute
19
+ end
20
+ end
21
+
22
+ desc 'info', 'This will read your profile data and give you an overview of your account.'
23
+ method_option :help, aliases: '-h', type: :boolean
24
+ def info(*)
25
+ if options[:help]
26
+ invoke :help, ['info']
27
+ else
28
+ require_relative 'account/info'
29
+ Sfctl::Commands::Account::Info.new(options).execute
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ require 'pastel'
2
+ require 'tty-table'
3
+ require_relative '../../command'
4
+ require_relative '../../starfish'
5
+
6
+ module Sfctl
7
+ module Commands
8
+ class Account
9
+ class Assignments < 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
+ return unless config_present?(output)
17
+
18
+ success, data = Starfish.account_assignments(@options['starfish-host'], @options['all'], access_token)
19
+
20
+ unless success
21
+ output.puts @pastel.red('Something went wrong. Unable to fetch assignments')
22
+ return
23
+ end
24
+
25
+ print_assignments(data['assignments'], output)
26
+ end
27
+
28
+ private
29
+
30
+ def rows(assignment)
31
+ [[
32
+ <<~HEREDOC
33
+ Service: #{assignment['service']}
34
+ Start: #{assignment['start_date']}
35
+ End: #{assignment['end_date']}
36
+ Budget: #{assignment['budget']} #{assignment['unit']}
37
+ HEREDOC
38
+ ]]
39
+ end
40
+
41
+ def print_assignments(assignments, output)
42
+ assignments.each do |assignment|
43
+ header = ["Assignment: #{assignment['name']}"]
44
+ table = ::TTY::Table.new header: header, rows: rows(assignment)
45
+ output.print table.render(:unicode, padding: [0, 1], multiline: true)
46
+ output.puts
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,40 @@
1
+ require 'pastel'
2
+ require 'tty-table'
3
+ require_relative '../../command'
4
+ require_relative '../../starfish'
5
+
6
+ module Sfctl
7
+ module Commands
8
+ class Account
9
+ class Info < 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
+ return unless config_present?(output)
17
+
18
+ success, info = Starfish.account_info(@options['starfish-host'], access_token)
19
+
20
+ unless success
21
+ output.puts @pastel.red('Something went wrong. Unable to fetch account info')
22
+ return
23
+ end
24
+
25
+ print_table(info, output)
26
+ end
27
+
28
+ private
29
+
30
+ def print_table(info, output)
31
+ header = info.keys
32
+ rows = [info.values]
33
+ table = ::TTY::Table.new header: header, rows: rows
34
+ output.print table.render(:unicode, padding: [0, 1])
35
+ output.puts
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'thor'
2
+
3
+ module Sfctl
4
+ module Commands
5
+ class Auth < Thor
6
+ namespace :auth
7
+
8
+ desc 'bye', 'Log out by either removing the config file.'
9
+ method_option :help, aliases: '-h', type: :boolean, desc: '...'
10
+ def bye(*)
11
+ if options[:help]
12
+ invoke :help, ['bye']
13
+ else
14
+ require_relative 'auth/bye'
15
+ Sfctl::Commands::Auth::Bye.new(options).execute
16
+ end
17
+ end
18
+
19
+ desc 'init [TOKEN]', 'Authenticate with Starfish.team'
20
+ long_desc <<~HEREDOC
21
+ Before you can use sfctl, you need to authenticate with Starfish.team by providing an access token,
22
+ which can be created on the profile page of your account.
23
+ HEREDOC
24
+ method_option :help, aliases: '-h', type: :boolean
25
+ def init(access_token)
26
+ if options[:help]
27
+ invoke :help, ['init']
28
+ else
29
+ require_relative 'auth/init'
30
+ Sfctl::Commands::Auth::Init.new(access_token, options).execute
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require 'tty-prompt'
5
+ require 'tty-file'
6
+
7
+ module Sfctl
8
+ module Commands
9
+ class Auth
10
+ class Bye < Sfctl::Command
11
+ def initialize(*); end
12
+
13
+ def execute(*)
14
+ prompt = ::TTY::Prompt.new
15
+ reset_config! if prompt.yes?('Are you sure?')
16
+ end
17
+
18
+ private
19
+
20
+ def reset_config!
21
+ ::TTY::File.remove_file CONFIG_PATH
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end