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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e08117848a46a27ffee42a7936ada4febefdda2c2d9a44ae0160dcdfa89989b8
|
|
4
|
+
data.tar.gz: a8573c8961ab241c73425008c5c9a44f03c6038a4a3ad791b3154b505cd4ee22
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43b8c4f4ff4d1fcd48a98d0fe2b7fdc6332ab29e83b81f2dad3a9a9ec3628f3c106c4ea518ae81b0e9edc882bf9618396b547c099e6dc1fb22f76d5c91ed6cd5
|
|
7
|
+
data.tar.gz: 2da763b8482d3e7a6f7e8fe8b912ef2c8175d6843da3019fc10a7aae9ea94d3978a9ed789763f5fee8d7755200f80bd04268b332ab74b2865fd6303823c99e34
|
data/lib/checkoff/attachments.rb
CHANGED
|
@@ -85,9 +85,10 @@ module Checkoff
|
|
|
85
85
|
# @return [Object]
|
|
86
86
|
def download_uri(uri, verify_mode: OpenSSL::SSL::VERIFY_PEER, &block)
|
|
87
87
|
out = nil
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
#
|
|
88
|
+
host = uri.host || raise("URI has no host: #{uri}")
|
|
89
|
+
Net::HTTP.start(host, uri.port, use_ssl: uri.scheme == 'https', verify_mode:) do |http|
|
|
90
|
+
# @sg-ignore tool-limitation:pr-1290-follow-up
|
|
91
|
+
# https://github.com/castwide/solargraph/pull/1290#issuecomment-5272957964
|
|
91
92
|
http.request(Net::HTTP::Get.new(uri)) do |response|
|
|
92
93
|
raise("Unexpected response code: #{response.code}") unless response.code == '200'
|
|
93
94
|
|
|
@@ -108,7 +109,6 @@ module Checkoff
|
|
|
108
109
|
def write_tempfile_from_response(response)
|
|
109
110
|
Tempfile.create('checkoff') do |tempfile|
|
|
110
111
|
tempfile.binmode
|
|
111
|
-
# @sg-ignore Unresolved call to read_body on #read_body
|
|
112
112
|
response.read_body do |chunk|
|
|
113
113
|
tempfile.write(chunk)
|
|
114
114
|
end
|
|
@@ -128,16 +128,15 @@ module Checkoff
|
|
|
128
128
|
def create_attachment_from_downloaded_url!(url, resource, attachment_name:,
|
|
129
129
|
verify_mode: OpenSSL::SSL::VERIFY_PEER)
|
|
130
130
|
uri = URI(url)
|
|
131
|
-
|
|
131
|
+
# @type [String]
|
|
132
|
+
resolved_attachment_name = attachment_name || File.basename(uri.path || '')
|
|
132
133
|
download_uri(uri, verify_mode:) do |tempfile|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
# @sg-ignore same attachment_name/content_type narrowing gap as above
|
|
140
|
-
resource.attach(filename: attachment_name, mime: content_type,
|
|
134
|
+
content_type = content_type_from_filename(resolved_attachment_name)
|
|
135
|
+
content_type ||= content_type_from_filename(uri.path || '')
|
|
136
|
+
# neither the attachment name nor the URL's path has a MIME-recognizable extension
|
|
137
|
+
content_type ||= 'application/octet-stream'
|
|
138
|
+
|
|
139
|
+
resource.attach(filename: resolved_attachment_name, mime: content_type,
|
|
141
140
|
io: tempfile)
|
|
142
141
|
end
|
|
143
142
|
end
|
|
@@ -196,8 +195,8 @@ module Checkoff
|
|
|
196
195
|
tasks = Checkoff::Tasks.new
|
|
197
196
|
attachments = Checkoff::Attachments.new
|
|
198
197
|
task = tasks.task_by_gid(gid)
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
raise "Could not find task #{gid}" if task.nil?
|
|
199
|
+
|
|
201
200
|
attachment = attachments.create_attachment_from_url!(url, task)
|
|
202
201
|
puts "Results: #{attachment.inspect}"
|
|
203
202
|
end
|
data/lib/checkoff/cli.rb
CHANGED
|
@@ -161,7 +161,7 @@ module Checkoff
|
|
|
161
161
|
# @param workspace_name [String]
|
|
162
162
|
# @param project_name [String, Symbol]
|
|
163
163
|
# @param section_name [String, Symbol, nil]
|
|
164
|
-
# @param task_name [String,
|
|
164
|
+
# @param task_name [String, nil]
|
|
165
165
|
# @param config [Checkoff::Internal::EnvFallbackConfigLoader, Hash]
|
|
166
166
|
# @param projects [Checkoff::Projects]
|
|
167
167
|
# @param sections [Checkoff::Sections]
|
|
@@ -192,7 +192,6 @@ module Checkoff
|
|
|
192
192
|
elsif task_name.nil?
|
|
193
193
|
run_on_section(workspace_name, project_name, section_name)
|
|
194
194
|
else
|
|
195
|
-
# @sg-ignore
|
|
196
195
|
run_on_task(workspace_name, project_name, section_name, task_name)
|
|
197
196
|
end
|
|
198
197
|
end
|
|
@@ -243,7 +242,6 @@ module Checkoff
|
|
|
243
242
|
task = tasks.task(workspace.to_s, project, task_name, section_name: section)
|
|
244
243
|
raise "Task not found: #{task_name}" if task.nil?
|
|
245
244
|
|
|
246
|
-
# @sg-ignore nil check above is not flow-sensitive
|
|
247
245
|
task_to_hash(task).to_json
|
|
248
246
|
end
|
|
249
247
|
|
|
@@ -311,8 +309,9 @@ module Checkoff
|
|
|
311
309
|
desc 'Add a short task to Asana'
|
|
312
310
|
arg 'workspace'
|
|
313
311
|
arg 'task_name'
|
|
312
|
+
# @param c [GLI::Command]
|
|
314
313
|
command :quickadd do |c|
|
|
315
|
-
# @
|
|
314
|
+
# @param args [Array<String>]
|
|
316
315
|
c.action do |_global_options, _options, args|
|
|
317
316
|
workspace_name = args.fetch(0)
|
|
318
317
|
task_name = args.fetch(1)
|
|
@@ -326,8 +325,9 @@ module Checkoff
|
|
|
326
325
|
arg 'project'
|
|
327
326
|
arg 'section', :optional
|
|
328
327
|
arg 'task_name', :optional
|
|
328
|
+
# @param c [GLI::Command]
|
|
329
329
|
command :view do |c|
|
|
330
|
-
# @
|
|
330
|
+
# @param args [Array<String>]
|
|
331
331
|
c.action do |_global_options, _options, args|
|
|
332
332
|
workspace_name = args.fetch(0)
|
|
333
333
|
project_name = args.fetch(1)
|
|
@@ -341,38 +341,33 @@ module Checkoff
|
|
|
341
341
|
desc 'Move tasks from one section to another within a project'
|
|
342
342
|
|
|
343
343
|
# rubocop:disable Metrics/BlockLength
|
|
344
|
+
# @param c [GLI::Command]
|
|
344
345
|
command :mv do |c|
|
|
345
|
-
# @sg-ignore
|
|
346
346
|
c.flag :from_workspace,
|
|
347
347
|
type: String,
|
|
348
348
|
default_value: :default_workspace,
|
|
349
349
|
desc: 'Workspace to move tasks from'
|
|
350
|
-
# @sg-ignore
|
|
351
350
|
c.flag :from_project,
|
|
352
351
|
type: String,
|
|
353
352
|
required: true,
|
|
354
353
|
desc: 'Project to move tasks from'
|
|
355
|
-
# @sg-ignore
|
|
356
354
|
c.flag :from_section,
|
|
357
355
|
type: String,
|
|
358
356
|
default_value: :all_sections,
|
|
359
357
|
desc: 'Section to move tasks from'
|
|
360
|
-
# @sg-ignore
|
|
361
358
|
c.flag :to_workspace,
|
|
362
359
|
type: String,
|
|
363
360
|
default_value: :source_workspace,
|
|
364
361
|
desc: 'Workspace to move tasks to'
|
|
365
|
-
# @sg-ignore
|
|
366
362
|
c.flag :to_project,
|
|
367
363
|
type: String,
|
|
368
364
|
default_value: :source_project,
|
|
369
365
|
desc: 'Section to move tasks to'
|
|
370
|
-
# @sg-ignore
|
|
371
366
|
c.flag :to_section,
|
|
372
367
|
type: String,
|
|
373
368
|
default_value: :source_section,
|
|
374
369
|
desc: 'Section to move tasks to'
|
|
375
|
-
# @
|
|
370
|
+
# @param options [Hash]
|
|
376
371
|
c.action do |_global_options, options, _args|
|
|
377
372
|
from_workspace = options.fetch('from_workspace')
|
|
378
373
|
from_project = options.fetch('from_project')
|
data/lib/checkoff/clients.rb
CHANGED
|
@@ -30,9 +30,7 @@ module Checkoff
|
|
|
30
30
|
# @return [Asana::Client]
|
|
31
31
|
def client
|
|
32
32
|
@client ||= @asana_client_class.new do |c|
|
|
33
|
-
# @sg-ignore Unresolved call to authentication — gem config DSL block, not covered by annotations
|
|
34
33
|
c.authentication :access_token, @config.fetch(:personal_access_token)
|
|
35
|
-
# @sg-ignore Unresolved call to default_headers — gem config DSL block, not covered by annotations
|
|
36
34
|
c.default_headers 'asana-enable' =>
|
|
37
35
|
'new_project_templates,new_user_task_lists,new_memberships,new_goal_memberships'
|
|
38
36
|
end
|
|
@@ -41,7 +41,6 @@ module Checkoff
|
|
|
41
41
|
# @param custom_field_name [String]
|
|
42
42
|
#
|
|
43
43
|
# @return [Asana::Resources::CustomField]
|
|
44
|
-
# @sg-ignore nil check above is not flow-sensitive
|
|
45
44
|
def custom_field_or_raise(workspace_name, custom_field_name)
|
|
46
45
|
cf = custom_field(workspace_name, custom_field_name)
|
|
47
46
|
raise "Could not find custom_field #{custom_field_name} under workspace #{workspace_name}." if cf.nil?
|
|
@@ -79,40 +78,32 @@ module Checkoff
|
|
|
79
78
|
# @param resource [Asana::Resources::Project,Asana::Resources::Task]
|
|
80
79
|
# @param custom_field_name [String]
|
|
81
80
|
# @return [Array<String>]
|
|
82
|
-
# @sg-ignore Declared return type Array<String> does not match inferred type Array,Array<Array> —
|
|
83
|
-
# fixing cleanly needs binding enum_value.fetch('name') to a typed local; leaving for a
|
|
84
|
-
# follow-up code PR to keep this one annotation-only.
|
|
85
81
|
def resource_custom_field_values_names_by_name(resource, custom_field_name)
|
|
86
82
|
custom_field = resource_custom_field_by_name(resource, custom_field_name)
|
|
87
83
|
return [] if custom_field.nil?
|
|
88
84
|
|
|
89
85
|
resource_custom_field_enum_values(custom_field).flat_map do |enum_value|
|
|
90
|
-
|
|
91
|
-
[]
|
|
92
|
-
else
|
|
93
|
-
[enum_value.fetch('name')]
|
|
94
|
-
end
|
|
86
|
+
[enum_value.fetch('name')]
|
|
95
87
|
end
|
|
96
88
|
end
|
|
97
89
|
|
|
98
90
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
99
91
|
# @param custom_field_name [String]
|
|
100
|
-
# @return [Hash, nil]
|
|
92
|
+
# @return [Hash{String => Object}, nil]
|
|
101
93
|
def resource_custom_field_by_name(resource, custom_field_name)
|
|
102
|
-
# @type [Array<Hash>]
|
|
94
|
+
# @type [Array<Hash{String => Object}>]
|
|
103
95
|
custom_fields = resource.custom_fields
|
|
104
96
|
if custom_fields.nil?
|
|
105
97
|
raise "custom fields not found on resource - did you add 'custom_fields' in your extra_fields argument?"
|
|
106
98
|
end
|
|
107
99
|
|
|
108
|
-
# @type [Hash, nil]
|
|
100
|
+
# @type [Hash{String => Object}, nil]
|
|
109
101
|
custom_fields.find { |field| field.fetch('name') == custom_field_name }
|
|
110
102
|
end
|
|
111
103
|
|
|
112
104
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
113
105
|
# @param custom_field_name [String]
|
|
114
|
-
# @return [Hash]
|
|
115
|
-
# @sg-ignore nil check above is not flow-sensitive
|
|
106
|
+
# @return [Hash{String => Object}]
|
|
116
107
|
def resource_custom_field_by_name_or_raise(resource, custom_field_name)
|
|
117
108
|
custom_field = resource_custom_field_by_name(resource, custom_field_name)
|
|
118
109
|
if custom_field.nil?
|
|
@@ -124,16 +115,15 @@ module Checkoff
|
|
|
124
115
|
|
|
125
116
|
# @param resource [Asana::Resources::Project,Asana::Resources::Task]
|
|
126
117
|
# @param custom_field_gid [String]
|
|
127
|
-
# @return [Hash]
|
|
128
|
-
# @sg-ignore nil check above is not flow-sensitive
|
|
118
|
+
# @return [Hash{String => Object}]
|
|
129
119
|
def resource_custom_field_by_gid_or_raise(resource, custom_field_gid)
|
|
130
|
-
# @type [Array<Hash>]
|
|
120
|
+
# @type [Array<Hash{String => Object}>]
|
|
131
121
|
custom_fields = resource.custom_fields
|
|
132
122
|
if custom_fields.nil?
|
|
133
123
|
raise "Could not find custom_fields under project (was 'custom_fields' included in 'extra_fields'?)"
|
|
134
124
|
end
|
|
135
125
|
|
|
136
|
-
# @type [Hash, nil]
|
|
126
|
+
# @type [Hash{String => Object}, nil]
|
|
137
127
|
matched_custom_field = custom_fields.find { |data| data.fetch('gid') == custom_field_gid }
|
|
138
128
|
if matched_custom_field.nil?
|
|
139
129
|
raise "Could not find custom field with gid #{custom_field_gid} " \
|
|
@@ -145,27 +135,24 @@ module Checkoff
|
|
|
145
135
|
|
|
146
136
|
private
|
|
147
137
|
|
|
148
|
-
#
|
|
138
|
+
# rubocop:disable Layout/LineLength, YARD/TagTypeSyntax
|
|
139
|
+
# @param custom_field [Hash{"resource_subtype" => String} & Hash{"enum_value" => Hash} & Hash{"multi_enum_values" => Array<Hash>}]
|
|
149
140
|
#
|
|
150
|
-
# @return [Array<Hash>]
|
|
151
|
-
#
|
|
152
|
-
# fixing cleanly needs restructuring the case/when to bind each branch to a typed local; leaving
|
|
153
|
-
# for a follow-up code PR to keep this one annotation-only.
|
|
141
|
+
# @return [Array<Hash{"gid" => String} & Hash{"name" => String}>]
|
|
142
|
+
# rubocop:enable Layout/LineLength, YARD/TagTypeSyntax
|
|
154
143
|
def resource_custom_field_enum_values(custom_field)
|
|
155
144
|
resource_subtype = custom_field.fetch('resource_subtype')
|
|
156
145
|
case resource_subtype
|
|
157
146
|
when 'enum'
|
|
158
|
-
# @type [Array<Hash>]
|
|
159
147
|
[custom_field.fetch('enum_value')]
|
|
160
148
|
when 'multi_enum'
|
|
161
|
-
# @type [Array<Hash>]
|
|
162
149
|
custom_field.fetch('multi_enum_values')
|
|
163
150
|
else
|
|
164
151
|
raise "Teach me how to handle resource_subtype #{resource_subtype}"
|
|
165
152
|
end
|
|
166
153
|
end
|
|
167
154
|
|
|
168
|
-
# @param custom_field [Hash]
|
|
155
|
+
# @param custom_field [Hash{String => Object}]
|
|
169
156
|
# @param enum_value [Hash{String => String}, nil]
|
|
170
157
|
# @return [Array<String>]
|
|
171
158
|
def find_gids(custom_field, enum_value)
|
|
@@ -69,16 +69,14 @@ module Checkoff
|
|
|
69
69
|
#
|
|
70
70
|
# @return [void]
|
|
71
71
|
def enrich_webhook_subscription!(webhook_subscription)
|
|
72
|
-
webhook_subscription
|
|
72
|
+
return if webhook_subscription.nil?
|
|
73
|
+
|
|
74
|
+
webhook_subscription.fetch('filters', nil)&.map! do |filter|
|
|
73
75
|
enrich_filter(filter)
|
|
74
76
|
end
|
|
75
|
-
resource = webhook_subscription
|
|
77
|
+
resource = webhook_subscription.fetch('resource', nil)
|
|
76
78
|
name, resource_type = enrich_gid(resource) if resource
|
|
77
|
-
# @sg-ignore webhook_subscription is declared [Hash, nil] but this line assumes non-nil
|
|
78
|
-
# without the &. used elsewhere in this method — looks like a real nil-safety gap, not
|
|
79
|
-
# just a typing gap; flagging for the follow-up code PR rather than silently widening
|
|
80
79
|
webhook_subscription['checkoff:enriched:name'] = name if name
|
|
81
|
-
# @sg-ignore same nil-safety gap as above
|
|
82
80
|
webhook_subscription['checkoff:enriched:resource_type'] = resource_type if resource_type
|
|
83
81
|
end
|
|
84
82
|
|
|
@@ -100,8 +98,8 @@ module Checkoff
|
|
|
100
98
|
#
|
|
101
99
|
# @return [void]
|
|
102
100
|
def enrich_filter_parent_gid!(filter)
|
|
103
|
-
parent_gid =
|
|
104
|
-
return unless parent_gid
|
|
101
|
+
parent_gid = filter['checkoff:parent.gid']
|
|
102
|
+
return unless parent_gid.is_a?(String)
|
|
105
103
|
|
|
106
104
|
name, resource_type = enrich_gid(parent_gid)
|
|
107
105
|
filter['checkoff:enriched:parent.name'] = name if name
|
|
@@ -112,9 +110,9 @@ module Checkoff
|
|
|
112
110
|
#
|
|
113
111
|
# @return [void]
|
|
114
112
|
def enrich_filter_resource!(filter)
|
|
115
|
-
resource_gid =
|
|
113
|
+
resource_gid = filter['checkoff:resource.gid']
|
|
116
114
|
|
|
117
|
-
return unless resource_gid
|
|
115
|
+
return unless resource_gid.is_a?(String)
|
|
118
116
|
|
|
119
117
|
task = tasks.task_by_gid(resource_gid)
|
|
120
118
|
task_name = task&.name
|
|
@@ -125,8 +123,8 @@ module Checkoff
|
|
|
125
123
|
#
|
|
126
124
|
# @return [void]
|
|
127
125
|
def enrich_filter_section!(filter)
|
|
128
|
-
section_gid =
|
|
129
|
-
return unless section_gid
|
|
126
|
+
section_gid = filter['checkoff:fetched.section.gid']
|
|
127
|
+
return unless section_gid.is_a?(String)
|
|
130
128
|
|
|
131
129
|
section = sections.section_by_gid(section_gid)
|
|
132
130
|
name = section&.name
|
|
@@ -157,7 +155,7 @@ module Checkoff
|
|
|
157
155
|
# @return [void]
|
|
158
156
|
def enrich_event_resource!(asana_event)
|
|
159
157
|
# @type [Hash{String => String}]
|
|
160
|
-
resource =
|
|
158
|
+
resource = asana_event['resource']
|
|
161
159
|
# @type [String]
|
|
162
160
|
resource_type = resource.fetch('resource_type')
|
|
163
161
|
|
|
@@ -20,12 +20,10 @@ module Checkoff
|
|
|
20
20
|
|
|
21
21
|
# @param key [Symbol]
|
|
22
22
|
# @return [Object]
|
|
23
|
-
# @sg-ignore return type could not be inferred — cascades from the ENV.fetch RBS gap below
|
|
24
23
|
def [](key)
|
|
25
24
|
config_value = @config[key]
|
|
26
25
|
return config_value unless config_value.nil?
|
|
27
26
|
|
|
28
|
-
# @sg-ignore Unresolved call to fetch on RBS::Unnamed::ENVClass, Class<ENV>
|
|
29
27
|
ENV.fetch(envvar_name(key), nil)
|
|
30
28
|
end
|
|
31
29
|
|
|
@@ -57,10 +57,8 @@ module Logging
|
|
|
57
57
|
private
|
|
58
58
|
|
|
59
59
|
# @return [Symbol]
|
|
60
|
-
# @sg-ignore return type could not be inferred — cascades from the ENV.fetch RBS gap below
|
|
61
60
|
def log_level
|
|
62
61
|
# rubocop:disable Style/RedundantFetchBlock
|
|
63
|
-
# @sg-ignore Unresolved call to fetch on RBS::Unnamed::ENVClass, Class<ENV>
|
|
64
62
|
ENV.fetch('LOG_LEVEL') { 'INFO' }.downcase.to_sym
|
|
65
63
|
# rubocop:enable Style/RedundantFetchBlock
|
|
66
64
|
end
|
|
@@ -10,9 +10,10 @@ module Checkoff
|
|
|
10
10
|
def initialize(_deps = {}); end
|
|
11
11
|
|
|
12
12
|
# @param project_obj [Asana::Resources::Project]
|
|
13
|
-
# @param project [String,
|
|
13
|
+
# @param project [String, :not_specified, :my_tasks, nil] - a String is a
|
|
14
|
+
# project name
|
|
14
15
|
#
|
|
15
|
-
# @return [Hash]
|
|
16
|
+
# @return [Hash{String => Object}]
|
|
16
17
|
def project_to_h(project_obj, project: :not_specified)
|
|
17
18
|
project = project_obj.name if project == :not_specified
|
|
18
19
|
project_hash = { **project_obj.to_h, 'project' => project }
|
|
@@ -26,8 +27,8 @@ module Checkoff
|
|
|
26
27
|
# @param project_hash [Hash]
|
|
27
28
|
# @return [void]
|
|
28
29
|
def unwrap_custom_fields(project_hash)
|
|
29
|
-
# @type [Array<Hash>,nil]
|
|
30
|
-
custom_fields = project_hash
|
|
30
|
+
# @type [Array<Hash{String => Object}>,nil]
|
|
31
|
+
custom_fields = project_hash['custom_fields']
|
|
31
32
|
|
|
32
33
|
return if custom_fields.nil?
|
|
33
34
|
|
|
@@ -41,7 +41,7 @@ module Checkoff
|
|
|
41
41
|
end.transform_values(&:to_h)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
# @type [Hash{String => Class<CustomFieldVariant>}]
|
|
44
|
+
# @type [Hash{String => Class<CustomFieldVariant::CustomFieldVariant>}]
|
|
45
45
|
VARIANTS = {
|
|
46
46
|
'is' => CustomFieldVariant::Is,
|
|
47
47
|
'no_value' => CustomFieldVariant::NoValue,
|
|
@@ -59,19 +59,15 @@ module Checkoff
|
|
|
59
59
|
# @param gid [String]
|
|
60
60
|
# @param single_custom_field_params [Hash{String => Array<String>}]
|
|
61
61
|
# @return [Array(Hash{String => String}, Array<Symbol, Array>)]
|
|
62
|
-
# @sg-ignore
|
|
63
62
|
def convert_single_custom_field_params(gid, single_custom_field_params)
|
|
64
63
|
variant_key = "custom_field_#{gid}.variant"
|
|
65
64
|
variant = single_custom_field_params.fetch(variant_key)
|
|
66
65
|
remaining_params = single_custom_field_params.reject { |k, _v| k == variant_key }
|
|
67
66
|
raise "Teach me how to handle #{variant_key} = #{variant}" unless variant.length == 1
|
|
68
67
|
|
|
69
|
-
# @
|
|
70
|
-
# @type [Class<CustomFieldVariant>]
|
|
71
|
-
# @sg-ignore
|
|
68
|
+
# @type [Class<CustomFieldVariant::CustomFieldVariant>, nil]
|
|
72
69
|
variant_class = VARIANTS[variant[0]]
|
|
73
70
|
# @type [Array(Hash{String => String}, Array<Symbol, Array>)]
|
|
74
|
-
# @sg-ignore
|
|
75
71
|
return variant_class.new(gid, remaining_params).convert unless variant_class.nil?
|
|
76
72
|
|
|
77
73
|
raise "Teach me how to handle #{variant_key} = #{variant}"
|
|
@@ -79,10 +75,14 @@ module Checkoff
|
|
|
79
75
|
|
|
80
76
|
# @param key [String]
|
|
81
77
|
# @return [String]
|
|
82
|
-
# @sg-ignore
|
|
83
78
|
def gid_from_custom_field_key(key)
|
|
84
|
-
|
|
85
|
-
key.
|
|
79
|
+
gid_and_suffix = key.split('_')[2]
|
|
80
|
+
raise "Unexpected custom field param key: #{key}" if gid_and_suffix.nil?
|
|
81
|
+
|
|
82
|
+
gid = gid_and_suffix.split('.')[0]
|
|
83
|
+
raise "Unexpected custom field param key: #{key}" if gid.nil?
|
|
84
|
+
|
|
85
|
+
gid
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
# @return [Hash{String => Array<String>}]
|
|
@@ -15,6 +15,13 @@ module Checkoff
|
|
|
15
15
|
@remaining_params = T.let(remaining_params, T::Hash[String, T::Array[String]])
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# Implemented by every subclass.
|
|
19
|
+
#
|
|
20
|
+
# @return [Array(Hash{String => String}, Array<Symbol, Array>)]
|
|
21
|
+
def convert
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
end
|
|
24
|
+
|
|
18
25
|
private
|
|
19
26
|
|
|
20
27
|
# @return [String]
|
|
@@ -15,7 +15,6 @@ module Checkoff
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# @return [Array(Hash{String => String}, Array<Symbol, Array>)]
|
|
18
|
-
# @sg-ignore
|
|
19
18
|
def convert
|
|
20
19
|
return [{}, []] if date_url_params.empty?
|
|
21
20
|
|
|
@@ -30,11 +29,19 @@ module Checkoff
|
|
|
30
29
|
|
|
31
30
|
raise "Teach me to handle these parameters: #{date_url_params.inspect}" unless date_url_params.empty?
|
|
32
31
|
|
|
33
|
-
out
|
|
32
|
+
ensure_matched!(out)
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
private
|
|
37
36
|
|
|
37
|
+
# @param out [Array(Hash{String => String}, Array<Symbol, Array>), nil]
|
|
38
|
+
# @return [Array(Hash{String => String}, Array<Symbol, Array>)]
|
|
39
|
+
def ensure_matched!(out)
|
|
40
|
+
return out if out
|
|
41
|
+
|
|
42
|
+
raise "no date param matched a known prefix: #{date_url_params.inspect}"
|
|
43
|
+
end
|
|
44
|
+
|
|
38
45
|
# @param prefix [String]
|
|
39
46
|
# @return [Array(Hash{String => String}, Array<Symbol, Array>)]
|
|
40
47
|
def convert_for_prefix(prefix)
|
|
@@ -129,7 +136,6 @@ module Checkoff
|
|
|
129
136
|
end
|
|
130
137
|
|
|
131
138
|
# @param param_key [String]
|
|
132
|
-
# @sg-ignore
|
|
133
139
|
# @return [String]
|
|
134
140
|
def get_single_param(param_key)
|
|
135
141
|
raise "Expected #{param_key} to have at least one value" unless date_url_params.key? param_key
|
|
@@ -138,7 +144,10 @@ module Checkoff
|
|
|
138
144
|
|
|
139
145
|
raise "Expected #{param_key} to have one value" if value.length != 1
|
|
140
146
|
|
|
141
|
-
value[0]
|
|
147
|
+
single_value = value[0]
|
|
148
|
+
raise "Expected #{param_key} to have a non-nil value" if single_value.nil?
|
|
149
|
+
|
|
150
|
+
single_value
|
|
142
151
|
end
|
|
143
152
|
|
|
144
153
|
# @param prefix [String]
|
|
@@ -24,10 +24,8 @@ module Checkoff
|
|
|
24
24
|
# @param url [String]
|
|
25
25
|
# @return [Array(Hash{String => String}, Array)]
|
|
26
26
|
def convert_params(url)
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
# gap, flagging for the follow-up code PR rather than silently asserting non-nil
|
|
30
|
-
url_params = CGI.parse(URI.parse(url).query)
|
|
27
|
+
# URI#query is nil for a URL with no query string; CGI.parse('') correctly yields {}
|
|
28
|
+
url_params = CGI.parse(URI.parse(url).query || '')
|
|
31
29
|
custom_field_params, date_url_params, simple_url_params = partition_url_params(url_params)
|
|
32
30
|
custom_field_args, custom_field_task_selector = convert_custom_field_params(custom_field_params)
|
|
33
31
|
date_url_args, date_task_selector = convert_date_params(date_url_params)
|
|
@@ -61,11 +59,8 @@ module Checkoff
|
|
|
61
59
|
# @return [Array(Hash{String => Array<String>}, Hash{String => Array<String>}, Hash{String => Array<String>})]
|
|
62
60
|
def partition_url_params(url_params)
|
|
63
61
|
groups = T.let(url_params.to_a.group_by do |key, _values|
|
|
64
|
-
# @sg-ignore Unresolved call to start_with? — T.let wrapping this
|
|
65
|
-
# chained group_by block seems to block block-param inference
|
|
66
62
|
if key.start_with? 'custom_field_'
|
|
67
63
|
:custom_field
|
|
68
|
-
# @sg-ignore Unresolved call to include? — same T.let/block-param gap
|
|
69
64
|
elsif key.include? '_date'
|
|
70
65
|
:date
|
|
71
66
|
else
|
|
@@ -31,6 +31,15 @@ module Checkoff
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
public
|
|
35
|
+
|
|
36
|
+
# @return [Array<String>]
|
|
37
|
+
def convert
|
|
38
|
+
raise 'Implement me!'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
34
43
|
# @return [String]
|
|
35
44
|
attr_reader :key
|
|
36
45
|
|
|
@@ -226,14 +235,13 @@ module Checkoff
|
|
|
226
235
|
# @return [Hash{String => String}] the converted params
|
|
227
236
|
def convert
|
|
228
237
|
# @type [Array<Array(String, String)>]
|
|
229
|
-
# @sg-ignore
|
|
230
238
|
arr_of_tuples = simple_url_params.to_a.flat_map do |key, values|
|
|
231
239
|
# @type
|
|
232
240
|
entry = convert_arg(key, values).each_slice(2).to_a
|
|
233
241
|
entry
|
|
234
242
|
end
|
|
235
243
|
# @type [Hash{String => String}]
|
|
236
|
-
out =
|
|
244
|
+
out = arr_of_tuples.to_h
|
|
237
245
|
unless out.include? 'sort_by'
|
|
238
246
|
# keep results consistent between calls; API using default
|
|
239
247
|
# sort_by does not seem to be.
|
|
@@ -265,14 +273,12 @@ module Checkoff
|
|
|
265
273
|
# https://developers.asana.com/docs/search-tasks-in-a-workspace
|
|
266
274
|
# @param key [String] the name of the search url param
|
|
267
275
|
# @param values [Array<String>] the values of the search url param
|
|
268
|
-
# @
|
|
269
|
-
# @return [Hash{String => String}] the converted params
|
|
276
|
+
# @return [Array<String>] the converted params, as an alternating key/value flat array
|
|
270
277
|
def convert_arg(key, values)
|
|
271
278
|
# @type [Class<SimpleParam::SimpleParam>]
|
|
272
279
|
clazz = ARGS.fetch(key)
|
|
273
280
|
# @type [SimpleParam::SimpleParam]
|
|
274
281
|
obj = clazz.new(key:, values:)
|
|
275
|
-
# @sg-ignore
|
|
276
282
|
obj.convert
|
|
277
283
|
end
|
|
278
284
|
|
|
@@ -8,7 +8,7 @@ module Checkoff
|
|
|
8
8
|
module Common
|
|
9
9
|
# Base class to evaluate a project selector function given fully evaluated arguments
|
|
10
10
|
class FunctionEvaluator < ::Checkoff::SelectorClasses::FunctionEvaluator
|
|
11
|
-
# @param selector [Array
|
|
11
|
+
# @param selector [Array<Symbol, Array>, Symbol]
|
|
12
12
|
# @param custom_fields [Checkoff::CustomFields]
|
|
13
13
|
# @param _kwargs [Hash]
|
|
14
14
|
def initialize(selector:, custom_fields:, **_kwargs)
|
|
@@ -17,9 +17,12 @@ module Checkoff
|
|
|
17
17
|
super()
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
# @return [Checkoff::CustomFields]
|
|
21
|
+
attr_reader :custom_fields
|
|
22
|
+
|
|
20
23
|
private
|
|
21
24
|
|
|
22
|
-
# @return [Array
|
|
25
|
+
# @return [Array<Symbol, Array>, Symbol]
|
|
23
26
|
attr_reader :selector
|
|
24
27
|
end
|
|
25
28
|
end
|