pcli 0.1.0

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +13 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +12 -0
  5. data/Gemfile.lock +176 -0
  6. data/Rakefile +12 -0
  7. data/exe/pcli +6 -0
  8. data/lib/pcli/api_request.rb +60 -0
  9. data/lib/pcli/api_response.rb +82 -0
  10. data/lib/pcli/command_output.rb +23 -0
  11. data/lib/pcli/container.rb +54 -0
  12. data/lib/pcli/depends.rb +43 -0
  13. data/lib/pcli/main.rb +22 -0
  14. data/lib/pcli/options.rb +41 -0
  15. data/lib/pcli/output/padded.rb +32 -0
  16. data/lib/pcli/output/server_error.rb +24 -0
  17. data/lib/pcli/output.rb +6 -0
  18. data/lib/pcli/pl.rb +15 -0
  19. data/lib/pcli/services/all_commands.rb +52 -0
  20. data/lib/pcli/services/all_steps.rb +23 -0
  21. data/lib/pcli/services/api.rb +82 -0
  22. data/lib/pcli/services/api_manager.rb +28 -0
  23. data/lib/pcli/services/app.rb +31 -0
  24. data/lib/pcli/services/authenticate.rb +60 -0
  25. data/lib/pcli/services/client.rb +24 -0
  26. data/lib/pcli/services/cmd.rb +13 -0
  27. data/lib/pcli/services/commander.rb +38 -0
  28. data/lib/pcli/services/commands/login.rb +22 -0
  29. data/lib/pcli/services/commands/logout.rb +24 -0
  30. data/lib/pcli/services/commands/templates/change.rb +95 -0
  31. data/lib/pcli/services/commands/templates/list.rb +52 -0
  32. data/lib/pcli/services/commands/user/change.rb +114 -0
  33. data/lib/pcli/services/commands/user/password.rb +77 -0
  34. data/lib/pcli/services/commands/user/show.rb +47 -0
  35. data/lib/pcli/services/commands/user/totp.rb +78 -0
  36. data/lib/pcli/services/commands/users/change.rb +134 -0
  37. data/lib/pcli/services/commands/users/create.rb +67 -0
  38. data/lib/pcli/services/commands/users/list.rb +52 -0
  39. data/lib/pcli/services/commands/users/remove.rb +85 -0
  40. data/lib/pcli/services/prompt.rb +21 -0
  41. data/lib/pcli/services/qr.rb +18 -0
  42. data/lib/pcli/services/steps/authenticate.rb +17 -0
  43. data/lib/pcli/services/steps/connect.rb +35 -0
  44. data/lib/pcli/services/steps/greeting.rb +18 -0
  45. data/lib/pcli/services/steps/main.rb +51 -0
  46. data/lib/pcli/services/steps/valediction.rb +21 -0
  47. data/lib/pcli/simple_spinner_bar.rb +38 -0
  48. data/lib/pcli/step.rb +69 -0
  49. data/lib/pcli/util/cli.rb +32 -0
  50. data/lib/pcli/util/hash.rb +25 -0
  51. data/lib/pcli/version.rb +5 -0
  52. data/lib/pcli.rb +24 -0
  53. metadata +389 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: af2b54a95a2b2ae5b14f0d296eef6efc1b66a5e3ce49846f9a11a93d3082a386
4
+ data.tar.gz: fb20b79e673b7147bda3e338a1ab286c27892c6393797c1acf89b792f5d47369
5
+ SHA512:
6
+ metadata.gz: f1097c95cd45552bffaf25f6f0e8a1597a704d121cdeddd3c03ace1410930de6f61f27e4e46c042079640cc8f942e7f64d5eb848e2f3bd7199478595be36572d
7
+ data.tar.gz: bd92a2ee5cd06e2eff2feca4595abc742e1726a1734835f3067c15313664b99771a2ff2ed4e31fa67ee287136da1e9a892123b51e7bcc4d51cedcdb686e723d9
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: single_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: single_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-04-02
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in pcli.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 13.0'
9
+
10
+ gem 'rspec', '~> 3.0'
11
+
12
+ gem 'rubocop', '~> 1.21'
data/Gemfile.lock ADDED
@@ -0,0 +1,176 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pcli (0.1.0)
5
+ actionview (~> 7.0)
6
+ activesupport (~> 7.0)
7
+ dry-cli (~> 1.0)
8
+ http (~> 5.1)
9
+ pastel (~> 0.8.0)
10
+ rqrcode (~> 2.0)
11
+ tty-command (~> 0.10.1)
12
+ tty-cursor (~> 0.7.1)
13
+ tty-editor (~> 0.7.0)
14
+ tty-option (~> 0.2.0)
15
+ tty-progressbar (~> 0.18.2)
16
+ tty-prompt (~> 0.23.1)
17
+ tty-screen (~> 0.8.1)
18
+ tty-spinner (~> 0.9.3)
19
+ tty-table (~> 0.12.0)
20
+ tty-which (~> 0.5.0)
21
+ zeitwerk (~> 2.6)
22
+
23
+ GEM
24
+ remote: https://rubygems.org/
25
+ specs:
26
+ actionview (7.0.4.3)
27
+ activesupport (= 7.0.4.3)
28
+ builder (~> 3.1)
29
+ erubi (~> 1.4)
30
+ rails-dom-testing (~> 2.0)
31
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
32
+ activesupport (7.0.4.3)
33
+ concurrent-ruby (~> 1.0, >= 1.0.2)
34
+ i18n (>= 1.6, < 2)
35
+ minitest (>= 5.1)
36
+ tzinfo (~> 2.0)
37
+ addressable (2.8.2)
38
+ public_suffix (>= 2.0.2, < 6.0)
39
+ ast (2.4.2)
40
+ builder (3.2.4)
41
+ byebug (11.1.3)
42
+ chunky_png (1.4.0)
43
+ concurrent-ruby (1.2.2)
44
+ crass (1.0.6)
45
+ diff-lcs (1.5.0)
46
+ domain_name (0.5.20190701)
47
+ unf (>= 0.0.5, < 1.0.0)
48
+ dry-cli (1.0.0)
49
+ erubi (1.12.0)
50
+ ffi (1.15.5)
51
+ ffi-compiler (1.0.1)
52
+ ffi (>= 1.0.0)
53
+ rake
54
+ http (5.1.1)
55
+ addressable (~> 2.8)
56
+ http-cookie (~> 1.0)
57
+ http-form_data (~> 2.2)
58
+ llhttp-ffi (~> 0.4.0)
59
+ http-cookie (1.0.5)
60
+ domain_name (~> 0.5)
61
+ http-form_data (2.3.0)
62
+ i18n (1.12.0)
63
+ concurrent-ruby (~> 1.0)
64
+ json (2.6.3)
65
+ llhttp-ffi (0.4.0)
66
+ ffi-compiler (~> 1.0)
67
+ rake (~> 13.0)
68
+ loofah (2.20.0)
69
+ crass (~> 1.0.2)
70
+ nokogiri (>= 1.5.9)
71
+ minitest (5.18.0)
72
+ nokogiri (1.14.2-x86_64-linux)
73
+ racc (~> 1.4)
74
+ parallel (1.22.1)
75
+ parser (3.2.2.0)
76
+ ast (~> 2.4.1)
77
+ pastel (0.8.0)
78
+ tty-color (~> 0.5)
79
+ public_suffix (5.0.1)
80
+ racc (1.6.2)
81
+ rails-dom-testing (2.0.3)
82
+ activesupport (>= 4.2.0)
83
+ nokogiri (>= 1.6)
84
+ rails-html-sanitizer (1.5.0)
85
+ loofah (~> 2.19, >= 2.19.1)
86
+ rainbow (3.1.1)
87
+ rake (13.0.6)
88
+ regexp_parser (2.7.0)
89
+ rexml (3.2.5)
90
+ rqrcode (2.1.2)
91
+ chunky_png (~> 1.0)
92
+ rqrcode_core (~> 1.0)
93
+ rqrcode_core (1.2.0)
94
+ rspec (3.12.0)
95
+ rspec-core (~> 3.12.0)
96
+ rspec-expectations (~> 3.12.0)
97
+ rspec-mocks (~> 3.12.0)
98
+ rspec-core (3.12.1)
99
+ rspec-support (~> 3.12.0)
100
+ rspec-expectations (3.12.2)
101
+ diff-lcs (>= 1.2.0, < 2.0)
102
+ rspec-support (~> 3.12.0)
103
+ rspec-mocks (3.12.5)
104
+ diff-lcs (>= 1.2.0, < 2.0)
105
+ rspec-support (~> 3.12.0)
106
+ rspec-support (3.12.0)
107
+ rubocop (1.49.0)
108
+ json (~> 2.3)
109
+ parallel (~> 1.10)
110
+ parser (>= 3.2.0.0)
111
+ rainbow (>= 2.2.2, < 4.0)
112
+ regexp_parser (>= 1.8, < 3.0)
113
+ rexml (>= 3.2.5, < 4.0)
114
+ rubocop-ast (>= 1.28.0, < 2.0)
115
+ ruby-progressbar (~> 1.7)
116
+ unicode-display_width (>= 2.4.0, < 3.0)
117
+ rubocop-ast (1.28.0)
118
+ parser (>= 3.2.1.0)
119
+ rubocop-rake (0.6.0)
120
+ rubocop (~> 1.0)
121
+ ruby-progressbar (1.13.0)
122
+ strings (0.2.1)
123
+ strings-ansi (~> 0.2)
124
+ unicode-display_width (>= 1.5, < 3.0)
125
+ unicode_utils (~> 1.4)
126
+ strings-ansi (0.2.0)
127
+ tty-color (0.6.0)
128
+ tty-command (0.10.1)
129
+ pastel (~> 0.8)
130
+ tty-cursor (0.7.1)
131
+ tty-editor (0.7.0)
132
+ tty-prompt (~> 0.22)
133
+ tty-option (0.2.0)
134
+ tty-progressbar (0.18.2)
135
+ strings-ansi (~> 0.2)
136
+ tty-cursor (~> 0.7)
137
+ tty-screen (~> 0.8)
138
+ unicode-display_width (>= 1.6, < 3.0)
139
+ tty-prompt (0.23.1)
140
+ pastel (~> 0.8)
141
+ tty-reader (~> 0.8)
142
+ tty-reader (0.9.0)
143
+ tty-cursor (~> 0.7)
144
+ tty-screen (~> 0.8)
145
+ wisper (~> 2.0)
146
+ tty-screen (0.8.1)
147
+ tty-spinner (0.9.3)
148
+ tty-cursor (~> 0.7)
149
+ tty-table (0.12.0)
150
+ pastel (~> 0.8)
151
+ strings (~> 0.2.0)
152
+ tty-screen (~> 0.8)
153
+ tty-which (0.5.0)
154
+ tzinfo (2.0.6)
155
+ concurrent-ruby (~> 1.0)
156
+ unf (0.1.4)
157
+ unf_ext
158
+ unf_ext (0.0.8.2)
159
+ unicode-display_width (2.4.2)
160
+ unicode_utils (1.4.0)
161
+ wisper (2.0.1)
162
+ zeitwerk (2.6.8)
163
+
164
+ PLATFORMS
165
+ x86_64-linux
166
+
167
+ DEPENDENCIES
168
+ byebug (~> 11.1)
169
+ pcli!
170
+ rake (~> 13.0)
171
+ rspec (~> 3.0)
172
+ rubocop (~> 1.21)
173
+ rubocop-rake (~> 0.6.0)
174
+
175
+ BUNDLED WITH
176
+ 2.3.22
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/exe/pcli ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'pcli'
5
+
6
+ Pcli::Main.new.run(ARGV)
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ class ApiRequest
5
+ def initialize(path)
6
+ @path = path
7
+ @method = 'get'
8
+ @params = {}
9
+ @headers = { 'Content-Type': 'application/json' }
10
+ end
11
+
12
+ def method(value = nil)
13
+ if value
14
+ @method = value
15
+ self
16
+ else
17
+ @method
18
+ end
19
+ end
20
+
21
+ def path(value = nil)
22
+ if value
23
+ @path = value
24
+ self
25
+ else
26
+ @path
27
+ end
28
+ end
29
+
30
+ def params(value = nil)
31
+ if value
32
+ @params = @params.merge(value)
33
+ self
34
+ else
35
+ @params
36
+ end
37
+ end
38
+
39
+ def param(key, value)
40
+ @params[key] = value
41
+ self
42
+ end
43
+
44
+ def headers(value = nil)
45
+ if value
46
+ @headers = @headers.merge(value)
47
+ self
48
+ else
49
+ @headers
50
+ end
51
+ end
52
+
53
+ def header(key, value)
54
+ @headers[key] = value
55
+ self
56
+ end
57
+
58
+ attr_writer :path, :method, :params, :headers
59
+ end
60
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ class ApiResponse
5
+ KnownError = Struct.new(:status, :type, :title, :message)
6
+
7
+ attr_reader :code, :status, :body
8
+
9
+ def initialize(code, success_status, status, body)
10
+ @code = code
11
+ @success_status = success_status
12
+ @status = status
13
+ @body = body
14
+ end
15
+
16
+ def json
17
+ if @json.nil?
18
+ begin
19
+ @json = JSON.parse(body)
20
+ rescue JSON::ParserError
21
+ @json = false
22
+ end
23
+ end
24
+
25
+ @json
26
+ end
27
+
28
+ def state
29
+ @state ||= calculate_state
30
+ end
31
+
32
+ def success?
33
+ state == :success
34
+ end
35
+
36
+ def known_error?
37
+ state == :error
38
+ end
39
+
40
+ def unknown_error?
41
+ state == :unknown_error
42
+ end
43
+
44
+ def failure?
45
+ !success?
46
+ end
47
+
48
+ def error
49
+ return @error unless @error.nil?
50
+
51
+ return @error = false unless state == :error
52
+
53
+ @error = KnownError.new(
54
+ json['status'],
55
+ json.dig('error', 'type'),
56
+ json.dig('error', 'title'),
57
+ json.dig('error', 'message')
58
+ )
59
+ end
60
+
61
+ private
62
+
63
+ def valid_error_schema
64
+ Util::Hash.has_keys?(json, 'error', 'status') &&
65
+ Util::Hash.has_keys?(json['error'], 'type', 'title', 'message')
66
+ end
67
+
68
+ def calculate_state
69
+ return :invalid_response unless json || body.nil? || body == ''
70
+
71
+ if success_status
72
+ :success
73
+ elsif valid_error_schema
74
+ :error
75
+ else
76
+ :unknown_error
77
+ end
78
+ end
79
+
80
+ attr_reader :success_status
81
+ end
82
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ class CommandOutput
5
+ class << self
6
+ def halt
7
+ new(true)
8
+ end
9
+
10
+ def continue
11
+ new(false)
12
+ end
13
+ end
14
+
15
+ def initialize(halt)
16
+ @halt = halt
17
+ end
18
+
19
+ def halt?
20
+ @halt
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ class Container
5
+ def initialize
6
+ @registrations = {}
7
+ @instances = {}
8
+ end
9
+
10
+ def register(name, type)
11
+ @registrations[name] = type
12
+ end
13
+
14
+ def register_instance(name, instance)
15
+ @instances[name] = instance
16
+ end
17
+
18
+ def register_module(m, hierarchy = [])
19
+ m.constants.each do |name|
20
+ c = m.const_get(name)
21
+ h = hierarchy.clone
22
+ h << name.to_s.underscore.downcase
23
+ next unless c.is_a?(Module)
24
+
25
+ if c.is_a?(Class)
26
+ registration_name = h.join('.')
27
+ register registration_name, c
28
+ end
29
+ register_module(c, h)
30
+ end
31
+ end
32
+
33
+ def resolve(name, originator = nil)
34
+ originator ||= name
35
+
36
+ return @instances[name] if @instances.key? name
37
+
38
+ raise "Unregistered dependency \"#{name}\"" unless @registrations.key? name
39
+
40
+ registration = @registrations[name]
41
+ dependencies = {}
42
+
43
+ if registration.respond_to?(:dependencies)
44
+ dependencies = registration.dependencies.map do |dependency|
45
+ raise "Circular dependency \"#{dependency}\"" if originator && dependency == originator
46
+
47
+ [dependency.to_sym, resolve(dependency, originator)]
48
+ end.to_h
49
+ end
50
+
51
+ @instances[name] = @registrations[name].new(**dependencies)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ module Depends
5
+ def self.on(*names, **kw_names)
6
+ Module.new do
7
+ class_methods = Module.new do
8
+ define_method :dependencies do
9
+ names + kw_names.values
10
+ end
11
+ end
12
+
13
+ define_singleton_method :included do |base|
14
+ base.extend class_methods
15
+ end
16
+
17
+ names.each do |name|
18
+ method_name = name.split('.').last
19
+
20
+ define_method(method_name) { @_dependencies[name] }
21
+ end
22
+
23
+ kw_names.each do |method_name, name|
24
+ define_method(method_name) { @_dependencies[name] }
25
+ end
26
+
27
+ define_method :initialize do |**args|
28
+ @_dependencies = self.class.dependencies.map { |n| [n, nil] }.to_h
29
+
30
+ args.each do |key, value|
31
+ key = key.to_s
32
+ raise "#{self.class.name} given invalid dependency \"#{key}\"" unless @_dependencies.key?(key)
33
+
34
+ @_dependencies[key] = value
35
+ end
36
+ @_dependencies.each_key do |n|
37
+ raise "#{self.class.name} missing dependency \"#{n}\"" unless args.key?(n.to_sym)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/pcli/main.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ class Main
5
+ def run(args)
6
+ options = Options.parse(args)
7
+
8
+ container = Container.new.tap do |c|
9
+ c.register_instance 'config.endpoint', options[:endpoint]
10
+ c.register_instance 'input', $stdin
11
+ c.register_instance 'output', $stdout
12
+ c.register_instance 'screen', TTY::Screen
13
+ c.register_instance 'which', TTY::Which
14
+ c.register_instance 'editor', TTY::Editor
15
+
16
+ c.register_module Services
17
+ end
18
+
19
+ container.resolve('app').run
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ class Options
5
+ class TTYOption
6
+ include TTY::Option
7
+
8
+ usage do
9
+ program 'ponsqb'
10
+
11
+ desc 'Launch the Ponsqb admin CLI.'
12
+ end
13
+
14
+ option :endpoint do
15
+ short '-e'
16
+ long '--endpoint string'
17
+ default 'https://ponsqb.provisionsbs.com'
18
+ desc 'Ponsqb endpoint.'
19
+ end
20
+
21
+ flag :help do
22
+ short '-h'
23
+ long '--help'
24
+ desc 'Print usgae.'
25
+ end
26
+
27
+ def evaluate
28
+ if params[:help]
29
+ print help
30
+ exit
31
+ end
32
+
33
+ params.to_h
34
+ end
35
+ end
36
+
37
+ def self.parse(args)
38
+ TTYOption.new.parse(args).evaluate
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ module Output
5
+ class Padded
6
+ def self.show(input, output, screen)
7
+ amount = 5
8
+ messages = input
9
+
10
+ messages = [messages] unless messages.respond_to?(:each)
11
+
12
+ messages = messages.flat_map { |m| m == '' ? '' : m.split("\n") }
13
+
14
+ length = screen.width - amount
15
+ result = ''
16
+
17
+ messages.each do |message|
18
+ message.each_char.with_index do |c, i|
19
+ if (i % length).zero?
20
+ result += "\n" if i.positive?
21
+ result += ' ' * amount
22
+ end
23
+ result += c
24
+ end
25
+ result += "\n"
26
+ end
27
+
28
+ output.print(result)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ module Output
5
+ class ServerError
6
+ def self.show(response, output, screen)
7
+ result = ''
8
+
9
+ if response.known_error?
10
+ output.puts 'Server:'
11
+ Output::Padded.show([
12
+ "#{Pl.yellow(response.error.title)} #{Pl.dim("(#{response.error.status})")}",
13
+ '',
14
+ response.error.message
15
+ ], output, screen)
16
+ else
17
+ output.puts "Server #{Pl.yellow("(#{response.code})")}:"
18
+ Output::Padded.show(response.body, output, screen)
19
+ end
20
+ result
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ module Output
5
+ end
6
+ end
data/lib/pcli/pl.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcli
4
+ class Pl
5
+ @base = Pastel.new
6
+
7
+ class << self
8
+ delegate_missing_to :base
9
+
10
+ private
11
+
12
+ attr_reader :base
13
+ end
14
+ end
15
+ end