labclient 0.3.5 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f545a5b4a6c1f514ab0f0b1273e45ed5ce0d2704813754474318ff1f53c84518
4
- data.tar.gz: c48a0be6ac4664251cebfd472d5458cf756ec95a67a64357a051251156e066f8
3
+ metadata.gz: '038bb19b37338ea79eae25d376f49c470e3a3901e84d9f578e51da0ef78f9041'
4
+ data.tar.gz: 8b7893bbcefc7fa4cbecdf1e3f1b3a21d0298678bb9ae8ed30cc2df13eee2e3f
5
5
  SHA512:
6
- metadata.gz: e4c133b2f5a580bfd9149eabe6bf10ce352618f2bc941d89fd1c5122acaff832d6de5e405976362c9a6419529fdba785c8810054731f510bb51bc491161d2e6f
7
- data.tar.gz: '091f20375cae7e990df3038975a51aba2c734cab106645fced426e166a5f5913ba18d7940ffd569a70fe8c6dccf216a59a51ea7cf5835b54c8dded1af5302ac9'
6
+ metadata.gz: 17c52ea58baf3bda4bf70c9111000a823e9f0eac9fdc2cbece3be95c89162ed19b31e3ec39ce86ee504be590f600d9710afe1e0ce5efe11ef339f4fa9dec963e
7
+ data.tar.gz: f4b66628ef3916cc5d431d36196d9abb278435fc98fba7b90ff6720cc159d3599ff6f0d87c5152b1ba0cf0d252fe4a3052a5344b28bdf8d26d8e59845f0f9d19
data/lib/labclient.rb CHANGED
@@ -5,8 +5,10 @@ require 'active_support/all'
5
5
  require 'amazing_print'
6
6
  require 'gitlab_chronic_duration'
7
7
  require 'ostruct'
8
+ require 'ougai'
8
9
 
9
10
  require 'labclient/version'
11
+ require 'labclient/logger'
10
12
  require 'labclient/docs'
11
13
  require 'labclient/curl'
12
14
  require 'labclient/http'
@@ -876,5 +878,8 @@ require 'labclient/generator/wizard'
876
878
  # Dynamically Require Templates (Simplify new template creation)
877
879
  Dir["#{File.dirname(__FILE__)}/labclient/generator/templates/*.rb"].sort.each { |file| require file }
878
880
 
879
- # I am Very Last
881
+ # Load Client Files - I am Very Last!
882
+ require 'labclient/client/setup'
883
+ require 'labclient/client/helpers'
884
+ require 'labclient/client/meta'
880
885
  require 'labclient/client'
@@ -22,7 +22,7 @@ module LabClient
22
22
  Timeout.timeout(total_time) do
23
23
  loop do
24
24
  reload
25
- puts 'Waiting for Pipelines'
25
+ logger.info 'Waiting for Pipelines' unless quiet?
26
26
  break unless pipelines.empty?
27
27
 
28
28
  sleep sleep_time
@@ -1,104 +1,10 @@
1
1
  # Top Namespace
2
2
  module LabClient
3
3
  # API Specifics
4
- # rubocop:disable Metrics/ClassLength
5
4
  class Client
6
- # include HTTParty
7
- attr_accessor :settings, :resp, :klass, :link, :http
8
-
9
- def inspect
10
- "#<LabClient::Client url: \"#{@settings[:url]}\">"
11
- end
12
-
13
- # Helper to make subclasses directly accessible
14
- def subclasses
15
- self.class.instance_variable_get(:@subclasses)
16
- end
17
-
18
- # ----------
19
- # Alias List
20
- # approvals == merge_request_approvals
21
- # repo == repository
22
- # ----------
23
- @subclasses = {
24
- appearance: Appearances,
25
- application_settings: ApplicationSettings,
26
- application_statistics: ApplicationStatistics,
27
- applications: Applications,
28
- approvals: Approvals,
29
- audit_events: AuditEvents,
30
- avatar: Avatars,
31
- awards: Awards,
32
- branches: Branches,
33
- broadcast_messages: BroadcastMessages,
34
- ci_lint: CiLint,
35
- commits: Commits,
36
- deploy_keys: DeployKeys,
37
- discussions: Discussions,
38
- epics: Epics,
39
- events: Events,
40
- feature_flags: FeatureFlags,
41
- files: Files,
42
- groups: Groups,
43
- impersonation_tokens: ImpersonationTokens,
44
- issues: Issues,
45
- jobs: Jobs,
46
- keys: Keys,
47
- license: Licenses,
48
- markdown: Markdown,
49
- members: Members,
50
- merge_request_approvals: Approvals,
51
- merge_requests: MergeRequests,
52
- namespaces: Namespaces,
53
- notes: Notes,
54
- notifications: Notifications,
55
- pipelines: Pipelines,
56
- project_runners: ProjectRunners,
57
- projects: Projects,
58
- protected_branches: ProtectedBranches,
59
- protected_environments: ProtectedEnvironments,
60
- protected_tags: ProtectedTags,
61
- registry: Registry,
62
- repo: Repositories,
63
- repository: Repositories,
64
- resource_labels: ResourceLabels,
65
- runners: Runners,
66
- snippets: Snippets,
67
- system_hooks: SystemHooks,
68
- tags: Tags,
69
- todos: Todos,
70
- users: Users,
71
- version: Version,
72
- wikis: Wikis,
73
- wizard: Generator::Wizard
74
- }
75
-
76
- @subclasses.each do |name, obj|
77
- define_method(name) do
78
- obj.new(self)
79
- end
80
- end
81
-
82
- def api_methods
83
- subclasses.keys.sort
84
- end
85
-
86
- def help(help_filter = nil)
87
- puts 'Available Methods'
88
-
89
- shown_subclasses = if help_filter
90
- api_methods.grep(/#{help_filter}/)
91
- else
92
- api_methods
93
- end
94
-
95
- puts " - #{shown_subclasses.join(' ')}\n\n"
96
- puts "See help for each specific sub-category\n"
97
- puts "- client.users.help\n"
98
- puts "- client.users.api_methods\n"
99
-
100
- nil
101
- end
5
+ include ClientHelpers
6
+ include ClientSetup
7
+ include Logger
102
8
 
103
9
  # Default setup, pull in settings
104
10
  def initialize(user_settings = nil)
@@ -114,88 +20,63 @@ module LabClient
114
20
  # Only prompt if explicitly set to nil
115
21
  prompt_for_token if @settings[:token].nil?
116
22
 
117
- self.http = HTTP.new(@settings)
118
- end
119
-
120
- def base_url
121
- "#{settings[:url]}/api/v4/"
122
- end
23
+ # Initial Delay / Retry Value
24
+ self.delay = 0
25
+ self.retries = 0
123
26
 
124
- def prompt_for_url
125
- print 'Enter GitLab URL (e.g. https://gitlab.com): '
126
- @settings[:url] = $stdin.gets.chomp
127
- raise 'LabClient Error - Missing URL!' if @settings[:url].blank?
27
+ # Request Configuration
28
+ self.http = HTTP.new(@settings)
128
29
  end
129
30
 
130
- def prompt_for_token
131
- print 'Enter Personal Access Token: '
132
- @settings[:token] = $stdin.gets.chomp
133
- end
31
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
32
+ def request(method, path, klass = nil, body = {}, dump_json = true)
33
+ self.klass = klass
34
+ self.resp = http.request(method, path, body, dump_json)
35
+ self.path = path
134
36
 
135
- def unspecified_defaults
136
- @settings[:paginate] = true if @settings[:paginate].nil?
137
- @settings[:ssl_verify] = true if @settings[:ssl_verify].nil?
138
- @settings[:quiet] = false if @settings[:quiet].nil?
139
- @settings[:token_type] = 'Private-Token' if @settings[:token_type].nil?
140
- end
37
+ debug_handler if debug?
141
38
 
142
- def home_file
143
- "#{ENV['HOME']}/.gitlab-labclient"
144
- end
39
+ post_request_handlers
145
40
 
146
- # Easier Profile Name Access
147
- def profile
148
- if settings&.key? :profile
149
- settings[:profile].to_sym
150
- else
151
- ENV['LABCLIENT_PROFILE'].to_sym
152
- end
153
- end
41
+ process resp
42
+ rescue LabClient::Error => e
43
+ logger.fatal('Request Failed', e.error_details) unless quiet?
44
+ resp
45
+ rescue LabClient::Retry
46
+ self.retries += 1
154
47
 
155
- # Support for Named Profiles
156
- def setup_profile
157
- return false unless File.exist? home_file
48
+ # Assume Retry After by Default
49
+ logger.debug('Retry After', value: retry_after) if debug?
50
+ self.delay = retry_after if resp.headers.key? 'retry-after'
158
51
 
159
- config = Oj.load_file(home_file, { symbol_keys: true })
160
- return false unless config.key? profile
52
+ self.delay += delay_factor
53
+ logger.warn "Received #{resp.code}. Retry in #{delay}", limit: retry_max, retries: retries unless quiet?
161
54
 
162
- self.settings ||= {}
163
- settings.merge! config[profile]
164
- end
55
+ sleep delay unless ENV['LABCLIENT_TESTING']
165
56
 
166
- # Load default profile
167
- def fill_configuration
168
- if File.exist? home_file
169
- Oj.load_file(home_file, { symbol_keys: true })
170
- else
171
- {
172
- url: ENV['LABCLIENT_URL'],
173
- token: ENV['LABCLIENT_TOKEN']
174
- }
175
- end
57
+ retry
176
58
  end
59
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
177
60
 
178
- def request(method, path, klass = nil, body = {}, dump_json = true)
179
- @klass = klass
180
-
181
- resp = http.request(method, path, body, dump_json)
61
+ # Handling Details for after the request
62
+ # Debug,Retry, Instance Variables, Error Handling
63
+ def post_request_handlers
64
+ # Handle Retry Logic
65
+ raise LabClient::Retry if should_retry?
182
66
 
183
67
  # Save Client
184
- resp.instance_variable_set(:@client, self)
68
+ save_client
69
+
70
+ # Exit on Max Retries
71
+ raise LabClient::Error.new(resp), resp.friendly_error if retry_max?
185
72
 
186
73
  raise LabClient::Error.new(resp), resp.friendly_error unless resp.success?
187
74
 
188
75
  # Drop in raw path
189
- resp.instance_variable_set(:@path, path)
190
-
191
- process resp
192
- rescue LabClient::Error => e
193
- puts e.message unless quiet?
194
- resp
195
- end
76
+ save_path
196
77
 
197
- def quiet?
198
- settings[:quiet]
78
+ # Reset Delay/Retry Factor
79
+ retry_update
199
80
  end
200
81
 
201
82
  # Assume we want LabStruct if @klass is ever nil
@@ -214,5 +95,4 @@ module LabClient
214
95
  end
215
96
  end
216
97
  end
217
- # rubocop:enable Metrics/ClassLength
218
98
  end
@@ -0,0 +1,108 @@
1
+ # Top Namespace
2
+ module LabClient
3
+ # Reader Methods / Accessor Helpers
4
+ module ClientHelpers
5
+ def api_methods
6
+ subclasses.keys.sort
7
+ end
8
+
9
+ def help(help_filter = nil)
10
+ puts 'Available Methods'
11
+
12
+ shown_subclasses = if help_filter
13
+ api_methods.grep(/#{help_filter}/)
14
+ else
15
+ api_methods
16
+ end
17
+
18
+ puts " - #{shown_subclasses.join(' ')}\n\n"
19
+ puts "See help for each specific sub-category\n"
20
+ puts "- client.users.help\n"
21
+ puts "- client.users.api_methods\n"
22
+
23
+ nil
24
+ end
25
+
26
+ def home_file
27
+ "#{ENV['HOME']}/.gitlab-labclient"
28
+ end
29
+
30
+ # Easier Profile Name Access
31
+ def profile
32
+ if settings&.key? :profile
33
+ settings[:profile].to_sym
34
+ else
35
+ ENV['LABCLIENT_PROFILE'].to_sym
36
+ end
37
+ end
38
+
39
+ # Instance Variable Helpers
40
+ def save_client
41
+ resp.instance_variable_set(:@client, self)
42
+ end
43
+
44
+ def save_path
45
+ resp.instance_variable_set(:@path, path)
46
+ end
47
+
48
+ def base_url
49
+ "#{settings[:url]}/api/v4/"
50
+ end
51
+
52
+ def quiet?
53
+ settings[:quiet]
54
+ end
55
+
56
+ def debug?
57
+ settings[:debug]
58
+ end
59
+
60
+ def delay_factor
61
+ settings[:retry][:delay_factor]
62
+ end
63
+
64
+ def retry_max
65
+ settings[:retry][:max]
66
+ end
67
+
68
+ # Maximum Retries
69
+ def retry_max?
70
+ retries >= retry_max
71
+ end
72
+
73
+ # On Successfully response lower delay
74
+ # Prevent multiple request / delays
75
+ def retry_update
76
+ self.delay = [delay - 1, 1].max
77
+ self.retries = [retries - 1, 0].max
78
+ end
79
+
80
+ # Debug Print Output
81
+ def debug_handler
82
+ options = resp.request.options
83
+
84
+ logger.debug(
85
+ options[:method].to_s.upcase,
86
+ code: resp.code,
87
+ path: path,
88
+ ssl_verify: options[:ssl_verifyhost],
89
+ message: resp.return_message,
90
+ klass: klass.to_s,
91
+ base_url: resp.request.base_url
92
+ )
93
+ end
94
+
95
+ # Helper for Accessing the Retry Headers
96
+ def retry_after
97
+ resp.headers['retry-after'].to_i || 0
98
+ end
99
+
100
+ # Handle Retry Logic
101
+ # 1. If response merits a retry
102
+ # 2. Retry is enabled
103
+ # 3. Retry Sleep Max isn't hit
104
+ def should_retry?
105
+ resp.retry? && settings[:retry] && !retry_max?
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,81 @@
1
+ # Top Namespace
2
+ module LabClient
3
+ # Variables / Meta
4
+ class Client
5
+ # include HTTParty
6
+ attr_accessor :settings, :resp, :klass, :link, :http, :delay, :retries, :path
7
+
8
+ def inspect
9
+ "#<LabClient::Client url: \"#{@settings[:url]}\">"
10
+ end
11
+
12
+ # Helper to make subclasses directly accessible
13
+ def subclasses
14
+ self.class.instance_variable_get(:@subclasses)
15
+ end
16
+
17
+ # ----------
18
+ # Alias List
19
+ # approvals == merge_request_approvals
20
+ # repo == repository
21
+ # ----------
22
+ @subclasses = {
23
+ appearance: Appearances,
24
+ application_settings: ApplicationSettings,
25
+ application_statistics: ApplicationStatistics,
26
+ applications: Applications,
27
+ approvals: Approvals,
28
+ audit_events: AuditEvents,
29
+ avatar: Avatars,
30
+ awards: Awards,
31
+ branches: Branches,
32
+ broadcast_messages: BroadcastMessages,
33
+ ci_lint: CiLint,
34
+ commits: Commits,
35
+ deploy_keys: DeployKeys,
36
+ discussions: Discussions,
37
+ epics: Epics,
38
+ events: Events,
39
+ feature_flags: FeatureFlags,
40
+ files: Files,
41
+ groups: Groups,
42
+ impersonation_tokens: ImpersonationTokens,
43
+ issues: Issues,
44
+ jobs: Jobs,
45
+ keys: Keys,
46
+ license: Licenses,
47
+ markdown: Markdown,
48
+ members: Members,
49
+ merge_request_approvals: Approvals,
50
+ merge_requests: MergeRequests,
51
+ namespaces: Namespaces,
52
+ notes: Notes,
53
+ notifications: Notifications,
54
+ pipelines: Pipelines,
55
+ project_runners: ProjectRunners,
56
+ projects: Projects,
57
+ protected_branches: ProtectedBranches,
58
+ protected_environments: ProtectedEnvironments,
59
+ protected_tags: ProtectedTags,
60
+ registry: Registry,
61
+ repo: Repositories,
62
+ repository: Repositories,
63
+ resource_labels: ResourceLabels,
64
+ runners: Runners,
65
+ snippets: Snippets,
66
+ system_hooks: SystemHooks,
67
+ tags: Tags,
68
+ todos: Todos,
69
+ users: Users,
70
+ version: Version,
71
+ wikis: Wikis,
72
+ wizard: Generator::Wizard
73
+ }
74
+
75
+ @subclasses.each do |name, obj|
76
+ define_method(name) do
77
+ obj.new(self)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,52 @@
1
+ # Top Namespace
2
+ module LabClient
3
+ # Methods for Initialization
4
+ module ClientSetup
5
+ # Load default profile
6
+ def fill_configuration
7
+ if File.exist? home_file
8
+ Oj.load_file(home_file, { symbol_keys: true })
9
+ else
10
+ {
11
+ url: ENV['LABCLIENT_URL'],
12
+ token: ENV['LABCLIENT_TOKEN']
13
+ }
14
+ end
15
+ end
16
+
17
+ # If nothing entered Prompt for Input
18
+ def prompt_for_url
19
+ print 'Enter GitLab URL (e.g. https://gitlab.com): '
20
+ @settings[:url] = $stdin.gets.chomp
21
+ raise 'LabClient Error - Missing URL!' if @settings[:url].blank?
22
+ end
23
+
24
+ # Check for Token
25
+ def prompt_for_token
26
+ print 'Enter Personal Access Token: '
27
+ @settings[:token] = $stdin.gets.chomp
28
+ end
29
+
30
+ # Fill Defaults
31
+ def unspecified_defaults
32
+ @settings[:paginate] = true if @settings[:paginate].nil?
33
+ @settings[:ssl_verify] = true if @settings[:ssl_verify].nil?
34
+ @settings[:quiet] = false if @settings[:quiet].nil?
35
+ @settings[:debug] = false if @settings[:quiet].nil?
36
+ @settings[:debug] = false if @settings[:debug].nil?
37
+ @settings[:token_type] = 'Private-Token' if @settings[:token_type].nil?
38
+ @settings[:retry] = { max: 5, delay_factor: 1, count: 0 } if @settings[:retry].nil?
39
+ end
40
+
41
+ # Support for Named Profiles
42
+ def setup_profile
43
+ return false unless File.exist? home_file
44
+
45
+ config = Oj.load_file(home_file, { symbol_keys: true })
46
+ return false unless config.key? profile
47
+
48
+ self.settings ||= {}
49
+ settings.merge! config[profile]
50
+ end
51
+ end
52
+ end
@@ -47,8 +47,13 @@ module LabClient
47
47
  end
48
48
 
49
49
  # Category and Primary Key for docs
50
+ # If/Else for Instance Variable Warnings
50
51
  def group_name
51
- self.class.instance_variable_get('@group_name') || klass
52
+ if self.class.instance_variable_defined? '@group_name'
53
+ self.class.instance_variable_get('@group_name')
54
+ else
55
+ klass
56
+ end
52
57
  end
53
58
 
54
59
  def inspect
@@ -70,7 +75,7 @@ module LabClient
70
75
  return nil if obj_id.nil?
71
76
 
72
77
  # Already a Integer
73
- return obj_id if obj_id.class == Integer
78
+ return obj_id if obj_id.instance_of?(Integer)
74
79
 
75
80
  # If LabClient Object, send ID
76
81
  return obj_id.id if obj_id.class.module_parent_name == 'LabClient'
@@ -107,7 +112,7 @@ module LabClient
107
112
  # :developer => 30
108
113
  # :owner => 50
109
114
  def query_access_level(query, key = :group_access)
110
- query[key] = machine_access_level query[key] if query.key?(key) && query[key].class == Symbol
115
+ query[key] = machine_access_level query[key] if query.key?(key) && query[key].instance_of?(Symbol)
111
116
  end
112
117
 
113
118
  # TODO: See if these are even needed
@@ -118,7 +123,7 @@ module LabClient
118
123
  # 40 => Maintainer access
119
124
  # 60 => Admin access
120
125
  def protected_query_access_level(query, key = :push_access_level)
121
- query[key] = machine_protected_access_level query[key] if query.key?(key) && query[key].class == Symbol
126
+ query[key] = machine_protected_access_level query[key] if query.key?(key) && query[key].instance_of?(Symbol)
122
127
  end
123
128
  end
124
129
  end
@@ -72,8 +72,13 @@ module LabClient
72
72
  end
73
73
 
74
74
  # Allow for Custom Group Name Overrides
75
+ # Ruby warning of uninitialized variables
75
76
  def group_name
76
- @group_name || name.split('::', 2).last.split(/(?=[A-Z])/).join(' ')
77
+ if defined? @group_name
78
+ @group_name
79
+ else
80
+ name.split('::', 2).last.split(/(?=[A-Z])/).join(' ')
81
+ end
77
82
  end
78
83
 
79
84
  # Helper to Make navigation rendered out once rather than evaluated on Ember
@@ -29,7 +29,7 @@ module LabClient
29
29
  epic_id = format_id(epic_id)
30
30
 
31
31
  # This is horrible, but has to be the epic_issue_id, not the issue's id or iid
32
- epic_issue_id = epic_issue_id.epic_issue_id if epic_issue_id.class == Issue
32
+ epic_issue_id = epic_issue_id.epic_issue_id if epic_issue_id.instance_of?(Issue)
33
33
 
34
34
  client.request(:delete, "groups/#{group_id}/epics/#{epic_id}/issues/#{epic_issue_id}")
35
35
  end
@@ -36,7 +36,7 @@ module LabClient
36
36
  epic_id = format_id(epic_id)
37
37
 
38
38
  # This is horrible, but has to be the epic_issue_id, not the issue's id or iid
39
- epic_issue_id = epic_issue_id.epic_issue_id if epic_issue_id.class == Issue
39
+ epic_issue_id = epic_issue_id.epic_issue_id if epic_issue_id.instance_of?(Issue)
40
40
 
41
41
  client.request(:put, "groups/#{group_id}/epics/#{epic_id}/issues/#{epic_issue_id}", nil, query)
42
42
  end
@@ -8,5 +8,14 @@ module LabClient
8
8
  super
9
9
  @resp = resp
10
10
  end
11
+
12
+ # Helper for Raising Exceptions
13
+ def error_details
14
+ { code: resp.code, message: resp.find_friendly_error }
15
+ end
16
+ end
17
+
18
+ # Class Shim
19
+ class Retry < StandardError
11
20
  end
12
21
  end
@@ -16,6 +16,7 @@ module LabClient
16
16
  # Common Helper Class
17
17
  class TemplateHelper
18
18
  include TemplateMethods
19
+ include LabClient::Logger
19
20
 
20
21
  attr_reader :client
21
22
  attr_accessor :opts
@@ -46,16 +47,16 @@ module LabClient
46
47
  attr_accessor :group_name, :group_path, :group_suffix, :project_name
47
48
 
48
49
  def run!
49
- puts "Running: #{group_suffix}"
50
+ logger.info "Running: #{group_suffix}"
50
51
  generate_group
51
52
 
52
53
  # Run `setup_` prefixed classes
53
54
  self.class.instance_methods.grep(/setup_/).each { |x| send(x) }
54
55
 
55
56
  # Print Created Groups/Project
56
- puts "#{@group.name} - #{@group.web_url}"
57
+ logger.info 'Group', name: @group.name, web_url: @group.web_url
57
58
  @projects.each do |project|
58
- puts " - #{project.name} - #{project.web_url}"
59
+ logger.info 'Project', name: project.name, web_url: project.web_url
59
60
  end
60
61
 
61
62
  {
@@ -4,6 +4,7 @@ module LabClient
4
4
  # Helper to Generate Data / Populate GitLab
5
5
  class Wizard
6
6
  include Generator::Names # Name Generator
7
+ include LabClient::Logger
7
8
  attr_reader :client
8
9
  attr_accessor :count, :random, :password, :templates, :domain, :skip_confirmation
9
10
 
@@ -26,7 +27,7 @@ module LabClient
26
27
  self.random = true # Populate Random or use only Templates
27
28
  self.count = default_count
28
29
  self.password = SecureRandom.uuid
29
- puts "Default Password: #{password}" unless client.quiet?
30
+ logger.info('Wizard Default Password', password: password) unless client.quiet?
30
31
 
31
32
  self.skip_confirmation = true
32
33
  self.domain = URI.parse(client.settings[:url]).hostname
@@ -54,11 +55,11 @@ module LabClient
54
55
  end
55
56
 
56
57
  def generate_users
57
- puts 'Generating Users' unless client.quiet?
58
+ logger.info 'Generating Users' unless client.quiet?
58
59
  @users = @user_names.map do |name|
59
60
  username = name.downcase.gsub(/[^0-9A-Za-z]/, '')
60
61
  email = "#{username}@#{domain}"
61
- puts "User -- Name: #{name}, UserName: #{username}, Email: #{email}" unless client.quiet?
62
+ logger.info('User', name: name, username: username, email: email) unless client.quiet?
62
63
 
63
64
  client.users.create(
64
65
  name: name,
@@ -71,41 +72,39 @@ module LabClient
71
72
  end
72
73
 
73
74
  def generate_groups
74
- puts 'Generating Groups' unless client.quiet?
75
+ logger.info 'Generating Groups' unless client.quiet?
75
76
  @groups = @group_names.map do |name|
76
77
  path = name.downcase.gsub(/[^0-9A-Za-z]/, '')
77
- puts "Group -- #{name}/#{path}" unless client.quiet?
78
+ logger.info "Group -- #{name}/#{path}" unless client.quiet?
78
79
  client.groups.create(name: name, path: path)
79
80
  end
80
81
  end
81
82
 
82
- # rubocop:disable Metrics/AbcSize
83
83
  def generate_group_membership
84
- puts 'Adding Group members' unless client.quiet?
84
+ logger.info 'Adding Group members' unless client.quiet?
85
85
  ## Group Access Level
86
86
  @groups.each do |group|
87
87
  @users.sample(rand(1..@users.count)).each do |user|
88
88
  level = group.valid_group_project_levels.sample
89
- puts "Group Add: #{group.name}: #{user.name} - #{level}" unless client.quiet?
89
+ logger.info('Group Add', name: group.name, user: user.name, level: level) unless client.quiet?
90
90
  group.member_add(user, access_level: level)
91
91
  # :nocov:
92
92
  rescue StandardError => e
93
- puts e.message unless client.quiet?
93
+ logger.fatal e.message unless client.quiet?
94
94
  next
95
95
  # :nocov:
96
96
  end
97
97
  end
98
98
  end
99
- # rubocop:enable Metrics/AbcSize
100
99
 
101
100
  def generate_projects(group)
102
- puts 'Generating Projects' unless client.quiet?
101
+ logger.info 'Generating Projects' unless client.quiet?
103
102
  # Collect Group Members
104
103
  members = group.members
105
104
 
106
105
  # Loop through project names, create project add issues
107
106
  @project_names.uniq.map do |project_name|
108
- puts "Project: #{project_name}" unless client.quiet?
107
+ logger.info "Project: #{project_name}" unless client.quiet?
109
108
  project = group.project_create(name: project_name, description: gen_description)
110
109
 
111
110
  rand(count[:issues]).times do
@@ -1,4 +1,3 @@
1
- # rubocop:disable Metrics/ClassLength
2
1
  # Top namespace
3
2
  module LabClient
4
3
  # Inspect Helper
@@ -344,4 +343,3 @@ module LabClient
344
343
  # rubocop:enable Metrics/BlockLength
345
344
  end
346
345
  end
347
- # rubocop:enable Metrics/ClassLength
@@ -70,20 +70,16 @@ module LabClient
70
70
 
71
71
  private
72
72
 
73
- # rubocop:disable Metrics/CyclomaticComplexity
74
- # TODO - Finish Classes
73
+ # TODO: - Finish Classes
75
74
  def klass_type(scope)
76
75
  case scope
77
76
  when :projects then Project
78
77
  when :issues then Issue
79
78
  when :merge_requests then MergeRequest
80
- when :wiki_blobs then nil # wiki_blobs
81
- when :milestones then nil # ::Project::MileStone
82
- when :blobs then nil # blobs
79
+ when :milestones, :wiki_blobs, :blobs then nil
83
80
  when :commits then Commit
84
81
  when :users then User
85
82
  end
86
83
  end
87
- # rubocop:enable Metrics/CyclomaticComplexity
88
84
  end
89
85
  end
@@ -3,6 +3,7 @@ module LabClient
3
3
  # Ugly Hack, but Makes Docs Easy
4
4
  class Groups < Common
5
5
  doc '' do
6
+ # Do Nothing!
6
7
  end
7
8
  end
8
9
  end
@@ -72,9 +72,9 @@ module Typhoeus
72
72
  attr_reader :path, :client
73
73
 
74
74
  def data
75
- return @data if @data
75
+ @data ||= process_body
76
76
 
77
- @data = process_body
77
+ @data
78
78
  end
79
79
 
80
80
  # Shim for CurlHelper
@@ -94,13 +94,31 @@ module Typhoeus
94
94
  end
95
95
  end
96
96
 
97
+ # Retry Helper Accessor
98
+ def retry?
99
+ code == 429
100
+ end
101
+
102
+ # Print Error information
103
+ # 1. Use Typheous `return_message` if there isn't any return body
104
+ # For network/uri/dns related issues
105
+ # 2. Use body for parsed responses
106
+ # For Bad Request, invalid params
107
+ # 3. Return raw data
108
+ # For non body responses
109
+ def find_friendly_error
110
+ case data
111
+ when nil
112
+ return_message
113
+ when LabClient::LabStruct
114
+ data[:message] || data[:error]
115
+ else # Handle String as well
116
+ data
117
+ end
118
+ end
119
+
97
120
  def friendly_error
98
- message = if data
99
- data[:message] || data[:error] || data
100
- else
101
- return_message
102
- end
103
- "#{code} - #{message}"
121
+ "#{code} - #{find_friendly_error}"
104
122
  end
105
123
  end
106
124
  end
@@ -12,7 +12,7 @@ module LabClient
12
12
  date_time_attrs %i[closed_at created_at updated_at]
13
13
 
14
14
  # User Fields
15
- user_attrs %i[closed_by author assignee]
15
+ user_attrs %i[author assignee]
16
16
 
17
17
  # Via State Events
18
18
  def close
@@ -2,6 +2,7 @@
2
2
  module LabClient
3
3
  # Common Configuration for all Class Helpers
4
4
  class Klass < LabStruct
5
+ include LabClient::Logger
5
6
  include CurlHelper
6
7
 
7
8
  attr_reader :client, :response
@@ -15,7 +16,6 @@ module LabClient
15
16
 
16
17
  # API Methods here have to be explicitly documented / custom helpers
17
18
  # Assume no methods by default
18
- # rubocop:disable Metrics/AbcSize
19
19
  def help(help_filter = nil)
20
20
  docs = LabClient::Docs.docs.dig(group_name, 'Reference')
21
21
  unless docs
@@ -38,7 +38,6 @@ module LabClient
38
38
  # Ignore Output
39
39
  nil
40
40
  end
41
- # rubocop:enable Metrics/AbcSize
42
41
 
43
42
  # Documented API Methods
44
43
  def api_methods
@@ -101,6 +100,11 @@ module LabClient
101
100
  self
102
101
  end
103
102
 
103
+ # Quiet Reader Helper
104
+ def quiet?
105
+ client.quiet?
106
+ end
107
+
104
108
  # rubocop:disable Lint/MissingSuper
105
109
  def initialize(hash = nil, response = nil, client = nil)
106
110
  @client = client
@@ -0,0 +1,50 @@
1
+ # Top Namespace
2
+ module LabClient
3
+ # Helper to Unify Log Output
4
+ module Logger
5
+ def logger
6
+ LabClient::Logger.logger
7
+ end
8
+
9
+ def self.logger_setup
10
+ logger = Ougai::Logger.new($stdout)
11
+ logger.formatter = Ougai::Formatters::LabClient.new
12
+
13
+ logger
14
+ end
15
+
16
+ def self.logger
17
+ @logger ||= logger_setup
18
+ end
19
+ end
20
+ end
21
+
22
+ # Log Formatter for Readable Inline (Timestamp)
23
+ # https://github.com/eropple/ougai-formatters-inline_readable/blob/master/lib/ougai/formatters/inline_readable.rb
24
+ module Ougai
25
+ module Formatters
26
+ # LabClient Specific
27
+ class LabClient < Ougai::Formatters::Readable
28
+ # For Amazing Print
29
+ def ai_settings
30
+ { ruby19_syntax: true, multiline: false }
31
+ end
32
+
33
+ def time_format
34
+ '%Y-%m-%e %k:%M:%S %z'
35
+ end
36
+
37
+ def call(severity, time, _progname, data)
38
+ msg = data.delete(:msg)
39
+ @excluded_fields.each { |f| data.delete(f) }
40
+
41
+ level = @plain ? severity : colored_level(severity)
42
+ output = "[#{time.strftime(time_format)}] #{level}: #{msg}"
43
+
44
+ output += " #{data.ai(ai_settings)}" unless data.empty?
45
+
46
+ "#{output}\n"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,7 +1,6 @@
1
1
  # Top namespace
2
2
  module LabClient
3
3
  # Inspect Helper
4
- # rubocop:disable Metrics/ClassLength
5
4
  class MergeRequest < Klass
6
5
  include ClassHelpers
7
6
  # extend Docs
@@ -193,7 +192,7 @@ module LabClient
193
192
  Timeout.timeout(total_time) do
194
193
  loop do
195
194
  reload
196
- puts "Waiting for Merge Status: #{merge_status}"
195
+ logger.info "Waiting for Merge Status: #{merge_status}" unless quiet?
197
196
  break if %w[can_be_merged unchecked].include? merge_status
198
197
  raise "Cannot be merged! #{import_error}" if %w[cannot_be_merged cannot_be_merged_recheck].include? merge_status
199
198
 
@@ -252,5 +251,4 @@ module LabClient
252
251
 
253
252
  # rubocop:enable Metrics/BlockLength
254
253
  end
255
- # rubocop:enable Metrics/ClassLength
256
254
  end
@@ -1,5 +1,4 @@
1
1
  # The glorious gitlab labclient of labs
2
- # rubocop:disable Metrics/ClassLength
3
2
  module LabClient
4
3
  # Helper for Docs
5
4
  class Overview
@@ -362,6 +361,30 @@ module LabClient
362
361
  DOC
363
362
  end
364
363
 
364
+ doc 'Other' do
365
+ title 'Retry'
366
+ markdown <<~DOC
367
+ Gitlab.com and other instances may have protected paths or rate limiting enabled. By default the LabClient will:
368
+
369
+ 1. Check if a `Retry Later` request was received (Return Code 429)
370
+ 2. Wait for combined `retry-after` and `delay_factor` value
371
+ 3. Retry until retries `max` is reached
372
+ DOC
373
+
374
+ example <<~DOC
375
+ client = LabClient::Client.new(
376
+ url: 'https://gitlab.labclient',
377
+ token: 'gitlab api token',
378
+ retry: {
379
+ max: 3, delay_factor: 2
380
+ }
381
+ )
382
+
383
+ # Or after the init
384
+ @client.settings[:retry] = { max: 3, delay_factor: 2 }
385
+ DOC
386
+ end
387
+
365
388
  doc 'Other' do
366
389
  title 'Quiet'
367
390
  desc 'Error messages by default are printed to STDOUT. This can be supressed via the :quiet setting'
@@ -378,6 +401,22 @@ module LabClient
378
401
  DOC
379
402
  end
380
403
 
404
+ doc 'Other' do
405
+ title 'Debug'
406
+ desc 'Print Request Information'
407
+
408
+ example <<~DOC
409
+ client = LabClient::Client.new(
410
+ url: 'https://gitlab.labclient',
411
+ token: 'gitlab api token',
412
+ debug: true
413
+ )
414
+
415
+ # Or after the init
416
+ client.settings[:debug] = true
417
+ DOC
418
+ end
419
+
381
420
  doc 'Other' do
382
421
  title 'Curl'
383
422
  desc 'Sometimes you just wana have a curl example to test or send someone'
@@ -428,4 +467,3 @@ module LabClient
428
467
  end
429
468
  end
430
469
  end
431
- # rubocop:enable Metrics/ClassLength
@@ -40,9 +40,7 @@ module LabClient
40
40
  DOC
41
41
  end
42
42
 
43
- # rubocop:disable Lint/MissingSuper
44
43
  def respond_to_missing?(method_name, include_private = false); end
45
- # rubocop:enable Lint/MissingSuper
46
44
 
47
45
  def each_page(&block)
48
46
  yield @array # This will eventually be the whole list
@@ -63,7 +63,7 @@ module LabClient
63
63
  Timeout.timeout(total_time) do
64
64
  loop do
65
65
  reload
66
- puts "Waiting for Pipeline: #{status}"
66
+ logger.info('Waiting for Pipeline', status: status) unless quiet?
67
67
  break if %w[skipped manual canceled success].include? status
68
68
  raise 'Pipeline failed' if status == 'failed'
69
69
 
@@ -748,7 +748,7 @@ module LabClient
748
748
  Timeout.timeout(total_time) do
749
749
  loop do
750
750
  reload
751
- puts "Waiting for Import Status: #{import_status}"
751
+ logger.info('Waiting for Import Status', status: import_status) unless quiet?
752
752
  break if %w[none finished].include? import_status
753
753
  raise "Import Failed: #{import_error}" if import_status == 'failed'
754
754
 
@@ -1,6 +1,6 @@
1
1
  # Top namespace
2
2
  module LabClient
3
- # rubocop:disable Metrics/BlockLength, Metrics/ClassLength
3
+ # rubocop:disable Metrics/BlockLength
4
4
  # Help Do Block
5
5
  class Project < Klass
6
6
  help do
@@ -268,5 +268,5 @@ module LabClient
268
268
  option 'parent', 'Show (API Call) Project Parent, returns either Group or User'
269
269
  end
270
270
  end
271
- # rubocop:enable Metrics/BlockLength, Metrics/ClassLength
271
+ # rubocop:enable Metrics/BlockLength
272
272
  end
@@ -72,20 +72,16 @@ module LabClient
72
72
 
73
73
  private
74
74
 
75
- # rubocop:disable Metrics/CyclomaticComplexity
76
- # TODO - Finish Classes
75
+ # TODO: - Finish Classes
77
76
  def klass_type(scope)
78
77
  case scope
79
78
  when :issues then Issue
80
79
  when :merge_requests then MergeRequest
81
80
  when :notes then Note
82
- when :wiki_blobs then nil # wiki_blobs
83
- when :milestones then nil # ::Project::MileStone
84
- when :blobs then nil # blobs
81
+ when :wiki_blobs, :milestones, :blobs then nil
85
82
  when :commits then Commit
86
83
  when :users then User
87
84
  end
88
85
  end
89
- # rubocop:enable Metrics/CyclomaticComplexity
90
86
  end
91
87
  end
@@ -1,3 +1,3 @@
1
1
  module LabClient
2
- VERSION = '0.3.5'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: labclient
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Davin Walker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-04 00:00:00.000000000 Z
11
+ date: 2021-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.10'
83
+ - !ruby/object:Gem::Dependency
84
+ name: ougai
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '2.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '2.0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: typhoeus
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -100,42 +114,42 @@ dependencies:
100
114
  requirements:
101
115
  - - ">="
102
116
  - !ruby/object:Gem::Version
103
- version: '1.16'
117
+ version: '2.2'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - ">="
109
123
  - !ruby/object:Gem::Version
110
- version: '1.16'
124
+ version: '2.2'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: faker
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - "~>"
116
130
  - !ruby/object:Gem::Version
117
- version: '2.12'
131
+ version: '2.16'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
136
  - - "~>"
123
137
  - !ruby/object:Gem::Version
124
- version: '2.12'
138
+ version: '2.16'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: minitest
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: '5.13'
145
+ version: '5.14'
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: '5.13'
152
+ version: '5.14'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: minitest-reporters
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -184,14 +198,14 @@ dependencies:
184
198
  requirements:
185
199
  - - "~>"
186
200
  - !ruby/object:Gem::Version
187
- version: '10.0'
201
+ version: '13.0'
188
202
  type: :development
189
203
  prerelease: false
190
204
  version_requirements: !ruby/object:Gem::Requirement
191
205
  requirements:
192
206
  - - "~>"
193
207
  - !ruby/object:Gem::Version
194
- version: '10.0'
208
+ version: '13.0'
195
209
  - !ruby/object:Gem::Dependency
196
210
  name: rdoc
197
211
  requirement: !ruby/object:Gem::Requirement
@@ -210,30 +224,58 @@ dependencies:
210
224
  name: rubocop
211
225
  requirement: !ruby/object:Gem::Requirement
212
226
  requirements:
213
- - - "<"
227
+ - - "~>"
214
228
  - !ruby/object:Gem::Version
215
- version: 0.91.0
229
+ version: '1.10'
216
230
  type: :development
217
231
  prerelease: false
218
232
  version_requirements: !ruby/object:Gem::Requirement
219
233
  requirements:
220
- - - "<"
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: '1.10'
237
+ - !ruby/object:Gem::Dependency
238
+ name: rubocop-minitest
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '0.10'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: '0.10'
251
+ - !ruby/object:Gem::Dependency
252
+ name: rubocop-rake
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: '0.5'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
221
263
  - !ruby/object:Gem::Version
222
- version: 0.91.0
264
+ version: '0.5'
223
265
  - !ruby/object:Gem::Dependency
224
266
  name: simplecov
225
267
  requirement: !ruby/object:Gem::Requirement
226
268
  requirements:
227
269
  - - "~>"
228
270
  - !ruby/object:Gem::Version
229
- version: '0.17'
271
+ version: '0.21'
230
272
  type: :development
231
273
  prerelease: false
232
274
  version_requirements: !ruby/object:Gem::Requirement
233
275
  requirements:
234
276
  - - "~>"
235
277
  - !ruby/object:Gem::Version
236
- version: '0.17'
278
+ version: '0.21'
237
279
  - !ruby/object:Gem::Dependency
238
280
  name: webmock
239
281
  requirement: !ruby/object:Gem::Requirement
@@ -323,6 +365,9 @@ files:
323
365
  - lib/labclient/ci_lint/ci_lint.rb
324
366
  - lib/labclient/class_helpers.rb
325
367
  - lib/labclient/client.rb
368
+ - lib/labclient/client/helpers.rb
369
+ - lib/labclient/client/meta.rb
370
+ - lib/labclient/client/setup.rb
326
371
  - lib/labclient/commits/cherry_pick.rb
327
372
  - lib/labclient/commits/comment_create.rb
328
373
  - lib/labclient/commits/comments.rb
@@ -528,6 +573,7 @@ files:
528
573
  - lib/labclient/license/delete.rb
529
574
  - lib/labclient/license/license.rb
530
575
  - lib/labclient/license/list.rb
576
+ - lib/labclient/logger.rb
531
577
  - lib/labclient/markdown/markdown.rb
532
578
  - lib/labclient/members/groups/add.rb
533
579
  - lib/labclient/members/groups/delete.rb