geet 0.26.0 → 0.27.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: d83c8506d30f9b5d55cc466860fb8aa0c1b4e2efc0abf644ad9546da9948b514
4
- data.tar.gz: db74f000152d9a001c4980515c3a152f605d34c076a26417081a6eb4622009bb
3
+ metadata.gz: 930e360b5ecee4ddf7ef77018c0e1288dc5dbcd0fea306cc99ebc862954989bd
4
+ data.tar.gz: e3a3d4d6f3cfacffe95c1701677e4a95f8ba3825dbbf03b9f11dfa6e2e8987cf
5
5
  SHA512:
6
- metadata.gz: 5ec6c24fee6db9577c34464e7f92a86be70059b7acb0407883f223f10913235df7b0da2b319fce45af90faed5ae6779c932db54d01ef7856b56f36c90a1e20de
7
- data.tar.gz: 9aa500830eab7372364f903f71b64694481b516087c0425a63b3ba6451b8b52e8c81c92fea49741173ce21028fb8999ff0cd65035b589d066c0ea4df57cc632e
6
+ metadata.gz: d2be595720c7e1288d256931cf4c27e91b5cdfe76641a9a2c87b73060567f363cfbb427dfaea7a2a3f492770710755f3f9f11b9af531f688671f5356f67b0d07
7
+ data.tar.gz: 4c6d651f84651a75434260dc6d743f06204130d65245d6f27ea8a72831789b3e023d7b8f74138e64fccf2a944f991bef5b8b475703bdcfd29a6170ed07314b80
data/Gemfile CHANGED
@@ -13,6 +13,7 @@ end
13
13
 
14
14
  group :test do
15
15
  gem 'rspec', '~> 3.13.0'
16
+ gem 'rspec-sorbet'
16
17
  gem 'vcr', '~> 6.4.0'
17
18
  gem 'webmock', '~> 3.1.1'
18
19
  end
data/geet.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.required_ruby_version = '>= 3.2.0'
10
10
  s.authors = ['Saverio Miroddi']
11
- s.date = '2026-01-14'
11
+ s.date = '2026-01-15'
12
12
  s.email = ['saverio.pub2@gmail.com']
13
13
  s.homepage = 'https://github.com/saveriomiroddi/geet'
14
14
  s.summary = 'Commandline interface for performing SCM host operations, eg. create a PR on GitHub'
@@ -53,7 +53,7 @@ module Geet
53
53
  end
54
54
 
55
55
  def issues(assignee: nil, milestone: nil)
56
- attempt_provider_call(:Issue, :list, api_interface, assignee: assignee, milestone: milestone)
56
+ attempt_provider_call(:Issue, :list, api_interface, assignee:, milestone:)
57
57
  end
58
58
 
59
59
  def create_milestone(title)
@@ -76,7 +76,7 @@ module Geet
76
76
  end
77
77
 
78
78
  def prs(owner: nil, head: nil, milestone: nil)
79
- attempt_provider_call(:PR, :list, api_interface, owner: owner, head: head, milestone: milestone)
79
+ attempt_provider_call(:PR, :list, api_interface, owner:, head:, milestone:)
80
80
  end
81
81
 
82
82
  # Returns the RemoteRepository instance.
@@ -1,12 +1,30 @@
1
1
  # frozen_string_literal: true
2
+ # typed: strict
2
3
 
3
4
  module Geet
4
5
  module Github
5
6
  # For clarity, in this class we keep only the identical logic between the subclasses, but
6
7
  # other methods could be moved in here at some complexity cost.
7
8
  class AbstractIssue
8
- attr_reader :number, :title, :link
9
+ extend T::Sig
9
10
 
11
+ sig { returns(Integer) }
12
+ attr_reader :number
13
+
14
+ sig { returns(String) }
15
+ attr_reader :title
16
+
17
+ sig { returns(String) }
18
+ attr_reader :link
19
+
20
+ sig {
21
+ overridable.params(
22
+ number: Integer,
23
+ api_interface: Geet::Github::ApiInterface,
24
+ title: String,
25
+ link: String
26
+ ).void
27
+ }
10
28
  def initialize(number, api_interface, title, link)
11
29
  @number = number
12
30
  @api_interface = api_interface
@@ -19,6 +37,16 @@ module Geet
19
37
  # This works both for Issues and PRs, however, when the `/pulls` API (path) is used, additional
20
38
  # information is provided (e.g. `head`).
21
39
  #
40
+ sig {
41
+ overridable.params(
42
+ api_interface: Geet::Github::ApiInterface,
43
+ milestone: T.nilable(Geet::Github::Milestone),
44
+ assignee: T.nilable(Geet::Github::User),
45
+ type_filter:
46
+ T.nilable(
47
+ T.proc.params(issue_data: T::Hash[String, T.untyped]).returns(T::Boolean)
48
+ )
49
+ ).returns(T::Array[Geet::Github::AbstractIssue]) }
22
50
  def self.list(api_interface, milestone: nil, assignee: nil, &type_filter)
23
51
  api_path = 'issues'
24
52
 
@@ -26,12 +54,12 @@ module Geet
26
54
  request_params[:milestone] = milestone.number if milestone
27
55
  request_params[:assignee] = assignee.username if assignee
28
56
 
29
- response = api_interface.send_request(api_path, params: request_params, multipage: true)
57
+ response = T.cast(api_interface.send_request(api_path, params: request_params, multipage: true), T::Array[T::Hash[String, T.untyped]])
30
58
 
31
59
  abstract_issues_list = response.map do |issue_data|
32
- number = issue_data.fetch('number')
33
- title = issue_data.fetch('title')
34
- link = issue_data.fetch('html_url')
60
+ number = T.cast(issue_data.fetch('number'), Integer)
61
+ title = T.cast(issue_data.fetch('title'), String)
62
+ link = T.cast(issue_data.fetch('html_url'), String)
35
63
 
36
64
  new(number, api_interface, title, link) if type_filter.nil? || type_filter.call(issue_data)
37
65
  end
@@ -39,9 +67,7 @@ module Geet
39
67
  abstract_issues_list.compact
40
68
  end
41
69
 
42
- # params:
43
- # users: String, or Array of strings.
44
- #
70
+ sig { params(users: T.any(String, T::Array[String])).void }
45
71
  def assign_users(users)
46
72
  api_path = "issues/#{@number}/assignees"
47
73
  request_data = { assignees: Array(users) }
@@ -49,17 +75,16 @@ module Geet
49
75
  @api_interface.send_request(api_path, data: request_data)
50
76
  end
51
77
 
52
- # labels: array of strings.
53
- #
78
+ sig { params(labels: T::Array[String]).void }
54
79
  def add_labels(labels)
55
80
  api_path = "issues/#{@number}/labels"
56
- request_data = labels
57
81
 
58
- @api_interface.send_request(api_path, data: request_data)
82
+ @api_interface.send_request(api_path, data: labels)
59
83
  end
60
84
 
61
85
  # See https://developer.github.com/v3/issues/comments/#create-a-comment
62
86
  #
87
+ sig { params(comment: String).void }
63
88
  def comment(comment)
64
89
  api_path = "issues/#{@number}/comments"
65
90
  request_data = { body: comment }
@@ -69,6 +94,7 @@ module Geet
69
94
 
70
95
  # See https://developer.github.com/v3/issues/#edit-an-issue
71
96
  #
97
+ sig { params(milestone: Integer).void }
72
98
  def edit(milestone:)
73
99
  request_data = { milestone: milestone }
74
100
  api_path = "issues/#{@number}"
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # typed: strict
2
3
 
3
4
  require 'uri'
4
5
  require 'net/http'
@@ -7,21 +8,32 @@ require 'json'
7
8
  module Geet
8
9
  module Github
9
10
  class ApiInterface
10
- API_AUTH_USER = '' # We don't need the login, as the API key uniquely identifies the user
11
- API_BASE_URL = 'https://api.github.com'
12
- GRAPHQL_API_URL = 'https://api.github.com/graphql'
11
+ extend T::Sig
13
12
 
13
+ API_AUTH_USER = T.let('', String) # We don't need the login, as the API key uniquely identifies the user
14
+ API_BASE_URL = T.let('https://api.github.com', String)
15
+ GRAPHQL_API_URL = T.let('https://api.github.com/graphql', String)
16
+
17
+ sig { returns(T.nilable(String)) }
14
18
  attr_reader :repository_path
15
19
 
16
20
  # repo_path: optional for operations that don't require a repository, eg. gist creation.
17
21
  # upstream: boolean; makes sense only when :repo_path is set.
18
22
  #
23
+ sig {
24
+ params(
25
+ api_token: String,
26
+ repo_path: T.nilable(String),
27
+ upstream: T.nilable(T::Boolean)
28
+ ).void
29
+ }
19
30
  def initialize(api_token, repo_path: nil, upstream: nil)
20
- @api_token = api_token
21
- @repository_path = repo_path
22
- @upstream = upstream
31
+ @api_token = T.let(api_token, String)
32
+ @repository_path = T.let(repo_path, T.nilable(String))
33
+ @upstream = T.let(upstream, T.nilable(T::Boolean))
23
34
  end
24
35
 
36
+ sig { returns(T.nilable(T::Boolean)) }
25
37
  def upstream?
26
38
  @upstream
27
39
  end
@@ -45,24 +57,33 @@ module Geet
45
57
  # :get and :post are automatically inferred by the present of :data; the other
46
58
  # cases must be specified.
47
59
  #
60
+ sig {
61
+ params(
62
+ api_path: String,
63
+ params: T.nilable(T::Hash[Symbol, T.untyped]),
64
+ data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped])),
65
+ multipage: T::Boolean,
66
+ http_method: T.nilable(Symbol)
67
+ ).returns(T.any(T::Hash[String, T.untyped], T::Array[T::Hash[String, T.untyped]], NilClass))
68
+ }
48
69
  def send_request(api_path, params: nil, data: nil, multipage: false, http_method: nil)
49
- address = api_url(api_path)
70
+ address = T.let(api_url(api_path), T.nilable(String))
50
71
  # filled only on :multipage
51
- parsed_responses = []
72
+ parsed_responses = T.let([], T::Array[T::Hash[String, T.untyped]])
52
73
 
53
74
  loop do
54
- response = send_http_request(address, params: params, data: data, http_method: http_method)
75
+ response = send_http_request(T.must(address), params:, data:, http_method:)
55
76
 
56
- parsed_response = JSON.parse(response.body) if response.body
77
+ parsed_response = T.unsafe(JSON.parse(T.must(response.body))) if response.body
57
78
 
58
79
  if error?(response)
59
- error_message = decode_and_format_error(parsed_response)
80
+ error_message = decode_and_format_error(T.cast(parsed_response, T::Hash[String, T.untyped]))
60
81
  raise Geet::Shared::HttpError.new(error_message, response.code)
61
82
  end
62
83
 
63
84
  return parsed_response if !multipage
64
85
 
65
- parsed_responses.concat(parsed_response)
86
+ parsed_responses.concat(T.cast(parsed_response, T::Array[T::Hash[String, T.untyped]]))
66
87
 
67
88
  address = link_next_page(response.to_hash)
68
89
 
@@ -78,6 +99,12 @@ module Geet
78
99
  # :query: GraphQL query string
79
100
  # :variables: (Hash) GraphQL variables
80
101
  #
102
+ sig {
103
+ params(
104
+ query: String,
105
+ variables: T::Hash[Symbol, T.untyped]
106
+ ).returns(T::Hash[String, T.untyped])
107
+ }
81
108
  def send_graphql_request(query, variables: {})
82
109
  uri = URI(GRAPHQL_API_URL)
83
110
 
@@ -90,24 +117,32 @@ module Geet
90
117
 
91
118
  response = http.request(request)
92
119
 
93
- parsed_response = JSON.parse(response.body) if response.body
120
+ parsed_response = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
121
+ if response.body
122
+ parsed_response = T.cast(T.unsafe(JSON.parse(response.body)), T::Hash[String, T.untyped])
123
+ end
94
124
 
95
125
  if error?(response)
96
- error_message = decode_and_format_error(parsed_response)
126
+ error_message = decode_and_format_error(T.must(parsed_response))
97
127
  raise Geet::Shared::HttpError.new(error_message, response.code)
98
128
  end
99
129
 
100
130
  if parsed_response&.key?('errors')
101
- error_messages = parsed_response['errors'].map { |err| err['message'] }.join(', ')
131
+ error_messages = T.cast(parsed_response['errors'], T::Array[T::Hash[String, T.untyped]]).map { |err| T.cast(err['message'], String) }.join(', ')
102
132
  raise Geet::Shared::HttpError.new("GraphQL errors: #{error_messages}", response.code)
103
133
  end
104
134
 
105
- parsed_response&.fetch('data')
135
+ T.cast(T.must(parsed_response).fetch('data'), T::Hash[String, T.untyped])
106
136
  end
107
137
  end
108
138
 
109
139
  private
110
140
 
141
+ sig {
142
+ params(
143
+ api_path: String
144
+ ).returns(String)
145
+ }
111
146
  def api_url(api_path)
112
147
  url = API_BASE_URL
113
148
 
@@ -119,6 +154,14 @@ module Geet
119
154
  url + api_path
120
155
  end
121
156
 
157
+ sig {
158
+ params(
159
+ address: String,
160
+ params: T.nilable(T::Hash[Symbol, T.untyped]),
161
+ data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped])),
162
+ http_method: T.nilable(Symbol)
163
+ ).returns(Net::HTTPResponse)
164
+ }
122
165
  def send_http_request(address, params: nil, data: nil, http_method: nil)
123
166
  uri = encode_uri(address, params)
124
167
  http_class = find_http_class(http_method, data)
@@ -134,16 +177,32 @@ module Geet
134
177
  end
135
178
  end
136
179
 
180
+ sig {
181
+ params(
182
+ address: String,
183
+ params: T.nilable(T::Hash[Symbol, T.untyped])
184
+ ).returns(URI::Generic)
185
+ }
137
186
  def encode_uri(address, params)
138
187
  address += '?' + URI.encode_www_form(params) if params
139
188
 
140
189
  URI(address)
141
190
  end
142
191
 
192
+ sig {
193
+ params(
194
+ response: Net::HTTPResponse
195
+ ).returns(T::Boolean)
196
+ }
143
197
  def error?(response)
144
198
  !response.code.start_with?('2')
145
199
  end
146
200
 
201
+ sig {
202
+ params(
203
+ parsed_response: T::Hash[String, T.untyped]
204
+ ).returns(String)
205
+ }
147
206
  def decode_and_format_error(parsed_response)
148
207
  message = parsed_response['message']
149
208
 
@@ -166,6 +225,11 @@ module Geet
166
225
  message
167
226
  end
168
227
 
228
+ sig {
229
+ params(
230
+ response_headers: T::Hash[String, T.untyped]
231
+ ).returns(T.nilable(String))
232
+ }
169
233
  def link_next_page(response_headers)
170
234
  # An array (or nil) is returned.
171
235
  link_header = Array(response_headers['link'])
@@ -175,6 +239,12 @@ module Geet
175
239
  link_header[0][/<(\S+)>; rel="next"/, 1]
176
240
  end
177
241
 
242
+ sig {
243
+ params(
244
+ http_method: T.nilable(Symbol),
245
+ data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped]))
246
+ ).returns(T.class_of(Net::HTTPRequest))
247
+ }
178
248
  def find_http_class(http_method, data)
179
249
  http_method ||= data ? :post : :get
180
250
 
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
+ # typed: strict
2
3
 
3
4
  module Geet
4
5
  module Github
5
6
  class Branch
7
+ extend T::Sig
8
+
6
9
  # See https://developer.github.com/v3/git/refs/#delete-a-reference
7
10
  #
8
- def self.delete(name, api_interface, **)
11
+ sig { params(name: String, api_interface: Geet::Github::ApiInterface).void }
12
+ def self.delete(name, api_interface)
9
13
  api_path = "git/refs/heads/#{name}"
10
14
 
11
15
  api_interface.send_request(api_path, http_method: :delete)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # typed: strict
2
3
 
3
4
  require_relative 'abstract_issue'
4
5
  require_relative '../github/gist'
@@ -6,29 +7,52 @@ require_relative '../github/gist'
6
7
  module Geet
7
8
  module Github
8
9
  class Gist
10
+ extend T::Sig
11
+
12
+ sig {
13
+ params(
14
+ filename: String,
15
+ content: String,
16
+ api_interface: Geet::Github::ApiInterface,
17
+ description: T.nilable(String),
18
+ publik: T::Boolean
19
+ ).returns(Geet::Github::Gist)
20
+ }
9
21
  def self.create(filename, content, api_interface, description: nil, publik: false)
10
22
  api_path = '/gists'
11
23
 
12
24
  request_data = prepare_request_data(filename, content, description, publik)
13
25
 
14
- response = api_interface.send_request(api_path, data: request_data)
26
+ response = T.cast(api_interface.send_request(api_path, data: T.unsafe(request_data)), T::Hash[String, T.untyped])
15
27
 
16
- id = response.fetch('id')
28
+ id = T.cast(response.fetch('id'), String)
17
29
 
18
30
  new(id)
19
31
  end
20
32
 
33
+ sig { params(id: String).void }
21
34
  def initialize(id)
22
35
  @id = id
23
36
  end
24
37
 
38
+ sig { returns(String) }
25
39
  def link
26
40
  "https://gist.github.com/#{@id}"
27
41
  end
28
42
 
29
43
  class << self
44
+ extend T::Sig
45
+
30
46
  private
31
47
 
48
+ sig {
49
+ params(
50
+ filename: String,
51
+ content: String,
52
+ description: T.nilable(String),
53
+ publik: T::Boolean
54
+ ).returns(T::Hash[String, T.untyped])
55
+ }
32
56
  def prepare_request_data(filename, content, description, publik)
33
57
  request_data = {
34
58
  'public' => publik,
@@ -1,20 +1,46 @@
1
1
  # frozen_string_literal: true
2
+ # typed: strict
2
3
 
3
4
  module Geet
4
5
  module Github
5
6
  class Issue < Geet::Github::AbstractIssue
6
- def self.create(title, description, api_interface, **)
7
+ extend T::Sig
8
+
9
+ sig {
10
+ params(
11
+ title: String,
12
+ description: String,
13
+ api_interface: Geet::Github::ApiInterface,
14
+ ).returns(Geet::Github::Issue)
15
+ }
16
+ def self.create(title, description, api_interface)
7
17
  api_path = 'issues'
8
- request_data = { title: title, body: description }
18
+ request_data = { title:, body: description }
9
19
 
10
- response = api_interface.send_request(api_path, data: request_data)
20
+ response = T.cast(
21
+ api_interface.send_request(api_path, data: request_data),
22
+ T::Hash[String, T.untyped]
23
+ )
11
24
 
12
- issue_number, title, link = response.fetch_values('number', 'title', 'html_url')
25
+ issue_number = T.cast(response.fetch('number'), Integer)
26
+ title = T.cast(response.fetch('title'), String)
27
+ link = T.cast(response.fetch('html_url'), String)
13
28
 
14
29
  new(issue_number, api_interface, title, link)
15
30
  end
16
31
 
17
- def self.list(api_interface, assignee: nil, milestone: nil)
32
+ sig {
33
+ override.params(
34
+ api_interface: Geet::Github::ApiInterface,
35
+ assignee: T.nilable(Geet::Github::User),
36
+ milestone: T.nilable(Geet::Github::Milestone),
37
+ type_filter:
38
+ T.nilable(
39
+ T.proc.params(issue_data: T::Hash[String, T.untyped]).returns(T::Boolean)
40
+ )
41
+ ).returns(T::Array[Geet::Github::AbstractIssue])
42
+ }
43
+ def self.list(api_interface, assignee: nil, milestone: nil, &type_filter)
18
44
  super do |issue_data|
19
45
  !issue_data.key?('pull_request')
20
46
  end
@@ -1,32 +1,56 @@
1
1
  # frozen_string_literal: true
2
+ # typed: strict
2
3
 
3
4
  module Geet
4
5
  module Github
5
6
  class Label
6
- attr_reader :name, :color
7
+ extend T::Sig
7
8
 
9
+ sig { returns(String) }
10
+ attr_reader :name
11
+
12
+ sig { returns(String) }
13
+ attr_reader :color
14
+
15
+ sig {
16
+ params(
17
+ name: String,
18
+ color: String
19
+ ).void
20
+ }
8
21
  def initialize(name, color)
9
22
  @name = name
10
23
  @color = color
11
24
  end
12
25
 
13
- # Returns a flat list of names in string form.
14
- def self.list(api_interface, **)
26
+ sig {
27
+ params(
28
+ api_interface: Geet::Github::ApiInterface
29
+ ).returns(T::Array[Geet::Github::Label])
30
+ }
31
+ def self.list(api_interface)
15
32
  api_path = 'labels'
16
- response = api_interface.send_request(api_path, multipage: true)
33
+ response = T.cast(api_interface.send_request(api_path, multipage: true), T::Array[T::Hash[String, T.untyped]])
17
34
 
18
35
  response.map do |label_entry|
19
- name = label_entry.fetch('name')
20
- color = label_entry.fetch('color')
36
+ name = T.cast(label_entry.fetch('name'), String)
37
+ color = T.cast(label_entry.fetch('color'), String)
21
38
 
22
39
  new(name, color)
23
40
  end
24
41
  end
25
42
 
26
43
  # See https://developer.github.com/v3/issues/labels/#create-a-label
27
- def self.create(name, color, api_interface, **)
44
+ sig {
45
+ params(
46
+ name: String,
47
+ color: String,
48
+ api_interface: Geet::Github::ApiInterface
49
+ ).returns(Geet::Github::Label)
50
+ }
51
+ def self.create(name, color, api_interface)
28
52
  api_path = 'labels'
29
- request_data = { name: name, color: color }
53
+ request_data = { name:, color: }
30
54
 
31
55
  api_interface.send_request(api_path, data: request_data)
32
56
 
@@ -1,18 +1,38 @@
1
1
  # frozen_string_literal: true
2
+ # typed: strict
2
3
 
3
4
  module Geet
4
5
  module Github
5
6
  class Milestone
6
- attr_reader :number, :title, :due_on
7
+ extend T::Sig
8
+
9
+ sig { returns(Integer) }
10
+ attr_reader :number
11
+
12
+ sig { returns(String) }
13
+ attr_reader :title
14
+
15
+ sig { returns(T.nilable(Date)) }
16
+ attr_reader :due_on
7
17
 
8
18
  STATE_CLOSED = 'closed'
9
19
 
10
20
  class << self
21
+ extend T::Sig
22
+
11
23
  private
12
24
 
13
25
  include Helpers::JsonHelper
14
26
  end
15
27
 
28
+ sig {
29
+ params(
30
+ number: Integer,
31
+ title: String,
32
+ due_on: T.nilable(Date),
33
+ api_interface: Geet::Github::ApiInterface
34
+ ).void
35
+ }
16
36
  def initialize(number, title, due_on, api_interface)
17
37
  @number = number
18
38
  @title = title
@@ -22,14 +42,23 @@ module Geet
22
42
  end
23
43
 
24
44
  # See https://developer.github.com/v3/issues/milestones/#create-a-milestone
25
- def self.create(title, api_interface, **)
45
+ sig {
46
+ params(
47
+ title: String,
48
+ api_interface: Geet::Github::ApiInterface
49
+ ).returns(Geet::Github::Milestone)
50
+ }
51
+ def self.create(title, api_interface)
26
52
  api_path = 'milestones'
27
53
  request_data = { title: title }
28
54
 
29
- response = api_interface.send_request(api_path, data: request_data)
55
+ response = T.cast(
56
+ api_interface.send_request(api_path, data: request_data),
57
+ T::Hash[String, T.untyped]
58
+ )
30
59
 
31
- number = response.fetch('number')
32
- title = response.fetch('title')
60
+ number = T.cast(response.fetch('number'), Integer)
61
+ title = T.cast(response.fetch('title'), String)
33
62
  due_on = nil
34
63
 
35
64
  new(number, title, due_on, api_interface)
@@ -37,15 +66,25 @@ module Geet
37
66
 
38
67
  # See https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
39
68
  #
40
- def self.list(api_interface, **)
69
+ sig {
70
+ params(
71
+ api_interface: Geet::Github::ApiInterface
72
+ ).returns(T::Array[Geet::Github::Milestone])
73
+ }
74
+ def self.list(api_interface)
41
75
  api_path = 'milestones'
42
76
 
43
- response = api_interface.send_request(api_path, multipage: true)
77
+ response = T.cast(
78
+ api_interface.send_request(api_path, multipage: true),
79
+ T::Array[T::Hash[String, T.untyped]]
80
+ )
44
81
 
45
82
  response.map do |milestone_data|
46
- number = milestone_data.fetch('number')
47
- title = milestone_data.fetch('title')
48
- due_on = parse_iso_8601_timestamp(milestone_data.fetch('due_on'))
83
+ number = T.cast(milestone_data.fetch('number'), Integer)
84
+ title = T.cast(milestone_data.fetch('title'), String)
85
+ due_on = parse_iso_8601_timestamp(
86
+ T.cast(milestone_data.fetch('due_on'), T.nilable(String))
87
+ )
49
88
 
50
89
  new(number, title, due_on, api_interface)
51
90
  end
@@ -55,7 +94,13 @@ module Geet
55
94
  #
56
95
  # This is a convenience method; the underlying operation is a generic update.
57
96
  #
58
- def self.close(number, api_interface, **)
97
+ sig {
98
+ params(
99
+ number: Integer,
100
+ api_interface: Geet::Github::ApiInterface
101
+ ).void
102
+ }
103
+ def self.close(number, api_interface)
59
104
  api_path = "milestones/#{number}"
60
105
  request_data = { state: STATE_CLOSED }
61
106