checkoff 0.258.0 → 0.261.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/checkoff/attachments.rb +14 -15
  3. data/lib/checkoff/cli.rb +7 -12
  4. data/lib/checkoff/clients.rb +0 -2
  5. data/lib/checkoff/custom_fields.rb +13 -24
  6. data/lib/checkoff/internal/asana_event_enrichment.rb +11 -21
  7. data/lib/checkoff/internal/asana_event_filter.rb +1 -5
  8. data/lib/checkoff/internal/config_loader.rb +0 -2
  9. data/lib/checkoff/internal/logging.rb +0 -2
  10. data/lib/checkoff/internal/project_hashes.rb +26 -4
  11. data/lib/checkoff/internal/search_url/custom_field_param_converter.rb +9 -10
  12. data/lib/checkoff/internal/search_url/custom_field_variant.rb +7 -2
  13. data/lib/checkoff/internal/search_url/date_param_converter.rb +13 -10
  14. data/lib/checkoff/internal/search_url/parser.rb +2 -7
  15. data/lib/checkoff/internal/search_url/simple_param_converter.rb +1 -4
  16. data/lib/checkoff/internal/section_selector_evaluator.rb +1 -1
  17. data/lib/checkoff/internal/selector_classes/common/function_evaluator.rb +2 -2
  18. data/lib/checkoff/internal/selector_classes/common.rb +2 -3
  19. data/lib/checkoff/internal/selector_classes/function_evaluator.rb +9 -4
  20. data/lib/checkoff/internal/selector_classes/project/function_evaluator.rb +2 -2
  21. data/lib/checkoff/internal/selector_classes/project.rb +0 -1
  22. data/lib/checkoff/internal/selector_classes/section/function_evaluator.rb +2 -2
  23. data/lib/checkoff/internal/selector_classes/section.rb +1 -2
  24. data/lib/checkoff/internal/selector_classes/task/function_evaluator.rb +2 -2
  25. data/lib/checkoff/internal/selector_classes/task.rb +2 -26
  26. data/lib/checkoff/internal/selector_evaluator.rb +6 -5
  27. data/lib/checkoff/internal/task_hashes.rb +17 -9
  28. data/lib/checkoff/internal/task_timing.rb +0 -4
  29. data/lib/checkoff/internal/thread_local.rb +0 -2
  30. data/lib/checkoff/monkeypatches/resource_marshalling.rb +3 -3
  31. data/lib/checkoff/my_tasks.rb +1 -4
  32. data/lib/checkoff/portfolios.rb +0 -1
  33. data/lib/checkoff/project_selectors.rb +1 -1
  34. data/lib/checkoff/projects.rb +19 -8
  35. data/lib/checkoff/resources.rb +14 -3
  36. data/lib/checkoff/sections.rb +9 -9
  37. data/lib/checkoff/subtasks.rb +1 -2
  38. data/lib/checkoff/tags.rb +3 -4
  39. data/lib/checkoff/task_searches.rb +1 -8
  40. data/lib/checkoff/task_selectors.rb +25 -12
  41. data/lib/checkoff/tasks.rb +6 -14
  42. data/lib/checkoff/timelines.rb +3 -20
  43. data/lib/checkoff/timing.rb +1 -2
  44. data/lib/checkoff/version.rb +1 -1
  45. data/lib/checkoff/workspaces.rb +1 -7
  46. data/rbi/checkoff.rbi +99 -146
  47. data/sig/checkoff.rbs +86 -153
  48. metadata +2 -2
@@ -73,9 +73,6 @@ module Checkoff
73
73
  workspace = workspaces.workspace_or_raise(workspace_name)
74
74
  api_params, task_selector = @search_url_parser.convert_params(url)
75
75
  debug { "Task search params: api_params=#{api_params}, task_selector=#{task_selector}" }
76
- # @sg-ignore task_selector expected Symbol,Array<Symbol,Integer,Array> received
77
- # Hash{String=>String} — Solargraph mis-assigns tuple-destructured types from
78
- # `api_params, task_selector = @search_url_parser.convert_params(url)` above
79
76
  raw_task_search(api_params, workspace_gid: workspace.gid, task_selector:,
80
77
  extra_fields:)
81
78
  end
@@ -189,12 +186,8 @@ module Checkoff
189
186
 
190
187
  # @param [Array<String>] extra_fields
191
188
  # @return [Hash{Symbol => Array<String>}]
192
- # @sg-ignore return type could not be inferred — bracket access on a Hash{Symbol=>undefined}
193
- # local doesn't narrow even with a T.cast on the return expression
194
189
  def calculate_api_options(extra_fields)
195
- # @type [Hash{Symbol => undefined}]
196
- all_options = projects.task_options(extra_fields: ['custom_fields'] + extra_fields)
197
- all_options[:options]
190
+ { fields: projects.task_fields(extra_fields: ['custom_fields'] + extra_fields) }
198
191
  end
199
192
 
200
193
  # bundle exec ./task_searches.rb
@@ -40,7 +40,7 @@ module Checkoff
40
40
  end
41
41
 
42
42
  # @param [Asana::Resources::Task] task
43
- # @param [Symbol, Array<Symbol, Integer, Array>] task_selector Filter based on
43
+ # @param [Symbol, Array<Symbol, String, Integer, Array>] task_selector Filter based on
44
44
  # task details. Examples: [:tag, 'foo'] [:not, [:tag, 'foo']] [:tag, 'foo']
45
45
  # @return [Boolean]
46
46
  def filter_via_task_selector(task, task_selector)
@@ -60,30 +60,43 @@ module Checkoff
60
60
  # @return [Checkoff::CustomFields]
61
61
  attr_reader :custom_fields
62
62
 
63
+ class << self
64
+ # @return [Array(Symbol, Array)]
65
+ def task_selector
66
+ task_selector_json = ARGV[2] || raise('Please pass task_selector in JSON form as third argument')
67
+ function_name, args = validate_task_selector(JSON.parse(task_selector_json))
68
+ [function_name.to_sym, args]
69
+ end
70
+
71
+ # @param parsed [Object] result of JSON.parse on a task selector
72
+ # @return [Array(String, Array)]
73
+ def validate_task_selector(parsed)
74
+ raise "Expected a 2-element [function_name, args] array, got #{parsed.inspect}" \
75
+ unless parsed.is_a?(Array) && parsed.length == 2
76
+
77
+ function_name = parsed.fetch(0)
78
+ args = parsed.fetch(1)
79
+ raise "Expected function_name to be a String, got #{function_name.inspect}" \
80
+ unless function_name.is_a?(String)
81
+ raise "Expected args to be an Array, got #{args.inspect}" unless args.is_a?(Array)
82
+
83
+ [function_name, args]
84
+ end
85
+ end
86
+
63
87
  # bundle exec ./task_selectors.rb
64
88
  # :nocov:
65
89
  class << self
66
90
  # @return [String]
67
- # @sg-ignore `x || raise(...)` isn't inferred as narrowing away the nil branch
68
91
  def project_name
69
92
  ARGV[1] || raise('Please pass project name to pull tasks from as first argument')
70
93
  end
71
94
 
72
95
  # @return [String]
73
- # @sg-ignore `x || raise(...)` isn't inferred as narrowing away the nil branch
74
96
  def workspace_name
75
97
  ARGV[0] || raise('Please pass workspace name as first argument')
76
98
  end
77
99
 
78
- # @return [Array(Symbol, Array)]
79
- # @sg-ignore `x || raise(...)` isn't inferred as narrowing away the nil branch
80
- def task_selector
81
- task_selector_json = ARGV[2] || raise('Please pass task_selector in JSON form as third argument')
82
-
83
- # @return [Symbol, Array]
84
- T.cast(JSON.parse(task_selector_json), [Symbol, Array])
85
- end
86
-
87
100
  # @return [void]
88
101
  def run
89
102
  require 'checkoff/projects'
@@ -32,7 +32,7 @@ module Checkoff
32
32
  LONG_CACHE_TIME = MINUTE * 15
33
33
  SHORT_CACHE_TIME = MINUTE * 5
34
34
 
35
- # @param config [Hash, Checkoff::Internal::EnvFallbackConfigLoader]
35
+ # @param config [Hash{Symbol => Object}, Checkoff::Internal::EnvFallbackConfigLoader]
36
36
  # @param client [Asana::Client]
37
37
  # @param workspaces [Checkoff::Workspaces]
38
38
  # @param sections [Checkoff::Sections]
@@ -180,8 +180,6 @@ module Checkoff
180
180
  # @param assignee_gid [String]
181
181
  #
182
182
  # @return [Asana::Resources::Task]
183
- # @sg-ignore @asana_task.create return type could not be inferred — calling a class method
184
- # through a Class<T>-typed ivar is a known Solargraph dispatch limitation
185
183
  def add_task(name,
186
184
  workspace_gid: @workspaces.default_workspace_gid,
187
185
  assignee_gid: default_assignee_gid)
@@ -282,7 +280,7 @@ module Checkoff
282
280
  task_hashes.task_to_h(task)
283
281
  end
284
282
 
285
- # @param task_data [Hash]
283
+ # @param task_data [Hash{String => Object}]
286
284
  #
287
285
  # @return [Asana::Resources::Task]
288
286
  def h_to_task(task_data)
@@ -295,16 +293,14 @@ module Checkoff
295
293
  # @param portfolio_name [String]
296
294
  # @param workspace_name [String]
297
295
  # @return [Boolean]
298
- # @sg-ignore Checkoff::Tasks#in_portfolio_named? return type could not be inferred
299
296
  def in_portfolio_named?(task,
300
297
  portfolio_name,
301
298
  workspace_name: T.must(@workspaces.default_workspace.name))
302
299
  portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
303
- T.cast(task.memberships.any? do |membership|
304
- m = T.cast(membership, T::Hash[String, T.untyped])
305
- project_gid = T.cast(m.fetch('project'), T::Hash[String, T.untyped]).fetch('gid')
300
+ task.memberships.any? do |membership|
301
+ project_gid = membership.fetch('project').fetch('gid')
306
302
  portfolio_projects.any? { |portfolio_project| portfolio_project.gid == project_gid }
307
- end, T::Boolean)
303
+ end
308
304
  end
309
305
 
310
306
  # True if the task is in a project which is in the given portfolio
@@ -320,8 +316,7 @@ module Checkoff
320
316
  portfolio_project_gids = portfolio_projects.map(&:gid)
321
317
  seen = T.let(false, T::Boolean)
322
318
  task.memberships.each do |membership|
323
- m = T.cast(membership, T::Hash[String, T.untyped])
324
- project_gid = T.cast(m.fetch('project'), T::Hash[String, T.untyped]).fetch('gid')
319
+ project_gid = membership.fetch('project').fetch('gid')
325
320
  next unless portfolio_project_gids.include?(project_gid)
326
321
  return true if seen
327
322
 
@@ -386,9 +381,6 @@ module Checkoff
386
381
  end
387
382
 
388
383
  # @return [String]
389
- # @sg-ignore @config.fetch return type could not be inferred — @config is intentionally
390
- # dual-typed [Hash, EnvFallbackConfigLoader] throughout this codebase; Solargraph can't
391
- # unify #fetch across that union
392
384
  def default_assignee_gid
393
385
  @config.fetch(:default_assignee_gid)
394
386
  end
@@ -54,16 +54,11 @@ module Checkoff
54
54
  memberships_data = task_data.fetch('memberships')
55
55
  memberships_data.all? do |membership_data|
56
56
  # @type [Hash{String => String}]
57
- # @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
58
- # https://github.com/castwide/solargraph/pull/1228
59
57
  section_data = membership_data.fetch('section')
60
58
  section_gid = section_data.fetch('gid')
61
- # @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
62
- # https://github.com/castwide/solargraph/pull/1228
63
59
  section = @sections.section_by_gid(section_gid)
64
- # @sg-ignore section_by_gid can return nil on a gid lookup miss, and
65
- # task_data_dependent_on_previous_section_last_milestone? dereferences it immediately
66
- # without a nil guard — looks like a real gap, flagging for the follow-up code PR
60
+ next false if section.nil?
61
+
67
62
  task_data_dependent_on_previous_section_last_milestone?(task_data, section)
68
63
  end
69
64
  end
@@ -85,20 +80,14 @@ module Checkoff
85
80
  md = membership_data
86
81
  unless limit_to_portfolio_name.nil?
87
82
  # @type [Hash{String => Object}]
88
- # @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
89
- # https://github.com/castwide/solargraph/pull/1228
90
83
  project_data = md.fetch('project')
91
84
  project_gid = project_data.fetch('gid')
92
85
  next true unless limit_to_projects.map(&:gid).include? project_gid
93
86
  end
94
87
  # @type [Hash{String => Object}]
95
- # @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
96
- # https://github.com/castwide/solargraph/pull/1228
97
88
  section_data = md.fetch('section')
98
89
  section_gid = section_data.fetch('gid')
99
90
 
100
- # @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
101
- # https://github.com/castwide/solargraph/pull/1228
102
91
  last_milestone = last_milestone_in_section(section_gid)
103
92
 
104
93
  next false if last_milestone.nil?
@@ -107,8 +96,6 @@ module Checkoff
107
96
 
108
97
  all_dependent_task_gids ||= @tasks.all_dependent_tasks(task).map(&:gid)
109
98
 
110
- # @sg-ignore all_dependent_task_gids is nil-initialized then set via ||= above, but
111
- # Solargraph doesn't narrow it back to non-nil after the reassignment
112
99
  all_dependent_task_gids.include? last_milestone.gid
113
100
  end
114
101
  end
@@ -130,8 +117,6 @@ module Checkoff
130
117
  md = membership_data
131
118
  unless limit_to_portfolio_name.nil?
132
119
  # @type [Hash{String => Object}]
133
- # @sg-ignore Hash#fetch generic<X> leak on rbs >= 4.1.0, fix in progress upstream
134
- # https://github.com/castwide/solargraph/pull/1228
135
120
  project_data = md.fetch('project')
136
121
  project_gid = project_data.fetch('gid')
137
122
  next true unless limit_to_projects.map(&:gid).include? project_gid
@@ -146,8 +131,6 @@ module Checkoff
146
131
  dependent_task.resource_subtype == 'milestone'
147
132
  end
148
133
 
149
- # @sg-ignore all_dependent_milestones is nil-initialized then set via ||= above, but
150
- # Solargraph doesn't narrow it back to non-nil after the reassignment
151
134
  all_dependent_milestones.any? do |milestone|
152
135
  milestone.memberships.any? do |milestone_membership_data|
153
136
  milestone_membership_data.fetch('project').fetch('gid') == project_gid
@@ -169,7 +152,7 @@ module Checkoff
169
152
 
170
153
  private
171
154
 
172
- # @param task_data [Hash]
155
+ # @param task_data [Hash{String => Object}]
173
156
  # @param section [Asana::Resources::Section]
174
157
  #
175
158
  # @return [Boolean]
@@ -36,7 +36,7 @@ module Checkoff
36
36
  end
37
37
 
38
38
  # @param date_or_time [Date,Time,nil]
39
- # @param period [Symbol, Array(Symbol,Integer)]
39
+ # @param period [Symbol, Array<Symbol, Integer>]
40
40
  #
41
41
  # Valid values: :this_week, :now_or_before, :indefinite, [:less_than_n_days_ago, Integer]
42
42
  def in_period?(date_or_time, period)
@@ -175,7 +175,6 @@ module Checkoff
175
175
  # @param num_days [Integer]
176
176
  #
177
177
  # @return [Date]
178
- # @sg-ignore Checkoff::Timing#n_days_from_today return type could not be inferred
179
178
  def n_days_from_today(num_days)
180
179
  T.cast(@today_getter.today + num_days, Date)
181
180
  end
@@ -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.258.0'
7
+ VERSION = '0.261.0'
8
8
  end
@@ -22,7 +22,7 @@ module Checkoff
22
22
  # @!parse
23
23
  # extend CacheMethod::ClassMethods
24
24
 
25
- # @param config [Hash, Checkoff::Internal::EnvFallbackConfigLoader]
25
+ # @param config [Hash{Symbol => Object}, Checkoff::Internal::EnvFallbackConfigLoader]
26
26
  # @param client [Asana::Client]
27
27
  # @param asana_workspace [Class<Asana::Resources::Workspace>]
28
28
  def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
@@ -44,8 +44,6 @@ module Checkoff
44
44
  cache_method :workspace, LONG_CACHE_TIME
45
45
 
46
46
  # @return [Asana::Resources::Workspace]
47
- # @sg-ignore @asana_workspace.find_by_id return type could not be inferred — calling a
48
- # class method through a Class<T>-typed ivar is a known Solargraph dispatch limitation
49
47
  def default_workspace
50
48
  @asana_workspace.find_by_id(client, default_workspace_gid)
51
49
  end
@@ -53,7 +51,6 @@ module Checkoff
53
51
 
54
52
  # @param [String, Symbol] workspace_name
55
53
  # @return [Asana::Resources::Workspace]
56
- # @sg-ignore workspace() is nil-checked below
57
54
  def workspace_or_raise(workspace_name)
58
55
  w = workspace(workspace_name)
59
56
  raise "Could not find workspace #{workspace_name}" if w.nil?
@@ -62,9 +59,6 @@ module Checkoff
62
59
  end
63
60
 
64
61
  # @return [String]
65
- # @sg-ignore @config.fetch return type could not be inferred — @config is intentionally
66
- # dual-typed [Hash, EnvFallbackConfigLoader] throughout this codebase; Solargraph can't
67
- # unify #fetch across that union
68
62
  def default_workspace_gid
69
63
  @config.fetch(:default_workspace_gid)
70
64
  end