checkoff 0.257.0 → 0.260.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 +4 -4
- data/lib/checkoff/attachments.rb +14 -15
- data/lib/checkoff/cli.rb +7 -12
- data/lib/checkoff/clients.rb +0 -2
- data/lib/checkoff/custom_fields.rb +13 -26
- data/lib/checkoff/internal/asana_event_enrichment.rb +11 -13
- data/lib/checkoff/internal/asana_event_filter.rb +1 -1
- data/lib/checkoff/internal/config_loader.rb +0 -2
- data/lib/checkoff/internal/logging.rb +0 -2
- data/lib/checkoff/internal/project_hashes.rb +5 -4
- data/lib/checkoff/internal/search_url/custom_field_param_converter.rb +9 -9
- data/lib/checkoff/internal/search_url/custom_field_variant.rb +7 -0
- data/lib/checkoff/internal/search_url/date_param_converter.rb +13 -4
- data/lib/checkoff/internal/search_url/parser.rb +2 -7
- data/lib/checkoff/internal/search_url/simple_param_converter.rb +11 -5
- data/lib/checkoff/internal/section_selector_evaluator.rb +1 -1
- data/lib/checkoff/internal/selector_classes/common/function_evaluator.rb +5 -2
- data/lib/checkoff/internal/selector_classes/common.rb +13 -31
- data/lib/checkoff/internal/selector_classes/function_evaluator.rb +20 -4
- data/lib/checkoff/internal/selector_classes/project/function_evaluator.rb +11 -2
- data/lib/checkoff/internal/selector_classes/project.rb +5 -13
- data/lib/checkoff/internal/selector_classes/section/function_evaluator.rb +8 -5
- data/lib/checkoff/internal/selector_classes/section.rb +2 -5
- data/lib/checkoff/internal/selector_classes/task/function_evaluator.rb +2 -2
- data/lib/checkoff/internal/selector_classes/task.rb +24 -23
- data/lib/checkoff/internal/selector_evaluator.rb +8 -9
- data/lib/checkoff/internal/task_hashes.rb +17 -9
- data/lib/checkoff/internal/task_timing.rb +0 -4
- data/lib/checkoff/internal/thread_local.rb +0 -2
- data/lib/checkoff/monkeypatches/resource_marshalling.rb +3 -3
- data/lib/checkoff/my_tasks.rb +1 -4
- data/lib/checkoff/portfolios.rb +0 -1
- data/lib/checkoff/project_selectors.rb +1 -1
- data/lib/checkoff/projects.rb +19 -8
- data/lib/checkoff/resources.rb +14 -3
- data/lib/checkoff/sections.rb +13 -19
- data/lib/checkoff/subtasks.rb +1 -2
- data/lib/checkoff/tags.rb +3 -4
- data/lib/checkoff/task_searches.rb +1 -8
- data/lib/checkoff/task_selectors.rb +25 -12
- data/lib/checkoff/tasks.rb +8 -16
- data/lib/checkoff/timelines.rb +5 -10
- data/lib/checkoff/timing.rb +1 -2
- data/lib/checkoff/version.rb +1 -1
- data/lib/checkoff/workspaces.rb +1 -7
- data/rbi/checkoff.rbi +122 -179
- data/sig/checkoff.rbs +83 -181
- metadata +2 -2
data/lib/checkoff/projects.rb
CHANGED
|
@@ -22,12 +22,19 @@ require 'asana'
|
|
|
22
22
|
module Checkoff
|
|
23
23
|
# Work with projects in Asana
|
|
24
24
|
class Projects
|
|
25
|
+
# @return [Integer]
|
|
25
26
|
MINUTE = 60
|
|
27
|
+
# @return [Integer]
|
|
26
28
|
HOUR = MINUTE * 60
|
|
29
|
+
# @return [Integer]
|
|
27
30
|
DAY = 24 * HOUR
|
|
31
|
+
# @return [Integer]
|
|
28
32
|
REALLY_LONG_CACHE_TIME = MINUTE * 30
|
|
33
|
+
# @return [Integer]
|
|
29
34
|
LONG_CACHE_TIME = MINUTE * 15
|
|
35
|
+
# @return [Integer]
|
|
30
36
|
MEDIUM_CACHE_TIME = MINUTE * 5
|
|
37
|
+
# @return [Integer]
|
|
31
38
|
SHORT_CACHE_TIME = MINUTE
|
|
32
39
|
|
|
33
40
|
# @!parse
|
|
@@ -65,6 +72,12 @@ module Checkoff
|
|
|
65
72
|
|
|
66
73
|
# Default options used in Asana API to pull tasks
|
|
67
74
|
#
|
|
75
|
+
# Keys are :per_page (Integer), :options (a Hash whose :fields is an
|
|
76
|
+
# Array<String>) and, when only_uncompleted is set, :completed_since
|
|
77
|
+
# (String). The value type is genuinely per-key, which a single YARD
|
|
78
|
+
# Hash value type cannot express.
|
|
79
|
+
# Callers who want the field list should call #task_fields directly.
|
|
80
|
+
#
|
|
68
81
|
# @param extra_fields [Array<String>]
|
|
69
82
|
# @param [Boolean] only_uncompleted
|
|
70
83
|
#
|
|
@@ -109,9 +122,7 @@ module Checkoff
|
|
|
109
122
|
# @type [Enumerable<Asana::Resources::Project>]
|
|
110
123
|
ps = projects_by_workspace_name(workspace_name, extra_fields:)
|
|
111
124
|
# @type [Asana::Resources::Project,nil]
|
|
112
|
-
|
|
113
|
-
# doesn't fully support numbered block parameters
|
|
114
|
-
project = ps.find { _1.name == project_name }
|
|
125
|
+
project = ps.find { |p| p.name == project_name }
|
|
115
126
|
project_by_gid(project.gid, extra_fields:) unless project.nil?
|
|
116
127
|
end
|
|
117
128
|
end
|
|
@@ -122,7 +133,6 @@ module Checkoff
|
|
|
122
133
|
# @param [Array<String>] extra_fields
|
|
123
134
|
#
|
|
124
135
|
# @return [Asana::Resources::Project]
|
|
125
|
-
# @sg-ignore nil check above is not flow-sensitive
|
|
126
136
|
def project_or_raise(workspace_name, project_name, extra_fields: [])
|
|
127
137
|
p = project(workspace_name, project_name, extra_fields:)
|
|
128
138
|
raise "Could not find project #{project_name.inspect} under workspace #{workspace_name}." if p.nil?
|
|
@@ -199,9 +209,10 @@ module Checkoff
|
|
|
199
209
|
cache_method :projects_by_workspace_name, REALLY_LONG_CACHE_TIME
|
|
200
210
|
|
|
201
211
|
# @param project_obj [Asana::Resources::Project]
|
|
202
|
-
# @param project [String,
|
|
212
|
+
# @param project [String, :not_specified, :my_tasks, nil] - a String is a
|
|
213
|
+
# project name
|
|
203
214
|
#
|
|
204
|
-
# @return [Hash]
|
|
215
|
+
# @return [Hash{String => Object}]
|
|
205
216
|
def project_to_h(project_obj, project: :not_specified)
|
|
206
217
|
project_hashes.project_to_h(project_obj, project:)
|
|
207
218
|
end
|
|
@@ -214,14 +225,14 @@ module Checkoff
|
|
|
214
225
|
# date is today or in the past.
|
|
215
226
|
#
|
|
216
227
|
# @param project [Asana::Resources::Project]
|
|
217
|
-
# @param period [Symbol, Array
|
|
228
|
+
# @param period [Symbol, Array<Symbol, Integer>] See Checkoff::Timing#in_period? - :now_or_before,:this_week
|
|
218
229
|
def project_ready?(project, period: :now_or_before)
|
|
219
230
|
in_period?(project, :ready, period)
|
|
220
231
|
end
|
|
221
232
|
|
|
222
233
|
# @param project [Asana::Resources::Project]
|
|
223
234
|
# @param field_name [Symbol,Array]
|
|
224
|
-
# @param period [Symbol, Array
|
|
235
|
+
# @param period [Symbol, Array<Symbol, Integer>] See Checkoff::Timing#in_period? - :now_or_before,:this_week
|
|
225
236
|
def in_period?(project, field_name, period)
|
|
226
237
|
# @type [Date,Time,nil]
|
|
227
238
|
project_date = project_timing.date_or_time_field_by_name(project, field_name)
|
data/lib/checkoff/resources.rb
CHANGED
|
@@ -62,9 +62,7 @@ module Checkoff
|
|
|
62
62
|
%w[task section project].each do |resource_type_to_try|
|
|
63
63
|
next unless [resource_type_to_try, nil].include?(resource_type)
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
resource = T.cast(method(:"fetch_#{resource_type_to_try}_gid").call(gid),
|
|
67
|
-
T.nilable(Asana::Resources::Resource))
|
|
65
|
+
resource = fetch_gid_by_type(resource_type_to_try, gid)
|
|
68
66
|
return [resource, resource_type_to_try] if resource
|
|
69
67
|
end
|
|
70
68
|
[nil, nil]
|
|
@@ -72,6 +70,19 @@ module Checkoff
|
|
|
72
70
|
|
|
73
71
|
private
|
|
74
72
|
|
|
73
|
+
# @param resource_type [String]
|
|
74
|
+
# @param gid [String]
|
|
75
|
+
#
|
|
76
|
+
# @return [Asana::Resources::Resource, nil]
|
|
77
|
+
def fetch_gid_by_type(resource_type, gid)
|
|
78
|
+
case resource_type
|
|
79
|
+
when 'task' then fetch_task_gid(gid)
|
|
80
|
+
when 'section' then fetch_section_gid(gid)
|
|
81
|
+
when 'project' then fetch_project_gid(gid)
|
|
82
|
+
else raise "Unexpected resource_type: #{resource_type}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
75
86
|
# @param gid [String]
|
|
76
87
|
#
|
|
77
88
|
# @return [Asana::Resources::Task, nil]
|
data/lib/checkoff/sections.rb
CHANGED
|
@@ -14,10 +14,15 @@ module Checkoff
|
|
|
14
14
|
# @!parse
|
|
15
15
|
# extend CacheMethod::ClassMethods
|
|
16
16
|
|
|
17
|
+
# @return [Integer]
|
|
17
18
|
MINUTE = 60
|
|
19
|
+
# @return [Integer]
|
|
18
20
|
HOUR = MINUTE * 60
|
|
21
|
+
# @return [Integer]
|
|
19
22
|
REALLY_LONG_CACHE_TIME = MINUTE * 30
|
|
23
|
+
# @return [Integer]
|
|
20
24
|
LONG_CACHE_TIME = MINUTE * 15
|
|
25
|
+
# @return [Integer]
|
|
21
26
|
SHORT_CACHE_TIME = MINUTE * 5
|
|
22
27
|
|
|
23
28
|
extend Forwardable
|
|
@@ -142,11 +147,9 @@ module Checkoff
|
|
|
142
147
|
# @param section_name [String, nil]
|
|
143
148
|
#
|
|
144
149
|
# @return [Array<String>]
|
|
145
|
-
# @sg-ignore
|
|
146
150
|
def section_task_names(workspace_name, project_name, section_name)
|
|
147
151
|
task_array = tasks(workspace_name, project_name, section_name)
|
|
148
|
-
|
|
149
|
-
T.cast(task_array.map(&:name), T::Array[String])
|
|
152
|
+
task_array.filter_map(&:name)
|
|
150
153
|
end
|
|
151
154
|
cache_method :section_task_names, SHORT_CACHE_TIME
|
|
152
155
|
|
|
@@ -156,7 +159,6 @@ module Checkoff
|
|
|
156
159
|
# @param extra_section_fields [Array<String>]
|
|
157
160
|
#
|
|
158
161
|
# @return [Asana::Resources::Section]
|
|
159
|
-
# @sg-ignore section() is nil-checked below
|
|
160
162
|
def section_or_raise(workspace_name, project_name, section_name, extra_section_fields: [])
|
|
161
163
|
s = section(workspace_name, project_name, section_name,
|
|
162
164
|
extra_section_fields:)
|
|
@@ -184,17 +186,15 @@ module Checkoff
|
|
|
184
186
|
#
|
|
185
187
|
# @return [Asana::Resources::Section, nil]
|
|
186
188
|
def previous_section(section)
|
|
187
|
-
|
|
189
|
+
project_sections = sections_by_project_gid(section.project.fetch('gid'))
|
|
188
190
|
|
|
189
191
|
# @type [Array<Asana::Resources::Section>]
|
|
190
|
-
|
|
191
|
-
sections = sections.to_a
|
|
192
|
+
project_sections_arr = project_sections.to_a
|
|
192
193
|
|
|
193
|
-
index =
|
|
194
|
+
index = project_sections_arr.find_index { |s| s.gid == section.gid }
|
|
194
195
|
return nil if index.nil? || index.zero?
|
|
195
196
|
|
|
196
|
-
|
|
197
|
-
sections[index - 1]
|
|
197
|
+
project_sections_arr[index - 1]
|
|
198
198
|
end
|
|
199
199
|
cache_method :previous_section, SHORT_CACHE_TIME
|
|
200
200
|
|
|
@@ -283,28 +283,22 @@ module Checkoff
|
|
|
283
283
|
# @return [void]
|
|
284
284
|
def file_task_by_section(by_section, task, project_gid)
|
|
285
285
|
membership = task.memberships.find do |m|
|
|
286
|
-
|
|
287
|
-
T.cast(membership_hash['project'], T::Hash[String, T.untyped])['gid'] == project_gid
|
|
286
|
+
m.fetch('project')['gid'] == project_gid
|
|
288
287
|
end
|
|
289
288
|
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
|
290
289
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
section_name = T.cast(section['name'], String)
|
|
290
|
+
section = membership.fetch('section')
|
|
291
|
+
section_name = section.fetch('name')
|
|
294
292
|
|
|
295
293
|
# @type [String, nil]
|
|
296
294
|
current_section = section_key(section_name)
|
|
297
295
|
|
|
298
|
-
# @sg-ignore Hash#fetch arg0 expected String,NilClass received String,nil — Solargraph
|
|
299
|
-
# treats the nil literal in a Hash{K,nil=>V} declaration and an inferred `nil` union
|
|
300
|
-
# member as distinct types; no comment-only fix found
|
|
301
296
|
by_section.fetch(current_section) << task
|
|
302
297
|
end
|
|
303
298
|
|
|
304
299
|
# @param workspace_name [String, Symbol]
|
|
305
300
|
# @param project_name [String, Symbol]
|
|
306
301
|
# @return [Asana::Resources::Project]
|
|
307
|
-
# @sg-ignore projects.project may be nil but is raised below
|
|
308
302
|
def project_or_raise(workspace_name, project_name)
|
|
309
303
|
raise ArgumentError, 'Provide nil project_name' if T.unsafe(project_name).nil?
|
|
310
304
|
|
data/lib/checkoff/subtasks.rb
CHANGED
|
@@ -72,7 +72,7 @@ module Checkoff
|
|
|
72
72
|
def subtasks_by_gid(task_gid,
|
|
73
73
|
extra_fields: [],
|
|
74
74
|
only_uncompleted: true)
|
|
75
|
-
# @type [Hash]
|
|
75
|
+
# @type [Hash{Symbol => undefined}]
|
|
76
76
|
all_options = projects.task_options(extra_fields: extra_fields + %w[is_rendered_as_separator],
|
|
77
77
|
only_uncompleted:)
|
|
78
78
|
options = all_options.fetch(:options, {})
|
|
@@ -90,7 +90,6 @@ module Checkoff
|
|
|
90
90
|
#
|
|
91
91
|
# @param subtask [Asana::Resources::Task]
|
|
92
92
|
# @return [Boolean, nil]
|
|
93
|
-
# @sg-ignore Checkoff::Subtasks#subtask_section? return type could not be inferred
|
|
94
93
|
def subtask_section?(subtask)
|
|
95
94
|
subtask.is_rendered_as_separator
|
|
96
95
|
end
|
data/lib/checkoff/tags.rb
CHANGED
|
@@ -79,7 +79,6 @@ module Checkoff
|
|
|
79
79
|
# @param tag_name [String]
|
|
80
80
|
#
|
|
81
81
|
# @return [Asana::Resources::Tag]
|
|
82
|
-
# @sg-ignore nil check above is not flow-sensitive
|
|
83
82
|
def tag_or_raise(workspace_name, tag_name)
|
|
84
83
|
t = tag(workspace_name, tag_name)
|
|
85
84
|
|
|
@@ -113,9 +112,9 @@ module Checkoff
|
|
|
113
112
|
#
|
|
114
113
|
# @return [Hash{Symbol => Object}]
|
|
115
114
|
def build_params(options)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
# @type [Hash{Symbol => Object}]
|
|
116
|
+
params = { limit: options[:per_page], completed_since: options[:completed_since] }
|
|
117
|
+
params.reject do |_, v|
|
|
119
118
|
v.nil? || Array(v).empty?
|
|
120
119
|
end
|
|
121
120
|
end
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
# @sg-ignore
|
|
73
95
|
# @return [String]
|
|
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
|
|
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'
|
data/lib/checkoff/tasks.rb
CHANGED
|
@@ -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
|
-
workspace_name: @workspaces.default_workspace.name)
|
|
298
|
+
workspace_name: T.must(@workspaces.default_workspace.name))
|
|
302
299
|
portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
|
|
303
|
-
|
|
304
|
-
|
|
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
|
|
303
|
+
end
|
|
308
304
|
end
|
|
309
305
|
|
|
310
306
|
# True if the task is in a project which is in the given portfolio
|
|
@@ -315,13 +311,12 @@ module Checkoff
|
|
|
315
311
|
# @return [Boolean]
|
|
316
312
|
def in_portfolio_more_than_once?(task,
|
|
317
313
|
portfolio_name,
|
|
318
|
-
workspace_name: @workspaces.default_workspace.name)
|
|
314
|
+
workspace_name: T.must(@workspaces.default_workspace.name))
|
|
319
315
|
portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
|
|
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
|
-
|
|
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
|
data/lib/checkoff/timelines.rb
CHANGED
|
@@ -57,9 +57,8 @@ module Checkoff
|
|
|
57
57
|
section_data = membership_data.fetch('section')
|
|
58
58
|
section_gid = section_data.fetch('gid')
|
|
59
59
|
section = @sections.section_by_gid(section_gid)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# without a nil guard — looks like a real gap, flagging for the follow-up code PR
|
|
60
|
+
next false if section.nil?
|
|
61
|
+
|
|
63
62
|
task_data_dependent_on_previous_section_last_milestone?(task_data, section)
|
|
64
63
|
end
|
|
65
64
|
end
|
|
@@ -69,7 +68,7 @@ module Checkoff
|
|
|
69
68
|
# @return [Boolean]
|
|
70
69
|
def last_task_milestone_depends_on_this_task?(task, limit_to_portfolio_name: nil)
|
|
71
70
|
unless limit_to_portfolio_name.nil?
|
|
72
|
-
limit_to_projects = @portfolios.projects_in_portfolio(@workspaces.default_workspace.name,
|
|
71
|
+
limit_to_projects = @portfolios.projects_in_portfolio(T.must(@workspaces.default_workspace.name),
|
|
73
72
|
limit_to_portfolio_name)
|
|
74
73
|
end
|
|
75
74
|
|
|
@@ -97,8 +96,6 @@ module Checkoff
|
|
|
97
96
|
|
|
98
97
|
all_dependent_task_gids ||= @tasks.all_dependent_tasks(task).map(&:gid)
|
|
99
98
|
|
|
100
|
-
# @sg-ignore all_dependent_task_gids is nil-initialized then set via ||= above, but
|
|
101
|
-
# Solargraph doesn't narrow it back to non-nil after the reassignment
|
|
102
99
|
all_dependent_task_gids.include? last_milestone.gid
|
|
103
100
|
end
|
|
104
101
|
end
|
|
@@ -108,7 +105,7 @@ module Checkoff
|
|
|
108
105
|
# @return [Boolean]
|
|
109
106
|
def any_milestone_depends_on_this_task?(task, limit_to_portfolio_name: nil)
|
|
110
107
|
unless limit_to_portfolio_name.nil?
|
|
111
|
-
limit_to_projects = @portfolios.projects_in_portfolio(@workspaces.default_workspace.name,
|
|
108
|
+
limit_to_projects = @portfolios.projects_in_portfolio(T.must(@workspaces.default_workspace.name),
|
|
112
109
|
limit_to_portfolio_name)
|
|
113
110
|
end
|
|
114
111
|
|
|
@@ -134,8 +131,6 @@ module Checkoff
|
|
|
134
131
|
dependent_task.resource_subtype == 'milestone'
|
|
135
132
|
end
|
|
136
133
|
|
|
137
|
-
# @sg-ignore all_dependent_milestones is nil-initialized then set via ||= above, but
|
|
138
|
-
# Solargraph doesn't narrow it back to non-nil after the reassignment
|
|
139
134
|
all_dependent_milestones.any? do |milestone|
|
|
140
135
|
milestone.memberships.any? do |milestone_membership_data|
|
|
141
136
|
milestone_membership_data.fetch('project').fetch('gid') == project_gid
|
|
@@ -157,7 +152,7 @@ module Checkoff
|
|
|
157
152
|
|
|
158
153
|
private
|
|
159
154
|
|
|
160
|
-
# @param task_data [Hash]
|
|
155
|
+
# @param task_data [Hash{String => Object}]
|
|
161
156
|
# @param section [Asana::Resources::Section]
|
|
162
157
|
#
|
|
163
158
|
# @return [Boolean]
|
data/lib/checkoff/timing.rb
CHANGED
|
@@ -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
|
|
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
|
data/lib/checkoff/version.rb
CHANGED
data/lib/checkoff/workspaces.rb
CHANGED
|
@@ -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
|