checkoff 0.19.2 → 0.21.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/.circleci/config.yml +6 -11
- data/.gitignore +1 -5
- data/.overcommit.yml +21 -0
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +61 -24
- data/Makefile +22 -2
- data/checkoff.gemspec +10 -3
- data/fix.sh +22 -3
- data/lib/checkoff/internal/config_loader.rb +1 -1
- data/lib/checkoff/internal/search_url/custom_field_param_converter.rb +73 -0
- data/lib/checkoff/internal/search_url/custom_field_variant.rb +111 -0
- data/lib/checkoff/internal/search_url/parser.rb +44 -0
- data/lib/checkoff/internal/search_url/simple_param_converter.rb +82 -0
- data/lib/checkoff/internal/search_url.rb +3 -0
- data/lib/checkoff/sections.rb +2 -1
- data/lib/checkoff/subtasks.rb +1 -1
- data/lib/checkoff/task_searches.rb +2 -2
- data/lib/checkoff/version.rb +1 -1
- data/rakelib/clear_metrics.rake +1 -9
- metadata +71 -11
- data/coverage/.last_run.json +0 -6
- data/lib/checkoff/internal/search_url_parser.rb +0 -228
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d7c237ce35c02738d3170942d13d3d47d02d0fc0ed65eb78b836b7c444aed241
|
|
4
|
+
data.tar.gz: 8ab5d779ab9feadeeb66d12294e25f8b2baf0de32d8a917618f3adc26c77a4ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz: '
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '078c32912450408243aea106d973047fabfb4db52c6267f2dd0030894a7f1b898a89acce8115da066e2b1fa2e45560286148a7aa6ccc0632d869797cf4b008aa'
|
|
7
|
+
data.tar.gz: 388f84ed37bc63dc68182e4b484e18fc5ab591625840c9f6a4f60fa385340cf6e7c4386cd56ee146100d3d5e99952f8d7c77e9c12c3ae9e47fbabc6b7e9e773e
|
data/.circleci/config.yml
CHANGED
|
@@ -37,9 +37,9 @@ commands:
|
|
|
37
37
|
sed -E -e 's/checkoff \([[:digit:]]+.[[:digit:]]+.[[:digit:]]+\)/checkoff (0.1.0)/g' \
|
|
38
38
|
Gemfile.lock > Gemfile.lock.deversioned
|
|
39
39
|
- restore_cache:
|
|
40
|
-
key: gems-
|
|
40
|
+
key: gems-v2-{{ checksum "Gemfile.lock.deversioned" }}
|
|
41
41
|
- restore_cache:
|
|
42
|
-
key: wheels-
|
|
42
|
+
key: wheels-v2-{{ checksum "requirements_dev.txt" }}
|
|
43
43
|
- run:
|
|
44
44
|
name: Initialize packages
|
|
45
45
|
command: |
|
|
@@ -54,11 +54,11 @@ commands:
|
|
|
54
54
|
exit 1
|
|
55
55
|
fi
|
|
56
56
|
- save_cache:
|
|
57
|
-
key: gems-
|
|
57
|
+
key: gems-v2-{{ checksum "Gemfile.lock.deversioned" }}
|
|
58
58
|
paths:
|
|
59
59
|
- "vendor/bundle"
|
|
60
60
|
- save_cache:
|
|
61
|
-
key: wheels-
|
|
61
|
+
key: wheels-v2-{{ checksum "requirements_dev.txt" }}
|
|
62
62
|
paths:
|
|
63
63
|
- "/home/circleci/.cache/pip/wheels"
|
|
64
64
|
- run:
|
|
@@ -104,13 +104,8 @@ jobs:
|
|
|
104
104
|
steps:
|
|
105
105
|
- set_up_environment
|
|
106
106
|
- run_with_languages:
|
|
107
|
-
label:
|
|
108
|
-
command:
|
|
109
|
-
bundle exec rake citest
|
|
110
|
-
# https://github.com/bluelabsio/records-mover/blob/master/Makefile#L25
|
|
111
|
-
git status --porcelain coverage/.last_run.json
|
|
112
|
-
git diff coverage/.last_run.json
|
|
113
|
-
test -z "$(git status --porcelain coverage/.last_run.json)"
|
|
107
|
+
label: test
|
|
108
|
+
command: make citest cicoverage
|
|
114
109
|
# This seemed to shave 5ish% of the build time off when added
|
|
115
110
|
resource_class: large
|
|
116
111
|
|
data/.gitignore
CHANGED
data/.overcommit.yml
CHANGED
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
# Uncomment the following lines to make the configuration take effect.
|
|
18
18
|
|
|
19
19
|
PreCommit:
|
|
20
|
+
# Extend default config at https://github.com/sds/overcommit/blob/master/config/default.yml
|
|
21
|
+
BrokenSymlinks:
|
|
22
|
+
enabled: true
|
|
23
|
+
description: 'Check for broken symlinks'
|
|
24
|
+
quiet: true
|
|
25
|
+
exclude:
|
|
26
|
+
# This is a symlink to sibling checkout of vincelifedaily, used
|
|
27
|
+
# only in local development
|
|
28
|
+
- config/env.1p
|
|
20
29
|
RuboCop:
|
|
21
30
|
enabled: true
|
|
22
31
|
on_warn: fail # Treat all warnings as failures
|
|
@@ -29,6 +38,10 @@ PreCommit:
|
|
|
29
38
|
- '**/Gemfile'
|
|
30
39
|
- '**/Rakefile'
|
|
31
40
|
- 'bin/*'
|
|
41
|
+
- 'script/*'
|
|
42
|
+
exclude:
|
|
43
|
+
- db/migrate/*.rb
|
|
44
|
+
- db/schema.rb
|
|
32
45
|
PythonFlake8:
|
|
33
46
|
enabled: true
|
|
34
47
|
on_warn: fail
|
|
@@ -37,6 +50,7 @@ PreCommit:
|
|
|
37
50
|
on_warn: fail
|
|
38
51
|
include:
|
|
39
52
|
- '.envrc'
|
|
53
|
+
- '**/*.sh'
|
|
40
54
|
YamlLint:
|
|
41
55
|
enabled: true
|
|
42
56
|
on_warn: fail
|
|
@@ -53,3 +67,10 @@ PrePush:
|
|
|
53
67
|
include:
|
|
54
68
|
- 'test/unit/**/test*.rb'
|
|
55
69
|
enabled: true
|
|
70
|
+
|
|
71
|
+
#PostCheckout:
|
|
72
|
+
# ALL: # Special hook name that customizes all hooks of this type
|
|
73
|
+
# quiet: true # Change all post-checkout hooks to only display output on failure
|
|
74
|
+
#
|
|
75
|
+
# IndexTags:
|
|
76
|
+
# enabled: true # Generate a tags file with `ctags` each time HEAD changes
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -12,7 +12,7 @@ GIT
|
|
|
12
12
|
PATH
|
|
13
13
|
remote: .
|
|
14
14
|
specs:
|
|
15
|
-
checkoff (0.
|
|
15
|
+
checkoff (0.21.0)
|
|
16
16
|
activesupport
|
|
17
17
|
asana (> 0.10.0)
|
|
18
18
|
cache_method
|
|
@@ -22,13 +22,16 @@ PATH
|
|
|
22
22
|
GEM
|
|
23
23
|
remote: https://rubygems.org/
|
|
24
24
|
specs:
|
|
25
|
-
activesupport (
|
|
25
|
+
activesupport (7.0.4)
|
|
26
26
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
27
27
|
i18n (>= 1.6, < 2)
|
|
28
28
|
minitest (>= 5.1)
|
|
29
29
|
tzinfo (~> 2.0)
|
|
30
|
-
|
|
30
|
+
addressable (2.8.1)
|
|
31
|
+
public_suffix (>= 2.0.2, < 6.0)
|
|
32
|
+
ansi (1.5.0)
|
|
31
33
|
ast (2.4.2)
|
|
34
|
+
builder (3.2.4)
|
|
32
35
|
bump (0.10.0)
|
|
33
36
|
cache (0.4.1)
|
|
34
37
|
cache_method (0.2.7)
|
|
@@ -38,8 +41,12 @@ GEM
|
|
|
38
41
|
childprocess (4.0.0)
|
|
39
42
|
coderay (1.1.3)
|
|
40
43
|
concurrent-ruby (1.1.10)
|
|
41
|
-
|
|
44
|
+
crack (0.4.5)
|
|
45
|
+
rexml
|
|
46
|
+
dalli (3.2.3)
|
|
47
|
+
diff-lcs (1.5.0)
|
|
42
48
|
docile (1.4.0)
|
|
49
|
+
fakeweb (1.3.0)
|
|
43
50
|
faraday (1.5.1)
|
|
44
51
|
faraday-em_http (~> 1.0)
|
|
45
52
|
faraday-em_synchrony (~> 1.0)
|
|
@@ -63,17 +70,19 @@ GEM
|
|
|
63
70
|
faraday_middleware
|
|
64
71
|
multi_json
|
|
65
72
|
gli (2.21.0)
|
|
73
|
+
hashdiff (1.0.1)
|
|
66
74
|
i18n (1.12.0)
|
|
67
75
|
concurrent-ruby (~> 1.0)
|
|
68
76
|
imagen (0.1.8)
|
|
69
77
|
parser (>= 2.5, != 2.5.1.1)
|
|
70
78
|
iniparse (1.5.0)
|
|
79
|
+
json (2.6.2)
|
|
71
80
|
jwt (2.2.3)
|
|
72
81
|
kramdown (2.4.0)
|
|
73
82
|
rexml
|
|
74
83
|
kramdown-parser-gfm (1.1.0)
|
|
75
84
|
kramdown (~> 2.0)
|
|
76
|
-
mdl (0.
|
|
85
|
+
mdl (0.12.0)
|
|
77
86
|
kramdown (~> 2.3)
|
|
78
87
|
kramdown-parser-gfm (~> 1.1)
|
|
79
88
|
mixlib-cli (~> 2.1, >= 2.1.1)
|
|
@@ -82,12 +91,17 @@ GEM
|
|
|
82
91
|
method_source (1.0.0)
|
|
83
92
|
minitest (5.16.2)
|
|
84
93
|
minitest-profile (0.0.2)
|
|
94
|
+
minitest-reporters (1.5.0)
|
|
95
|
+
ansi
|
|
96
|
+
builder
|
|
97
|
+
minitest (>= 5.0)
|
|
98
|
+
ruby-progressbar
|
|
85
99
|
mixlib-cli (2.1.8)
|
|
86
100
|
mixlib-config (3.0.27)
|
|
87
101
|
tomlrb
|
|
88
102
|
mixlib-shellout (3.2.7)
|
|
89
103
|
chef-utils
|
|
90
|
-
mocha (
|
|
104
|
+
mocha (2.0.0.alpha.1)
|
|
91
105
|
multi_json (1.15.0)
|
|
92
106
|
multi_xml (0.6.0)
|
|
93
107
|
multipart-post (2.1.1)
|
|
@@ -101,35 +115,50 @@ GEM
|
|
|
101
115
|
childprocess (>= 0.6.3, < 5)
|
|
102
116
|
iniparse (~> 1.4)
|
|
103
117
|
rexml (~> 3.2)
|
|
104
|
-
parallel (1.
|
|
105
|
-
parser (3.
|
|
118
|
+
parallel (1.22.1)
|
|
119
|
+
parser (3.1.2.1)
|
|
106
120
|
ast (~> 2.4.1)
|
|
107
121
|
pry (0.14.1)
|
|
108
122
|
coderay (~> 1.1)
|
|
109
123
|
method_source (~> 1.0)
|
|
124
|
+
public_suffix (5.0.0)
|
|
110
125
|
rack (2.2.4)
|
|
111
|
-
rainbow (3.
|
|
126
|
+
rainbow (3.1.1)
|
|
112
127
|
rake (13.0.3)
|
|
113
|
-
regexp_parser (2.
|
|
128
|
+
regexp_parser (2.6.0)
|
|
114
129
|
rexml (3.2.5)
|
|
115
|
-
|
|
130
|
+
rspec (3.11.0)
|
|
131
|
+
rspec-core (~> 3.11.0)
|
|
132
|
+
rspec-expectations (~> 3.11.0)
|
|
133
|
+
rspec-mocks (~> 3.11.0)
|
|
134
|
+
rspec-core (3.11.0)
|
|
135
|
+
rspec-support (~> 3.11.0)
|
|
136
|
+
rspec-expectations (3.11.1)
|
|
137
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
138
|
+
rspec-support (~> 3.11.0)
|
|
139
|
+
rspec-mocks (3.11.1)
|
|
140
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
141
|
+
rspec-support (~> 3.11.0)
|
|
142
|
+
rspec-support (3.11.1)
|
|
143
|
+
rubocop (1.36.0)
|
|
144
|
+
json (~> 2.3)
|
|
116
145
|
parallel (~> 1.10)
|
|
117
|
-
parser (>= 3.
|
|
146
|
+
parser (>= 3.1.2.1)
|
|
118
147
|
rainbow (>= 2.2.2, < 4.0)
|
|
119
148
|
regexp_parser (>= 1.8, < 3.0)
|
|
120
|
-
rexml
|
|
121
|
-
rubocop-ast (>= 1.
|
|
149
|
+
rexml (>= 3.2.5, < 4.0)
|
|
150
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
|
122
151
|
ruby-progressbar (~> 1.7)
|
|
123
152
|
unicode-display_width (>= 1.4.0, < 3.0)
|
|
124
|
-
rubocop-ast (1.
|
|
125
|
-
parser (>= 3.
|
|
153
|
+
rubocop-ast (1.22.0)
|
|
154
|
+
parser (>= 3.1.1.0)
|
|
126
155
|
rubocop-minitest (0.12.1)
|
|
127
156
|
rubocop (>= 0.90, < 2.0)
|
|
128
157
|
rubocop-rake (0.5.1)
|
|
129
158
|
rubocop
|
|
130
159
|
ruby-progressbar (1.11.0)
|
|
131
160
|
ruby2_keywords (0.0.5)
|
|
132
|
-
rugged (1.
|
|
161
|
+
rugged (1.5.0.1)
|
|
133
162
|
simplecov (0.21.2)
|
|
134
163
|
docile (~> 1.1)
|
|
135
164
|
simplecov-html (~> 0.11)
|
|
@@ -140,17 +169,21 @@ GEM
|
|
|
140
169
|
tomlrb (2.0.3)
|
|
141
170
|
tzinfo (2.0.5)
|
|
142
171
|
concurrent-ruby (~> 1.0)
|
|
143
|
-
undercover (0.4.
|
|
172
|
+
undercover (0.4.5)
|
|
144
173
|
imagen (>= 0.1.8)
|
|
145
174
|
rainbow (>= 2.1, < 4.0)
|
|
146
|
-
rugged (>= 0.27, < 1.
|
|
147
|
-
unicode-display_width (2.
|
|
148
|
-
|
|
175
|
+
rugged (>= 0.27, < 1.6)
|
|
176
|
+
unicode-display_width (2.3.0)
|
|
177
|
+
webmock (3.18.1)
|
|
178
|
+
addressable (>= 2.8.0)
|
|
179
|
+
crack (>= 0.3.2)
|
|
180
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
149
181
|
|
|
150
182
|
PLATFORMS
|
|
151
183
|
ruby
|
|
152
184
|
x86_64-darwin-19
|
|
153
185
|
x86_64-darwin-20
|
|
186
|
+
x86_64-darwin-21
|
|
154
187
|
x86_64-linux
|
|
155
188
|
|
|
156
189
|
DEPENDENCIES
|
|
@@ -158,18 +191,22 @@ DEPENDENCIES
|
|
|
158
191
|
bump
|
|
159
192
|
bundler
|
|
160
193
|
checkoff!
|
|
194
|
+
fakeweb
|
|
161
195
|
mdl
|
|
162
196
|
minitest-profile
|
|
163
|
-
|
|
197
|
+
minitest-reporters
|
|
198
|
+
mocha (~> 2.0.0.alpha.1)
|
|
164
199
|
overcommit (>= 0.58.0)
|
|
165
200
|
pry
|
|
166
201
|
rake (~> 13.0)
|
|
167
|
-
|
|
202
|
+
rspec (>= 3.4)
|
|
203
|
+
rubocop (~> 1.36)
|
|
168
204
|
rubocop-minitest
|
|
169
205
|
rubocop-rake
|
|
170
206
|
simplecov (>= 0.18.0)
|
|
171
207
|
simplecov-lcov
|
|
172
208
|
undercover
|
|
209
|
+
webmock
|
|
173
210
|
|
|
174
211
|
BUNDLED WITH
|
|
175
|
-
2.
|
|
212
|
+
2.3.26
|
data/Makefile
CHANGED
|
@@ -15,7 +15,17 @@ export PRINT_HELP_PYSCRIPT
|
|
|
15
15
|
help:
|
|
16
16
|
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
|
|
17
17
|
|
|
18
|
-
default:
|
|
18
|
+
default: clean-coverage test coverage clean-typecoverage typecheck typecoverage quality ## run default typechecking, tests and quality
|
|
19
|
+
|
|
20
|
+
typecheck: ## validate types in code and configuration
|
|
21
|
+
|
|
22
|
+
citypecheck: typecheck ## Run type check from CircleCI
|
|
23
|
+
|
|
24
|
+
typecoverage: typecheck ## Run type checking and then ratchet coverage in metrics/
|
|
25
|
+
|
|
26
|
+
clean-typecoverage: ## Clean out type-related coverage previous results to avoid flaky results
|
|
27
|
+
|
|
28
|
+
citypecoverage: typecoverage ## Run type checking, ratchet coverage, and then complain if ratchet needs to be committed
|
|
19
29
|
|
|
20
30
|
requirements_dev.txt.installed: requirements_dev.txt
|
|
21
31
|
pip install -q --disable-pip-version-check -r requirements_dev.txt
|
|
@@ -40,7 +50,7 @@ clear_metrics: ## remove or reset result artifacts created by tests and quality
|
|
|
40
50
|
clean: clear_metrics ## remove all built artifacts
|
|
41
51
|
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
citest: test ## Run unit tests from CircleCI
|
|
44
54
|
|
|
45
55
|
overcommit: ## run precommit quality checks
|
|
46
56
|
bundle exec overcommit --run
|
|
@@ -56,6 +66,16 @@ localtest: ## run default local actions
|
|
|
56
66
|
repl: ## Load up checkoff in pry
|
|
57
67
|
@bundle exec rake repl
|
|
58
68
|
|
|
69
|
+
clean-coverage:
|
|
70
|
+
@bundle exec rake clear_metrics
|
|
71
|
+
|
|
72
|
+
coverage: test report-coverage ## check code coverage
|
|
73
|
+
@bundle exec rake undercover
|
|
74
|
+
|
|
75
|
+
report-coverage: test ## Report summary of coverage to stdout, and generate HTML, XML coverage report
|
|
76
|
+
|
|
77
|
+
cicoverage: coverage ## check code coverage
|
|
78
|
+
|
|
59
79
|
update_from_cookiecutter: ## Bring in changes from template project used to create this repo
|
|
60
80
|
bundle exec overcommit --uninstall
|
|
61
81
|
IN_COOKIECUTTER_PROJECT_UPGRADER=1 cookiecutter_project_upgrader || true
|
data/checkoff.gemspec
CHANGED
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.summary = 'Command-line and gem client for Asana (unofficial)'
|
|
14
14
|
spec.homepage = 'https://github.com/apiology/checkoff'
|
|
15
15
|
spec.license = 'MIT license'
|
|
16
|
-
spec.required_ruby_version = '>= 2.
|
|
16
|
+
spec.required_ruby_version = '>= 2.7'
|
|
17
17
|
|
|
18
18
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
19
19
|
`git ls-files -z`.split("\x0").reject do |f|
|
|
@@ -32,20 +32,27 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
|
|
33
33
|
spec.add_development_dependency 'bump'
|
|
34
34
|
spec.add_development_dependency 'bundler'
|
|
35
|
+
spec.add_development_dependency 'fakeweb'
|
|
35
36
|
spec.add_development_dependency 'mdl'
|
|
36
37
|
spec.add_development_dependency 'minitest-profile'
|
|
37
|
-
spec.add_development_dependency '
|
|
38
|
+
spec.add_development_dependency 'minitest-reporters'
|
|
39
|
+
spec.add_development_dependency 'mocha', ['~> 2.0.0.alpha.1']
|
|
38
40
|
# 0.58.0 and 0.57.0 don't seem super compatible with signatures, and
|
|
39
41
|
# magit doesn't seem to want to use the bundled version at the moment,
|
|
40
42
|
# so let's favor the more recent version...
|
|
41
43
|
spec.add_development_dependency 'overcommit', ['>=0.58.0']
|
|
42
44
|
spec.add_development_dependency 'pry'
|
|
43
45
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
44
|
-
spec.add_development_dependency '
|
|
46
|
+
spec.add_development_dependency 'rspec', '>=3.4'
|
|
47
|
+
spec.add_development_dependency 'rubocop', ['~> 1.36']
|
|
45
48
|
spec.add_development_dependency 'rubocop-minitest'
|
|
46
49
|
spec.add_development_dependency 'rubocop-rake'
|
|
47
50
|
# ensure version with branch coverage
|
|
48
51
|
spec.add_development_dependency 'simplecov', ['>=0.18.0']
|
|
49
52
|
spec.add_development_dependency 'simplecov-lcov'
|
|
50
53
|
spec.add_development_dependency 'undercover'
|
|
54
|
+
spec.add_development_dependency 'webmock'
|
|
55
|
+
spec.metadata = {
|
|
56
|
+
'rubygems_mfa_required' => 'true',
|
|
57
|
+
}
|
|
51
58
|
end
|
data/fix.sh
CHANGED
|
@@ -69,7 +69,18 @@ ensure_rbenv() {
|
|
|
69
69
|
|
|
70
70
|
latest_ruby_version() {
|
|
71
71
|
major_minor=${1}
|
|
72
|
-
|
|
72
|
+
|
|
73
|
+
# Double check that this command doesn't error out under -e
|
|
74
|
+
rbenv install --list >/dev/null 2>&1
|
|
75
|
+
|
|
76
|
+
# not sure why, but 'rbenv install --list' below exits with error code
|
|
77
|
+
# 1...after providing the same output the previous line gave when it
|
|
78
|
+
# exited with error code 0.
|
|
79
|
+
#
|
|
80
|
+
# https://github.com/rbenv/rbenv/issues/1441
|
|
81
|
+
set +e
|
|
82
|
+
rbenv install --list 2>/dev/null | cat | grep "^${major_minor}."
|
|
83
|
+
set -e
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
ensure_dev_library() {
|
|
@@ -91,12 +102,20 @@ ensure_ruby_build_requirements() {
|
|
|
91
102
|
ensure_dev_library openssl/ssl.h openssl libssl-dev
|
|
92
103
|
}
|
|
93
104
|
|
|
105
|
+
ensure_latest_ruby_build_definitions() {
|
|
106
|
+
ensure_rbenv
|
|
107
|
+
|
|
108
|
+
git -C "$(rbenv root)"/plugins/ruby-build pull
|
|
109
|
+
}
|
|
110
|
+
|
|
94
111
|
# You can find out which feature versions are still supported / have
|
|
95
112
|
# been release here: https://www.ruby-lang.org/en/downloads/
|
|
96
113
|
ensure_ruby_versions() {
|
|
114
|
+
ensure_latest_ruby_build_definitions
|
|
115
|
+
|
|
97
116
|
# You can find out which feature versions are still supported / have
|
|
98
117
|
# been release here: https://www.ruby-lang.org/en/downloads/
|
|
99
|
-
ruby_versions="$(latest_ruby_version 2.
|
|
118
|
+
ruby_versions="$(latest_ruby_version 2.7)"
|
|
100
119
|
|
|
101
120
|
echo "Latest Ruby versions: ${ruby_versions}"
|
|
102
121
|
|
|
@@ -169,7 +188,7 @@ ensure_bundle() {
|
|
|
169
188
|
#
|
|
170
189
|
# This affects nokogiri, which will try to reinstall itself in
|
|
171
190
|
# Docker builds where it's already installed if this is not run.
|
|
172
|
-
for platform in x86_64-darwin-
|
|
191
|
+
for platform in x86_64-darwin-21 x86_64-linux
|
|
173
192
|
do
|
|
174
193
|
grep "${platform:?}" Gemfile.lock >/dev/null 2>&1 || bundle lock --add-platform "${platform:?}"
|
|
175
194
|
done
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'custom_field_variant'
|
|
4
|
+
|
|
5
|
+
module Checkoff
|
|
6
|
+
module Internal
|
|
7
|
+
module SearchUrl
|
|
8
|
+
# Convert custom field parameters from an Asana search URL into
|
|
9
|
+
# API search arguments and Checkoff task selectors
|
|
10
|
+
class CustomFieldParamConverter
|
|
11
|
+
def initialize(custom_field_params:)
|
|
12
|
+
@custom_field_params = custom_field_params
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def convert
|
|
16
|
+
args = {}
|
|
17
|
+
task_selector = []
|
|
18
|
+
by_custom_field.each do |gid, single_custom_field_params|
|
|
19
|
+
new_args, new_task_selector = convert_single_custom_field_params(gid,
|
|
20
|
+
single_custom_field_params)
|
|
21
|
+
args, task_selector = merge_args_and_task_selectors(args, new_args,
|
|
22
|
+
task_selector, new_task_selector)
|
|
23
|
+
end
|
|
24
|
+
[args, task_selector]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def by_custom_field
|
|
30
|
+
custom_field_params.group_by do |key, _value|
|
|
31
|
+
gid_from_custom_field_key(key)
|
|
32
|
+
end.transform_values(&:to_h)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def merge_args_and_task_selectors(args, new_args, task_selector, new_task_selector)
|
|
36
|
+
args = args.merge(new_args)
|
|
37
|
+
return [args, task_selector] if new_task_selector == []
|
|
38
|
+
|
|
39
|
+
raise 'Teach me how to merge task selectors' unless task_selector == []
|
|
40
|
+
|
|
41
|
+
task_selector = new_task_selector
|
|
42
|
+
|
|
43
|
+
[args, task_selector]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
VARIANTS = {
|
|
47
|
+
'is' => CustomFieldVariant::Is,
|
|
48
|
+
'no_value' => CustomFieldVariant::NoValue,
|
|
49
|
+
'is_not' => CustomFieldVariant::IsNot,
|
|
50
|
+
'less_than' => CustomFieldVariant::LessThan,
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
53
|
+
def convert_single_custom_field_params(gid, single_custom_field_params)
|
|
54
|
+
variant_key = "custom_field_#{gid}.variant"
|
|
55
|
+
variant = single_custom_field_params.fetch(variant_key)
|
|
56
|
+
remaining_params = single_custom_field_params.reject { |k, _v| k == variant_key }
|
|
57
|
+
raise "Teach me how to handle #{variant_key} = #{variant}" unless variant.length == 1
|
|
58
|
+
|
|
59
|
+
variant_class = VARIANTS[variant[0]]
|
|
60
|
+
return variant_class.new(gid, remaining_params).convert unless variant_class.nil?
|
|
61
|
+
|
|
62
|
+
raise "Teach me how to handle #{variant_key} = #{variant}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def gid_from_custom_field_key(key)
|
|
66
|
+
key.split('_')[2].split('.')[0]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
attr_reader :custom_field_params
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Checkoff
|
|
4
|
+
module Internal
|
|
5
|
+
module SearchUrl
|
|
6
|
+
module CustomFieldVariant
|
|
7
|
+
# base class for handling different custom_field_#{gid}.variant params
|
|
8
|
+
class CustomFieldVariant
|
|
9
|
+
def initialize(gid, remaining_params)
|
|
10
|
+
@gid = gid
|
|
11
|
+
@remaining_params = remaining_params
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
attr_reader :gid, :remaining_params
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# custom_field_#{gid}.variant = 'less_than'
|
|
20
|
+
class LessThan < CustomFieldVariant
|
|
21
|
+
def convert
|
|
22
|
+
max_param = "custom_field_#{gid}.max"
|
|
23
|
+
case remaining_params.keys
|
|
24
|
+
when [max_param]
|
|
25
|
+
convert_single_custom_field_less_than_params_max_param(max_param)
|
|
26
|
+
else
|
|
27
|
+
raise "Teach me how to handle #{remaining_params}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def convert_single_custom_field_less_than_params_max_param(max_param)
|
|
34
|
+
max_values = remaining_params.fetch(max_param)
|
|
35
|
+
unless max_values.length == 1
|
|
36
|
+
raise "Teach me how to handle these remaining keys for #{max_param}: #{remaining_params}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
max_value = max_values[0]
|
|
40
|
+
empty_task_selector = []
|
|
41
|
+
[{ "custom_fields.#{gid}.less_than" => max_value }, empty_task_selector]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# custom_field_#{gid}.variant = 'is_not'
|
|
46
|
+
class IsNot < CustomFieldVariant
|
|
47
|
+
def convert
|
|
48
|
+
case remaining_params.keys
|
|
49
|
+
when ["custom_field_#{gid}.selected_options"]
|
|
50
|
+
convert_single_custom_field_is_not_params_selected_options
|
|
51
|
+
else
|
|
52
|
+
raise "Teach me how to handle #{remaining_params}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def convert_single_custom_field_is_not_params_selected_options
|
|
59
|
+
selected_options = remaining_params.fetch("custom_field_#{gid}.selected_options")
|
|
60
|
+
raise "Teach me how to handle #{remaining_params}" unless selected_options.length == 1
|
|
61
|
+
|
|
62
|
+
[{ "custom_fields.#{gid}.is_set" => 'true' },
|
|
63
|
+
['not',
|
|
64
|
+
['custom_field_gid_value_contains_any_gid',
|
|
65
|
+
gid,
|
|
66
|
+
selected_options.fetch(0).split('~')]]]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# custom_field_#{gid}.variant = 'no_value'
|
|
71
|
+
class NoValue < CustomFieldVariant
|
|
72
|
+
def convert
|
|
73
|
+
unless remaining_params.length.zero?
|
|
74
|
+
raise "Teach me how to handle these remaining keys for #{variant_key}: #{remaining_params}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
empty_task_selector = []
|
|
78
|
+
[{ "custom_fields.#{gid}.is_set" => 'false' }, empty_task_selector]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# custom_field_#{gid}.variant = 'is'
|
|
83
|
+
class Is < CustomFieldVariant
|
|
84
|
+
def convert
|
|
85
|
+
unless remaining_params.length == 1
|
|
86
|
+
raise "Teach me how to handle these remaining keys for #{variant_key}: #{remaining_params}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
key, values = remaining_params.to_a[0]
|
|
90
|
+
convert_custom_field_is_arg(key, values)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def convert_custom_field_is_arg(key, values)
|
|
96
|
+
empty_task_selector = []
|
|
97
|
+
|
|
98
|
+
if key.end_with? '.selected_options'
|
|
99
|
+
raise "Too many values found for #{key}: #{values}" if values.length != 1
|
|
100
|
+
|
|
101
|
+
return [{ "custom_fields.#{gid}.value" => values[0] },
|
|
102
|
+
empty_task_selector]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
raise "Teach me how to handle #{key} = #{values}"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
require_relative 'simple_param_converter'
|
|
6
|
+
require_relative 'custom_field_param_converter'
|
|
7
|
+
|
|
8
|
+
module Checkoff
|
|
9
|
+
module Internal
|
|
10
|
+
module SearchUrl
|
|
11
|
+
# Parse Asana search URLs into parameters suitable to pass into
|
|
12
|
+
# the /workspaces/{workspace_gid}/tasks/search endpoint
|
|
13
|
+
class Parser
|
|
14
|
+
def initialize(_deps = {})
|
|
15
|
+
# allow dependencies to be passed in by tests
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def convert_params(url)
|
|
19
|
+
url_params = CGI.parse(URI.parse(url).query)
|
|
20
|
+
custom_field_params, simple_url_params = partition_url_params(url_params)
|
|
21
|
+
custom_field_args, task_selector = convert_custom_field_params(custom_field_params)
|
|
22
|
+
simple_url_args = convert_simple_params(simple_url_params)
|
|
23
|
+
[custom_field_args.merge(simple_url_args), task_selector]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def convert_simple_params(simple_url_params)
|
|
29
|
+
SimpleParamConverter.new(simple_url_params: simple_url_params).convert
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def convert_custom_field_params(custom_field_params)
|
|
33
|
+
CustomFieldParamConverter.new(custom_field_params: custom_field_params).convert
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def partition_url_params(url_params)
|
|
37
|
+
url_params.to_a.partition do |key, _values|
|
|
38
|
+
key.start_with? 'custom_field_'
|
|
39
|
+
end.map(&:to_h)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'custom_field_param_converter'
|
|
4
|
+
|
|
5
|
+
module Checkoff
|
|
6
|
+
module Internal
|
|
7
|
+
module SearchUrl
|
|
8
|
+
module SimpleParam
|
|
9
|
+
# base class for handling different types of search url params
|
|
10
|
+
class SimpleParam
|
|
11
|
+
def initialize(values:)
|
|
12
|
+
@values = values
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
attr_reader :values
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Handle 'any_projects.ids' search url param
|
|
21
|
+
class AnyProjectsIds < SimpleParam
|
|
22
|
+
def convert
|
|
23
|
+
['projects.any', values.join(',')]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Handle 'completion' search url param
|
|
28
|
+
class Completion < SimpleParam
|
|
29
|
+
def convert
|
|
30
|
+
raise "Teach me how to handle #{key} = #{values}" if values.length != 1
|
|
31
|
+
|
|
32
|
+
value = values.fetch(0)
|
|
33
|
+
raise "Teach me how to handle #{key} = #{values}" if value != 'incomplete'
|
|
34
|
+
|
|
35
|
+
['completed', false]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Handle 'not_tags.ids' search url param
|
|
40
|
+
class NotTagsIds < SimpleParam
|
|
41
|
+
def convert
|
|
42
|
+
raise "Teach me how to handle #{key} = #{values}" if values.length != 1
|
|
43
|
+
|
|
44
|
+
value = values.fetch(0)
|
|
45
|
+
tag_ids = value.split('~')
|
|
46
|
+
['tags.not', tag_ids.join(',')]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Convert simple parameters - ones where the param name itself
|
|
52
|
+
# doesn't encode any parameters'
|
|
53
|
+
class SimpleParamConverter
|
|
54
|
+
def initialize(simple_url_params:)
|
|
55
|
+
@simple_url_params = simple_url_params
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def convert
|
|
59
|
+
simple_url_params.to_a.to_h do |key, values|
|
|
60
|
+
convert_arg(key, values)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
ARGS = {
|
|
67
|
+
'any_projects.ids' => SimpleParam::AnyProjectsIds,
|
|
68
|
+
'completion' => SimpleParam::Completion,
|
|
69
|
+
'not_tags.ids' => SimpleParam::NotTagsIds,
|
|
70
|
+
}.freeze
|
|
71
|
+
|
|
72
|
+
# https://developers.asana.com/docs/search-tasks-in-a-workspace
|
|
73
|
+
def convert_arg(key, values)
|
|
74
|
+
clazz = ARGS.fetch(key)
|
|
75
|
+
clazz.new(values: values).convert
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
attr_reader :simple_url_params
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/checkoff/sections.rb
CHANGED
|
@@ -110,7 +110,8 @@ module Checkoff
|
|
|
110
110
|
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
|
111
111
|
|
|
112
112
|
current_section = membership['section']['name']
|
|
113
|
-
|
|
113
|
+
inbox_section_names = ['(no section)', 'Untitled section', 'Inbox']
|
|
114
|
+
current_section = nil if inbox_section_names.include?(current_section)
|
|
114
115
|
by_section[current_section] ||= []
|
|
115
116
|
by_section[current_section] << task
|
|
116
117
|
end
|
data/lib/checkoff/subtasks.rb
CHANGED
|
@@ -41,7 +41,7 @@ module Checkoff
|
|
|
41
41
|
def raw_subtasks(task)
|
|
42
42
|
task_options = projects.task_options
|
|
43
43
|
task_options[:options][:fields] << 'is_rendered_as_separator'
|
|
44
|
-
task.subtasks(task_options)
|
|
44
|
+
task.subtasks(**task_options)
|
|
45
45
|
end
|
|
46
46
|
cache_method :raw_subtasks, SHORT_CACHE_TIME
|
|
47
47
|
|
|
@@ -12,7 +12,7 @@ require_relative 'task_selectors'
|
|
|
12
12
|
require 'asana/resource_includes/collection'
|
|
13
13
|
require 'asana/resource_includes/response_helper'
|
|
14
14
|
|
|
15
|
-
require 'checkoff/internal/
|
|
15
|
+
require 'checkoff/internal/search_url'
|
|
16
16
|
|
|
17
17
|
# https://developers.asana.com/docs/task-searches
|
|
18
18
|
module Checkoff
|
|
@@ -33,7 +33,7 @@ module Checkoff
|
|
|
33
33
|
projects: Checkoff::Projects.new(config: config),
|
|
34
34
|
clients: Checkoff::Clients.new(config: config),
|
|
35
35
|
client: clients.client,
|
|
36
|
-
search_url_parser: Checkoff::Internal::
|
|
36
|
+
search_url_parser: Checkoff::Internal::SearchUrl::Parser.new,
|
|
37
37
|
asana_resources_collection_class: Asana::Resources::Collection)
|
|
38
38
|
@workspaces = workspaces
|
|
39
39
|
@task_selectors = task_selectors
|
data/lib/checkoff/version.rb
CHANGED
data/rakelib/clear_metrics.rake
CHANGED
|
@@ -3,15 +3,7 @@
|
|
|
3
3
|
desc 'Ensure that any locally ratcheted coverage metrics are cleared back ' \
|
|
4
4
|
'to git baseline'
|
|
5
5
|
task :clear_metrics do |_t|
|
|
6
|
-
ret =
|
|
7
|
-
system('git checkout coverage/.last_run.json')
|
|
8
|
-
raise unless ret
|
|
9
|
-
|
|
10
6
|
# Without this old lines which are removed are still counted,
|
|
11
7
|
# leading to inconsistent coverage percentages between runs.
|
|
12
|
-
|
|
13
|
-
# need to save coverage/.last_run.json
|
|
14
|
-
ret =
|
|
15
|
-
system('rm -fr coverage/assets coverage/.*.json.lock coverage/lcov/* coverage/index.html coverage/.resultset.json')
|
|
16
|
-
raise unless ret
|
|
8
|
+
raise unless system('rm -fr coverage/')
|
|
17
9
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: checkoff
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.21.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vince Broz
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-12-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: fakeweb
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
126
|
name: mdl
|
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -137,7 +151,7 @@ dependencies:
|
|
|
137
151
|
- !ruby/object:Gem::Version
|
|
138
152
|
version: '0'
|
|
139
153
|
- !ruby/object:Gem::Dependency
|
|
140
|
-
name:
|
|
154
|
+
name: minitest-reporters
|
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
|
142
156
|
requirements:
|
|
143
157
|
- - ">="
|
|
@@ -150,6 +164,20 @@ dependencies:
|
|
|
150
164
|
- - ">="
|
|
151
165
|
- !ruby/object:Gem::Version
|
|
152
166
|
version: '0'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: mocha
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - "~>"
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: 2.0.0.alpha.1
|
|
174
|
+
type: :development
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - "~>"
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: 2.0.0.alpha.1
|
|
153
181
|
- !ruby/object:Gem::Dependency
|
|
154
182
|
name: overcommit
|
|
155
183
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -193,19 +221,33 @@ dependencies:
|
|
|
193
221
|
- !ruby/object:Gem::Version
|
|
194
222
|
version: '13.0'
|
|
195
223
|
- !ruby/object:Gem::Dependency
|
|
196
|
-
name:
|
|
224
|
+
name: rspec
|
|
197
225
|
requirement: !ruby/object:Gem::Requirement
|
|
198
226
|
requirements:
|
|
199
227
|
- - ">="
|
|
200
228
|
- !ruby/object:Gem::Version
|
|
201
|
-
version: '
|
|
229
|
+
version: '3.4'
|
|
202
230
|
type: :development
|
|
203
231
|
prerelease: false
|
|
204
232
|
version_requirements: !ruby/object:Gem::Requirement
|
|
205
233
|
requirements:
|
|
206
234
|
- - ">="
|
|
207
235
|
- !ruby/object:Gem::Version
|
|
208
|
-
version: '
|
|
236
|
+
version: '3.4'
|
|
237
|
+
- !ruby/object:Gem::Dependency
|
|
238
|
+
name: rubocop
|
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
|
240
|
+
requirements:
|
|
241
|
+
- - "~>"
|
|
242
|
+
- !ruby/object:Gem::Version
|
|
243
|
+
version: '1.36'
|
|
244
|
+
type: :development
|
|
245
|
+
prerelease: false
|
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
247
|
+
requirements:
|
|
248
|
+
- - "~>"
|
|
249
|
+
- !ruby/object:Gem::Version
|
|
250
|
+
version: '1.36'
|
|
209
251
|
- !ruby/object:Gem::Dependency
|
|
210
252
|
name: rubocop-minitest
|
|
211
253
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -276,6 +318,20 @@ dependencies:
|
|
|
276
318
|
- - ">="
|
|
277
319
|
- !ruby/object:Gem::Version
|
|
278
320
|
version: '0'
|
|
321
|
+
- !ruby/object:Gem::Dependency
|
|
322
|
+
name: webmock
|
|
323
|
+
requirement: !ruby/object:Gem::Requirement
|
|
324
|
+
requirements:
|
|
325
|
+
- - ">="
|
|
326
|
+
- !ruby/object:Gem::Version
|
|
327
|
+
version: '0'
|
|
328
|
+
type: :development
|
|
329
|
+
prerelease: false
|
|
330
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
331
|
+
requirements:
|
|
332
|
+
- - ">="
|
|
333
|
+
- !ruby/object:Gem::Version
|
|
334
|
+
version: '0'
|
|
279
335
|
description:
|
|
280
336
|
email:
|
|
281
337
|
- vince@broz.cc
|
|
@@ -310,7 +366,6 @@ files:
|
|
|
310
366
|
- bin/rake
|
|
311
367
|
- bin/setup
|
|
312
368
|
- checkoff.gemspec
|
|
313
|
-
- coverage/.last_run.json
|
|
314
369
|
- docs/cookiecutter_input.json
|
|
315
370
|
- docs/example_project.png
|
|
316
371
|
- exe/checkoff
|
|
@@ -322,7 +377,11 @@ files:
|
|
|
322
377
|
- lib/checkoff/custom_fields.rb
|
|
323
378
|
- lib/checkoff/internal/config_loader.rb
|
|
324
379
|
- lib/checkoff/internal/create-class.sh
|
|
325
|
-
- lib/checkoff/internal/
|
|
380
|
+
- lib/checkoff/internal/search_url.rb
|
|
381
|
+
- lib/checkoff/internal/search_url/custom_field_param_converter.rb
|
|
382
|
+
- lib/checkoff/internal/search_url/custom_field_variant.rb
|
|
383
|
+
- lib/checkoff/internal/search_url/parser.rb
|
|
384
|
+
- lib/checkoff/internal/search_url/simple_param_converter.rb
|
|
326
385
|
- lib/checkoff/internal/task_selector_evaluator.rb
|
|
327
386
|
- lib/checkoff/my_tasks.rb
|
|
328
387
|
- lib/checkoff/projects.rb
|
|
@@ -366,7 +425,8 @@ files:
|
|
|
366
425
|
homepage: https://github.com/apiology/checkoff
|
|
367
426
|
licenses:
|
|
368
427
|
- MIT license
|
|
369
|
-
metadata:
|
|
428
|
+
metadata:
|
|
429
|
+
rubygems_mfa_required: 'true'
|
|
370
430
|
post_install_message:
|
|
371
431
|
rdoc_options: []
|
|
372
432
|
require_paths:
|
|
@@ -375,14 +435,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
375
435
|
requirements:
|
|
376
436
|
- - ">="
|
|
377
437
|
- !ruby/object:Gem::Version
|
|
378
|
-
version: '2.
|
|
438
|
+
version: '2.7'
|
|
379
439
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
380
440
|
requirements:
|
|
381
441
|
- - ">="
|
|
382
442
|
- !ruby/object:Gem::Version
|
|
383
443
|
version: '0'
|
|
384
444
|
requirements: []
|
|
385
|
-
rubygems_version: 3.
|
|
445
|
+
rubygems_version: 3.1.6
|
|
386
446
|
signing_key:
|
|
387
447
|
specification_version: 4
|
|
388
448
|
summary: Command-line and gem client for Asana (unofficial)
|
data/coverage/.last_run.json
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# frozen_string_literal: true
|
|
4
|
-
|
|
5
|
-
module Checkoff
|
|
6
|
-
module Internal
|
|
7
|
-
# base class for handling different custom_field_#{gid}.variant params
|
|
8
|
-
class CustomFieldVariant
|
|
9
|
-
def initialize(gid, remaining_params)
|
|
10
|
-
@gid = gid
|
|
11
|
-
@remaining_params = remaining_params
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
attr_reader :gid, :remaining_params
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# custom_field_#{gid}.variant = 'less_than'
|
|
20
|
-
class LessThanCustomFieldVariant < CustomFieldVariant
|
|
21
|
-
def convert
|
|
22
|
-
max_param = "custom_field_#{gid}.max"
|
|
23
|
-
case remaining_params.keys
|
|
24
|
-
when [max_param]
|
|
25
|
-
convert_single_custom_field_less_than_params_max_param(max_param)
|
|
26
|
-
else
|
|
27
|
-
raise "Teach me how to handle #{remaining_params}"
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def convert_single_custom_field_less_than_params_max_param(max_param)
|
|
34
|
-
max_values = remaining_params.fetch(max_param)
|
|
35
|
-
unless max_values.length == 1
|
|
36
|
-
raise "Teach me how to handle these remaining keys for #{max_param}: #{remaining_params}"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
max_value = max_values[0]
|
|
40
|
-
empty_task_selector = []
|
|
41
|
-
[{ "custom_fields.#{gid}.less_than" => max_value }, empty_task_selector]
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# custom_field_#{gid}.variant = 'is_not'
|
|
46
|
-
class IsNotCustomFieldVariant < CustomFieldVariant
|
|
47
|
-
def convert
|
|
48
|
-
case remaining_params.keys
|
|
49
|
-
when ["custom_field_#{gid}.selected_options"]
|
|
50
|
-
convert_single_custom_field_is_not_params_selected_options
|
|
51
|
-
else
|
|
52
|
-
raise "Teach me how to handle #{remaining_params}"
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
private
|
|
57
|
-
|
|
58
|
-
def convert_single_custom_field_is_not_params_selected_options
|
|
59
|
-
selected_options = remaining_params.fetch("custom_field_#{gid}.selected_options")
|
|
60
|
-
raise "Teach me how to handle #{remaining_params}" unless selected_options.length == 1
|
|
61
|
-
|
|
62
|
-
[{ "custom_fields.#{gid}.is_set" => 'true' },
|
|
63
|
-
['not',
|
|
64
|
-
['custom_field_gid_value_contains_any_gid',
|
|
65
|
-
gid,
|
|
66
|
-
selected_options.fetch(0).split('~')]]]
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# custom_field_#{gid}.variant = 'no_value'
|
|
71
|
-
class NoValueCustomFieldVariant < CustomFieldVariant
|
|
72
|
-
def convert
|
|
73
|
-
unless remaining_params.length.zero?
|
|
74
|
-
raise "Teach me how to handle these remaining keys for #{variant_key}: #{remaining_params}"
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
empty_task_selector = []
|
|
78
|
-
[{ "custom_fields.#{gid}.is_set" => 'false' }, empty_task_selector]
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# custom_field_#{gid}.variant = 'is'
|
|
83
|
-
class IsCustomFieldVariant < CustomFieldVariant
|
|
84
|
-
def convert
|
|
85
|
-
unless remaining_params.length == 1
|
|
86
|
-
raise "Teach me how to handle these remaining keys for #{variant_key}: #{remaining_params}"
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
key, values = remaining_params.to_a[0]
|
|
90
|
-
convert_custom_field_is_arg(key, values)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
private
|
|
94
|
-
|
|
95
|
-
def convert_custom_field_is_arg(key, values)
|
|
96
|
-
empty_task_selector = []
|
|
97
|
-
|
|
98
|
-
if key.end_with? '.selected_options'
|
|
99
|
-
raise "Too many values found for #{key}: #{values}" if values.length != 1
|
|
100
|
-
|
|
101
|
-
return [{ "custom_fields.#{gid}.value" => values[0] },
|
|
102
|
-
empty_task_selector]
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
raise "Teach me how to handle #{key} = #{values}"
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Convert custom field parameters from an Asana search URL into
|
|
110
|
-
# API search arguments and Checkoff task selectors
|
|
111
|
-
class CustomFieldParamConverter
|
|
112
|
-
def initialize(custom_field_params:)
|
|
113
|
-
@custom_field_params = custom_field_params
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def convert
|
|
117
|
-
args = {}
|
|
118
|
-
task_selector = []
|
|
119
|
-
by_custom_field.each do |gid, single_custom_field_params|
|
|
120
|
-
new_args, new_task_selector = convert_single_custom_field_params(gid,
|
|
121
|
-
single_custom_field_params)
|
|
122
|
-
args, task_selector = merge_args_and_task_selectors(args, new_args,
|
|
123
|
-
task_selector, new_task_selector)
|
|
124
|
-
end
|
|
125
|
-
[args, task_selector]
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
private
|
|
129
|
-
|
|
130
|
-
def by_custom_field
|
|
131
|
-
custom_field_params.group_by do |key, _value|
|
|
132
|
-
gid_from_custom_field_key(key)
|
|
133
|
-
end.transform_values(&:to_h)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def merge_args_and_task_selectors(args, new_args, task_selector, new_task_selector)
|
|
137
|
-
args = args.merge(new_args)
|
|
138
|
-
return [args, task_selector] if new_task_selector == []
|
|
139
|
-
|
|
140
|
-
raise 'Teach me how to merge task selectors' unless task_selector == []
|
|
141
|
-
|
|
142
|
-
task_selector = new_task_selector
|
|
143
|
-
|
|
144
|
-
[args, task_selector]
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
VARIANTS = {
|
|
148
|
-
'is' => IsCustomFieldVariant,
|
|
149
|
-
'no_value' => NoValueCustomFieldVariant,
|
|
150
|
-
'is_not' => IsNotCustomFieldVariant,
|
|
151
|
-
'less_than' => LessThanCustomFieldVariant,
|
|
152
|
-
}.freeze
|
|
153
|
-
|
|
154
|
-
def convert_single_custom_field_params(gid, single_custom_field_params)
|
|
155
|
-
variant_key = "custom_field_#{gid}.variant"
|
|
156
|
-
variant = single_custom_field_params.fetch(variant_key)
|
|
157
|
-
remaining_params = single_custom_field_params.reject { |k, _v| k == variant_key }
|
|
158
|
-
raise "Teach me how to handle #{variant_key} = #{variant}" unless variant.length == 1
|
|
159
|
-
|
|
160
|
-
variant_class = VARIANTS[variant[0]]
|
|
161
|
-
return variant_class.new(gid, remaining_params).convert unless variant_class.nil?
|
|
162
|
-
|
|
163
|
-
raise "Teach me how to handle #{variant_key} = #{variant}"
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def gid_from_custom_field_key(key)
|
|
167
|
-
key.split('_')[2].split('.')[0]
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
attr_reader :custom_field_params
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# Parse Asana search URLs into parameters suitable to pass into
|
|
174
|
-
# the /workspaces/{workspace_gid}/tasks/search endpoint
|
|
175
|
-
class SearchUrlParser
|
|
176
|
-
def initialize(_deps = {}); end
|
|
177
|
-
|
|
178
|
-
def convert_params(url)
|
|
179
|
-
url_params = CGI.parse(URI.parse(url).query)
|
|
180
|
-
custom_field_params, regular_url_params = partition_url_params(url_params)
|
|
181
|
-
custom_field_args, task_selector = convert_custom_field_params(custom_field_params)
|
|
182
|
-
regular_url_args = convert_regular_params(regular_url_params)
|
|
183
|
-
[custom_field_args.merge(regular_url_args), task_selector]
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
private
|
|
187
|
-
|
|
188
|
-
def convert_regular_params(regular_url_params)
|
|
189
|
-
regular_url_params.to_a.map do |key, values|
|
|
190
|
-
convert_arg(key, values)
|
|
191
|
-
end.to_h
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def convert_custom_field_params(custom_field_params)
|
|
195
|
-
CustomFieldParamConverter.new(custom_field_params: custom_field_params).convert
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def partition_url_params(url_params)
|
|
199
|
-
url_params.to_a.partition do |key, _values|
|
|
200
|
-
key.start_with? 'custom_field_'
|
|
201
|
-
end.map(&:to_h)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# https://developers.asana.com/docs/search-tasks-in-a-workspace
|
|
205
|
-
def convert_arg(key, values)
|
|
206
|
-
case key
|
|
207
|
-
when 'any_projects.ids'
|
|
208
|
-
['projects.any', values.join(',')]
|
|
209
|
-
when 'completion'
|
|
210
|
-
raise "Teach me how to handle #{key} = #{values}" if values.length != 1
|
|
211
|
-
|
|
212
|
-
value = values.fetch(0)
|
|
213
|
-
raise "Teach me how to handle #{key} = #{values}" if value != 'incomplete'
|
|
214
|
-
|
|
215
|
-
['completed', false]
|
|
216
|
-
when 'not_tags.ids'
|
|
217
|
-
raise "Teach me how to handle #{key} = #{values}" if values.length != 1
|
|
218
|
-
|
|
219
|
-
value = values.fetch(0)
|
|
220
|
-
tag_ids = value.split('~')
|
|
221
|
-
['tags.not', tag_ids.join(',')]
|
|
222
|
-
else
|
|
223
|
-
raise "Teach me how to handle #{key} = #{values}"
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
end
|