labclient 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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