smart_todo 1.9.1 → 1.9.2

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: 8d203126f1877df2db5e717ea31690fc81f42efe1da8c3286ac619e946f6bd75
4
- data.tar.gz: cd9e9bbc4ab3a93db418d16296a9c634f55b571ad5dfe159ed8b25b6ec37cc22
3
+ metadata.gz: beb68e6ca2807c8c2fb782fb34109d5abe45f3e306cab7ad2f34b211f6a250aa
4
+ data.tar.gz: cc9978ba39c42bf194de59836c945197d066537187e34285857758bfcaf63ed5
5
5
  SHA512:
6
- metadata.gz: 89fc8bb08ed5ea7be2590b8bcfc38bd2e5cc75e2da96c200b70a4a7308a77b2ab2592d8190433d1d9a97e649e20f2bc6a965fb48161223e9b3d9d93e32ae4c30
7
- data.tar.gz: cc43abab984dfb7bfd1c97fdcfb0ea7bf045522ee328f46527fb15cfe899e64bd62ead2f63a9a5135f881a4ad757d84948aea23f7a89271e314241a5d8d3e9cf
6
+ metadata.gz: 580080fabbbcc83ad8f372e56b5d4c2238688fc4617edd1501ac3032ddd61497db9e9c2a648f7a7b32acdda27790fa63fe0eb5c5c08d8b97c2e72116f2868f92
7
+ data.tar.gz: 6593132b64a848caa0205518b8c2124d04e6099b225af92e18ff997b5d204c1518c70c556280b766f3d6ea639bf9d4b44460152230f37baeda08f49f707bc889
@@ -15,7 +15,7 @@ jobs:
15
15
  version: [3.0, 3.1, 3.2, 3.3]
16
16
 
17
17
  steps:
18
- - uses: actions/checkout@v2
18
+ - uses: actions/checkout@v4
19
19
  - name: Set up Ruby ${{ matrix.version }}
20
20
  uses: ruby/setup-ruby@v1
21
21
  with:
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: ubuntu-latest
8
8
 
9
9
  steps:
10
- - uses: actions/checkout@v2
10
+ - uses: actions/checkout@v4
11
11
  - name: Set up Ruby
12
12
  uses: ruby/setup-ruby@v1
13
13
  with:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_todo (1.9.1)
4
+ smart_todo (1.9.2)
5
5
  prism (~> 1.0)
6
6
 
7
7
  GEM
@@ -9,44 +9,47 @@ GEM
9
9
  specs:
10
10
  addressable (2.8.7)
11
11
  public_suffix (>= 2.0.2, < 7.0)
12
- ast (2.4.2)
12
+ ast (2.4.3)
13
13
  bigdecimal (3.1.9)
14
14
  crack (1.0.0)
15
15
  bigdecimal
16
16
  rexml
17
17
  hashdiff (1.1.2)
18
- json (2.7.1)
19
- language_server-protocol (3.17.0.3)
20
- minitest (5.25.4)
21
- parallel (1.24.0)
22
- parser (3.3.0.5)
18
+ json (2.10.2)
19
+ language_server-protocol (3.17.0.4)
20
+ lint_roller (1.1.0)
21
+ minitest (5.25.5)
22
+ parallel (1.26.3)
23
+ parser (3.3.7.3)
23
24
  ast (~> 2.4.1)
24
25
  racc
25
- prism (1.0.0)
26
+ prism (1.4.0)
26
27
  public_suffix (6.0.1)
27
- racc (1.7.3)
28
+ racc (1.8.1)
28
29
  rainbow (3.1.1)
29
- rake (13.0.6)
30
- regexp_parser (2.9.0)
31
- rexml (3.4.0)
32
- rubocop (1.62.1)
30
+ rake (13.2.1)
31
+ regexp_parser (2.10.0)
32
+ rexml (3.4.1)
33
+ rubocop (1.74.0)
33
34
  json (~> 2.3)
34
- language_server-protocol (>= 3.17.0)
35
+ language_server-protocol (~> 3.17.0.2)
36
+ lint_roller (~> 1.1.0)
35
37
  parallel (~> 1.10)
36
38
  parser (>= 3.3.0.2)
37
39
  rainbow (>= 2.2.2, < 4.0)
38
- regexp_parser (>= 1.8, < 3.0)
39
- rexml (>= 3.2.5, < 4.0)
40
- rubocop-ast (>= 1.31.1, < 2.0)
40
+ regexp_parser (>= 2.9.3, < 3.0)
41
+ rubocop-ast (>= 1.38.0, < 2.0)
41
42
  ruby-progressbar (~> 1.7)
42
- unicode-display_width (>= 2.4.0, < 3.0)
43
- rubocop-ast (1.31.2)
44
- parser (>= 3.3.0.4)
45
- rubocop-shopify (2.14.0)
43
+ unicode-display_width (>= 2.4.0, < 4.0)
44
+ rubocop-ast (1.42.0)
45
+ parser (>= 3.3.7.2)
46
+ rubocop-shopify (2.15.1)
46
47
  rubocop (~> 1.51)
47
48
  ruby-progressbar (1.13.0)
48
- unicode-display_width (2.5.0)
49
- webmock (3.24.0)
49
+ unicode-display_width (3.1.4)
50
+ unicode-emoji (~> 4.0, >= 4.0.4)
51
+ unicode-emoji (4.0.4)
52
+ webmock (3.25.1)
50
53
  addressable (>= 2.8.0)
51
54
  crack (>= 0.3.2)
52
55
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -35,15 +35,17 @@ module SmartTodo
35
35
  def dispatch_one(assignee)
36
36
  user = slack_user_or_channel(assignee)
37
37
 
38
- client.post_message(user.dig("user", "id"), slack_message(user, assignee))
39
- rescue SlackClient::Error => error
40
- if ["users_not_found", "channel_not_found", "is_archived"].include?(error.error_code)
41
- user = { "user" => { "id" => @options[:fallback_channel] }, "fallback" => true }
42
- else
43
- raise(error)
44
- end
38
+ return unless user
45
39
 
46
- client.post_message(user.dig("user", "id"), slack_message(user, assignee))
40
+ begin
41
+ client.post_message(user.dig("user", "id"), slack_message(user, assignee))
42
+ rescue SlackClient::Error => error
43
+ user = handle_slack_error(error, "Error dispatching message")
44
+ retry
45
+ rescue Net::HTTPError => error
46
+ $stderr.puts "Error dispatching message: #{error.message}"
47
+ $stderr.puts "Response: #{error.response.body}"
48
+ end
47
49
  end
48
50
 
49
51
  private
@@ -58,12 +60,25 @@ module SmartTodo
58
60
  else
59
61
  { "user" => { "id" => assignee } }
60
62
  end
63
+ rescue SlackClient::Error => error
64
+ handle_slack_error(error, "Error finding user or channel")
65
+ rescue Net::HTTPError => error
66
+ $stderr.puts "Error finding user or channel: #{error.message}"
67
+ $stderr.puts "Response: #{error.response.body}"
61
68
  end
62
69
 
63
70
  # @return [SlackClient] an instance of SlackClient
64
71
  def client
65
72
  @client ||= SlackClient.new(@options[:slack_token])
66
73
  end
74
+
75
+ def handle_slack_error(error, message)
76
+ if ["users_not_found", "channel_not_found", "is_archived"].include?(error.error_code)
77
+ { "user" => { "id" => @options[:fallback_channel] }, "fallback" => true }
78
+ else
79
+ $stderr.puts "#{message}: #{error.message}"
80
+ end
81
+ end
67
82
  end
68
83
  end
69
84
  end
@@ -32,16 +32,17 @@ module SmartTodo
32
32
  # Retrieve the Slack ID of a user from his email
33
33
  #
34
34
  # @param email [String]
35
+ # @param min_sleep [Integer] - the minimum sleep time in seconds in case of rate limiting
35
36
  # @return [Hash]
36
37
  #
37
38
  # @raise [Net::HTTPError] in case the request to Slack failed
38
39
  # @raise [SlackClient::Error] in case Slack returns a { ok: false } in the body
39
40
  #
40
41
  # @see https://api.slack.com/methods/users.lookupByEmail
41
- def lookup_user_by_email(email)
42
+ def lookup_user_by_email(email, min_sleep = 30)
42
43
  headers = default_headers.merge("Content-Type" => "application/x-www-form-urlencoded")
43
44
  request = Net::HTTP::Get.new("/api/users.lookupByEmail?email=#{CGI.escape(email)}", headers)
44
- dispatch(request)
45
+ dispatch(request, 5, min_sleep)
45
46
  end
46
47
 
47
48
  # Send a message to a Slack channel or to a user
@@ -63,25 +64,27 @@ module SmartTodo
63
64
 
64
65
  private
65
66
 
66
- # @param method [Symbol]
67
- # @param endpoint [String]
68
- # @param data [String] JSON encoded data when making a POST request
69
- # @param headers [Hash]
67
+ # @param request [Net::HTTPRequest]
68
+ # @param max_attempts [Integer] - the maximum number of attempts to make
69
+ # @param min_sleep [Integer] - the minimum sleep time in seconds in case of rate limiting
70
70
  #
71
71
  # @raise [Net::HTTPError] in case the request to Slack failed
72
72
  # @raise [SlackClient::Error] in case Slack returns a { ok: false } in the body
73
- def dispatch(request, max_attempts = 5)
73
+ def dispatch(request, max_attempts = 5, min_sleep = 30)
74
74
  response = @client.request(request)
75
75
  attempts = 1
76
76
 
77
77
  while response.is_a?(Net::HTTPTooManyRequests) && attempts < max_attempts
78
- sleep([Integer(response["Retry-After"]), 600].min)
78
+ sleep_time = Integer(response["Retry-After"]).clamp(min_sleep, 120)
79
+ puts "Rate limited, sleeping for #{sleep_time} seconds"
80
+ sleep(sleep_time)
79
81
  response = @client.request(request)
80
82
  attempts += 1
81
83
  end
82
84
 
83
85
  unless response.code_type < Net::HTTPSuccess
84
- raise(Net::HTTPError.new("Request to slack failed", response))
86
+ message = "Request to slack failed #{response.body}, code #{response.code}, attempt #{attempts}"
87
+ raise(Net::HTTPError.new(message, response))
85
88
  end
86
89
 
87
90
  body = JSON.parse(response.body)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SmartTodo
4
- VERSION = "1.9.1"
4
+ VERSION = "1.9.2"
5
5
  end
@@ -27,6 +27,8 @@ module RuboCop
27
27
  add_offense(comment)
28
28
  elsif (methods = invalid_event_methods(metadata.events)).any?
29
29
  add_offense(comment, message: "Invalid event method(s): #{methods.join(", ")}. #{HELP}")
30
+ elsif invalid_assignees(metadata.assignees).any?
31
+ add_offense(comment, message: "Invalid event assignee. This method only accepts strings. #{HELP}")
30
32
  end
31
33
  end
32
34
  end
@@ -52,6 +54,12 @@ module RuboCop
52
54
  def invalid_event_methods(events)
53
55
  events.map(&:method_name).reject { |method| ::SmartTodo::Events.method_defined?(method) }
54
56
  end
57
+
58
+ # @param assignees [Array]
59
+ # @return [Array]
60
+ def invalid_assignees(assignees)
61
+ assignees.reject { |assignee| assignee.is_a?(String) }
62
+ end
55
63
  end
56
64
  end
57
65
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_todo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-04 00:00:00.000000000 Z
10
+ date: 2025-03-31 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: prism
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  - !ruby/object:Gem::Version
148
148
  version: '0'
149
149
  requirements: []
150
- rubygems_version: 3.6.3
150
+ rubygems_version: 3.6.6
151
151
  specification_version: 4
152
152
  summary: Enhance todo's comments in your codebase.
153
153
  test_files: []