checkoff 0.265.0 → 0.267.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: 43afa4bb02fd82f7f199254d12c4f489377a9cd527b2ca5e38a94e5b845e6055
4
- data.tar.gz: a796db5e7e98d6f73b051475a4c2f891ca648f8179b973bcd1be935f0fc990f0
3
+ metadata.gz: cbab55444b46fb8c7b10d95bb86294e10d19f6f28ecf909d9362e2e814bcc24e
4
+ data.tar.gz: be2e338d41917f0207fd8e0fc363a83ae1f567f11b32a1b1aacb7db60a23f01a
5
5
  SHA512:
6
- metadata.gz: 0127c901a6b621d61251c5052f694e65913911f2f87b9a96b25cdf8c88b0dcd46d38a5aa20fea4a94c73a54b3f82807dd9ae9e480ccf1fcd1ace550e47051d69
7
- data.tar.gz: fc574a52669db73bd063fa97b6e3fd33a1c4b5e379ebcc901328b10f2be0e3a00ae1c9301fa6d409b0e2ce76c044e52f0fe38b16611f935285ca226e6b5050b8
6
+ metadata.gz: 5eaea449f58a098495c90e20a120a7b04daa06e94520861f842283169f756fab5f4601d4d09341408d64a162705c3119d60ad123c9dd0db78f38dbb44fc17f81
7
+ data.tar.gz: 54061f77cb640e118d6f613170407e0284d011cddfa8591776de716cf315eb80c1110b1f156314429697ca350d9156d974e70f5ed0be59703a6b2079b23ae909
@@ -166,7 +166,7 @@ module Checkoff
166
166
  # https://github.com/Asana/ruby-asana/blob/master/lib/asana/resource_includes/response_helper.rb#L7
167
167
  # @param response [Asana::HttpClient::Response]
168
168
  #
169
- # @return [Array<Hash, Hash>]
169
+ # @return [Array(Hash, Hash)]
170
170
  def parse(response)
171
171
  data = response.body.fetch('data') do
172
172
  raise("Unexpected response body: #{response.body}")
@@ -91,7 +91,7 @@ module Checkoff
91
91
  # @param custom_field_name [String]
92
92
  # @return [Hash{String => Object}, nil]
93
93
  def resource_custom_field_by_name(resource, custom_field_name)
94
- # @type [Array<Hash{String => Object}>]
94
+ # @type [Array<Hash{String => Object}>, nil]
95
95
  custom_fields = resource.custom_fields
96
96
  if custom_fields.nil?
97
97
  raise "custom fields not found on resource - did you add 'custom_fields' in your extra_fields argument?"
@@ -117,7 +117,7 @@ module Checkoff
117
117
  # @param custom_field_gid [String]
118
118
  # @return [Hash{String => Object}]
119
119
  def resource_custom_field_by_gid_or_raise(resource, custom_field_gid)
120
- # @type [Array<Hash{String => Object}>]
120
+ # @type [Array<Hash{String => Object}>, nil]
121
121
  custom_fields = resource.custom_fields
122
122
  if custom_fields.nil?
123
123
  raise "Could not find custom_fields under project (was 'custom_fields' included in 'extra_fields'?)"
@@ -0,0 +1,25 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Checkoff
5
+ module Internal
6
+ # Reads the project/section memberships of an Asana task
7
+ class Memberships
8
+ # Asana returns nil for memberships unless they were requested via
9
+ # 'fields' / extra_fields; raise a clear error instead.
10
+ #
11
+ # @param task [Asana::Resources::Task]
12
+ #
13
+ # @return [Array<Hash{String => Hash{String => String}}>]
14
+ def memberships_or_raise(task)
15
+ memberships = task.memberships
16
+ if memberships.nil?
17
+ raise "memberships not found on task #{task.gid} - " \
18
+ "did you add 'memberships' in your extra_fields argument?"
19
+ end
20
+
21
+ memberships
22
+ end
23
+ end
24
+ end
25
+ end
@@ -190,7 +190,7 @@ module Checkoff
190
190
 
191
191
  empty_task_selector = []
192
192
  if selected_options.length == 1
193
- [{ "custom_fields.#{gid}.value" => selected_options[0] },
193
+ [{ "custom_fields.#{gid}.value" => selected_options.fetch(0) },
194
194
  empty_task_selector]
195
195
  else
196
196
  # As of 2023-01,
@@ -3,6 +3,7 @@
3
3
 
4
4
  require_relative 'task/function_evaluator'
5
5
  require 'checkoff/internal/task_timing'
6
+ require 'checkoff/internal/memberships'
6
7
 
7
8
  module Checkoff
8
9
  module SelectorClasses
@@ -98,7 +99,7 @@ module Checkoff
98
99
  # @param project_name [String]
99
100
  # @return [Boolean]
100
101
  def evaluate(task, project_name)
101
- project_names = task.memberships.map do |membership|
102
+ project_names = Checkoff::Internal::Memberships.new.memberships_or_raise(task).map do |membership|
102
103
  membership.fetch('project').fetch('name')
103
104
  end
104
105
  project_names.include?(project_name)
@@ -242,10 +243,21 @@ module Checkoff
242
243
  end
243
244
  return true if stories.empty? # no stories == infinitely old!
244
245
 
245
- last_story = T.must(stories.last)
246
- last_story_created_at = Time.parse(last_story.created_at)
246
+ last_story = stories.last
247
247
  n_days_ago = Time.at(Time.now.to_i - (num_days * 86_400))
248
- last_story_created_at < n_days_ago
248
+ # @sg-ignore https://github.com/apiology/solargraph/pull/175
249
+ created_time(last_story) < n_days_ago
250
+ end
251
+
252
+ private
253
+
254
+ # @param story [Asana::Resources::Story]
255
+ # @return [Time]
256
+ def created_time(story)
257
+ created_at = story.created_at
258
+ raise "Story #{story.gid} has no created_at" if created_at.nil?
259
+
260
+ Time.parse(created_at)
249
261
  end
250
262
  end
251
263
 
@@ -78,9 +78,8 @@ module Checkoff
78
78
  def portfolio_by_gid(portfolio_gid,
79
79
  extra_fields: [])
80
80
  options = {
81
- fields: ['name'],
81
+ fields: ['name'] + extra_fields,
82
82
  }
83
- options[:fields] += extra_fields
84
83
  client.portfolios.find_by_id(portfolio_gid, options:)
85
84
  end
86
85
  cache_method :portfolio_by_gid, SHORT_CACHE_TIME
@@ -7,6 +7,7 @@ require_relative 'workspaces'
7
7
  require_relative 'clients'
8
8
  require_relative 'my_tasks'
9
9
  require_relative 'internal/logging'
10
+ require_relative 'internal/memberships'
10
11
 
11
12
  module Checkoff
12
13
  # Query different sections of Asana projects
@@ -186,7 +187,8 @@ module Checkoff
186
187
  #
187
188
  # @return [Asana::Resources::Section, nil]
188
189
  def previous_section(section)
189
- project_sections = sections_by_project_gid(section.project.fetch('gid'))
190
+ project = section_project_or_raise(section)
191
+ project_sections = sections_by_project_gid(project.fetch('gid'))
190
192
 
191
193
  # @type [Array<Asana::Resources::Section>]
192
194
  project_sections_arr = project_sections.to_a
@@ -232,10 +234,27 @@ module Checkoff
232
234
 
233
235
  include Logging
234
236
 
237
+ # Asana returns nil for a section's project unless it was requested
238
+ # via 'fields' / extra_fields; raise a clear error instead.
239
+ #
240
+ # @param section [Asana::Resources::Section]
241
+ #
242
+ # @return [Hash{String => String}]
243
+ def section_project_or_raise(section)
244
+ # @type [Hash{String => String}, nil]
245
+ project = section.project
246
+ if project.nil?
247
+ raise "project not found on section #{section.gid} - " \
248
+ "did you add 'project' in your extra_fields argument?"
249
+ end
250
+
251
+ project
252
+ end
253
+
235
254
  # https://github.com/Asana/ruby-asana/blob/master/lib/asana/resource_includes/response_helper.rb#L7
236
255
  # @param response [Asana::HttpClient::Response]
237
256
  #
238
- # @return [Array<Hash, Hash>]
257
+ # @return [Array(Hash, Hash)]
239
258
  def parse(response)
240
259
  data = response.body.fetch('data') do
241
260
  raise("Unexpected response body: #{response.body}")
@@ -282,7 +301,7 @@ module Checkoff
282
301
  # @param project_gid [String]
283
302
  # @return [void]
284
303
  def file_task_by_section(by_section, task, project_gid)
285
- membership = task.memberships.find do |m|
304
+ membership = memberships.memberships_or_raise(task).find do |m|
286
305
  m.fetch('project')['gid'] == project_gid
287
306
  end
288
307
  raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
@@ -309,5 +328,10 @@ module Checkoff
309
328
  end
310
329
  project
311
330
  end
331
+
332
+ # @return [Checkoff::Internal::Memberships]
333
+ def memberships
334
+ @memberships ||= Checkoff::Internal::Memberships.new
335
+ end
312
336
  end
313
337
  end
data/lib/checkoff/tags.rb CHANGED
@@ -123,7 +123,7 @@ module Checkoff
123
123
  #
124
124
  # @param response [Asana::HttpClient::Response]
125
125
  #
126
- # @return [Array<Hash, Hash>]
126
+ # @return [Array(Array<Hash>, Hash)]
127
127
  def parse(response)
128
128
  data = response.body.fetch('data')
129
129
  extra = response.body.except('data')
@@ -171,7 +171,10 @@ module Checkoff
171
171
 
172
172
  break if oldest.nil?
173
173
 
174
- new_api_params['created_at.before'] = oldest.created_at
174
+ oldest_created_at = oldest.created_at
175
+ raise "Task #{oldest.gid} has no created_at" if oldest_created_at.nil?
176
+
177
+ new_api_params['created_at.before'] = oldest_created_at
175
178
 
176
179
  tasks.concat(task_batch.to_a)
177
180
  end
@@ -10,6 +10,7 @@ require_relative 'workspaces'
10
10
  require_relative 'internal/config_loader'
11
11
  require_relative 'internal/task_timing'
12
12
  require_relative 'internal/task_hashes'
13
+ require_relative 'internal/memberships'
13
14
  require_relative 'internal/logging'
14
15
  require_relative 'internal/thread_local'
15
16
  require 'asana'
@@ -297,7 +298,7 @@ module Checkoff
297
298
  portfolio_name,
298
299
  workspace_name: T.must(@workspaces.default_workspace.name))
299
300
  portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
300
- task.memberships.any? do |membership|
301
+ memberships.memberships_or_raise(task).any? do |membership|
301
302
  project_gid = membership.fetch('project').fetch('gid')
302
303
  portfolio_projects.any? { |portfolio_project| portfolio_project.gid == project_gid }
303
304
  end
@@ -315,7 +316,7 @@ module Checkoff
315
316
  portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
316
317
  portfolio_project_gids = portfolio_projects.map(&:gid)
317
318
  seen = T.let(false, T::Boolean)
318
- task.memberships.each do |membership|
319
+ memberships.memberships_or_raise(task).each do |membership|
319
320
  project_gid = membership.fetch('project').fetch('gid')
320
321
  next unless portfolio_project_gids.include?(project_gid)
321
322
  return true if seen
@@ -344,6 +345,11 @@ module Checkoff
344
345
  @task_hashes ||= Checkoff::Internal::TaskHashes.new
345
346
  end
346
347
 
348
+ # @return [Checkoff::Internal::Memberships]
349
+ def memberships
350
+ @memberships ||= Checkoff::Internal::Memberships.new
351
+ end
352
+
347
353
  # @param workspace_name [String, Symbol]
348
354
  # @param project_name [String, Symbol]
349
355
  # @param section_name [String, nil, Symbol] - :unspecified
@@ -6,6 +6,7 @@
6
6
  require 'forwardable'
7
7
  require 'cache_method'
8
8
  require_relative 'internal/config_loader'
9
+ require_relative 'internal/memberships'
9
10
  require_relative 'workspaces'
10
11
  require_relative 'clients'
11
12
 
@@ -73,9 +74,7 @@ module Checkoff
73
74
  end
74
75
 
75
76
  all_dependent_task_gids = nil
76
- # @type [Array<Hash{String => Object}>]
77
- memberships = task.memberships
78
- memberships.all? do |membership_data|
77
+ memberships.memberships_or_raise(task).all? do |membership_data|
79
78
  # @type [Hash{String => Object}]
80
79
  md = membership_data
81
80
  unless limit_to_portfolio_name.nil?
@@ -110,9 +109,7 @@ module Checkoff
110
109
  end
111
110
 
112
111
  all_dependent_milestones = nil
113
- # @type [Array<Hash{String => Object}>]
114
- memberships = task.memberships
115
- memberships.all? do |membership_data|
112
+ memberships.memberships_or_raise(task).all? do |membership_data|
116
113
  # @type [Hash{String => Object}]
117
114
  md = membership_data
118
115
  unless limit_to_portfolio_name.nil?
@@ -132,7 +129,7 @@ module Checkoff
132
129
  end
133
130
 
134
131
  all_dependent_milestones.any? do |milestone|
135
- milestone.memberships.any? do |milestone_membership_data|
132
+ memberships.memberships_or_raise(milestone).any? do |milestone_membership_data|
136
133
  milestone_membership_data.fetch('project').fetch('gid') == project_gid
137
134
  end
138
135
  end
@@ -176,6 +173,11 @@ module Checkoff
176
173
  # @return [Asana::Client]
177
174
  attr_reader :client
178
175
 
176
+ # @return [Checkoff::Internal::Memberships]
177
+ def memberships
178
+ @memberships ||= Checkoff::Internal::Memberships.new
179
+ end
180
+
179
181
  # bundle exec ./timelines.rb
180
182
  # :nocov:
181
183
  class << self
@@ -4,5 +4,5 @@
4
4
  # Command-line and gem client for Asana (unofficial)
5
5
  module Checkoff
6
6
  # Version of library
7
- VERSION = '0.265.0'
7
+ VERSION = '0.267.0'
8
8
  end