checkoff 0.133.0 → 0.135.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7939d56faf627c0cf1df999033e52d4af51ba20415ead6d0ad7851e9068db682
4
- data.tar.gz: 6b48464946d3ec56a5d5da6dfcafa8aef58ad971e2a75288ada658a102a95e47
3
+ metadata.gz: a1405ced59ee8be44fb64638fb70395489ac438812bb1307a58dc46f9bf2b0a3
4
+ data.tar.gz: 2a3eb1041ff8894f18ea135774542c0efc09ed0f3ec1771130059aacd93b7733
5
5
  SHA512:
6
- metadata.gz: 48eec1f70323b78b165435b238ce25b0faaac5fcbc76fd0de68b0f8e30c34ce5fb1cb690093685be3ac0c39abb1df9c3c141a3a437c972e45dc11738b4e9e7a6
7
- data.tar.gz: 6eba3315a48d7608a1cdd0a2626685eb9f29dc7bb1b02464e76224769deb876ff123dfb88203fef8c75f2dc216e481f0bc79559a35ebb35b3c2ae0523fd96567
6
+ metadata.gz: 376987e755790ea699766a7d419ff07353122a256d6d398c4cfbc015ef29418f396e3b3b5761de595e28f0c9bf29baf74afe87ede5c885e6bea5b1d742ebc3bc
7
+ data.tar.gz: 70c8298da537b7c9bdc9562eebdc03a5cffc04762917b9fcef577bef05c9dfc4bdc2a2bfba72c295b3dc7f343be08e1d4febbbcb3cc0817470118b0bca8adb0a
data/Gemfile CHANGED
@@ -11,7 +11,7 @@ gem 'fakeweb'
11
11
  gem 'mdl'
12
12
  gem 'minitest-profile'
13
13
  gem 'minitest-reporters'
14
- gem 'mocha', ['~> 2.0.0.alpha.1']
14
+ gem 'mocha', ['>= 2']
15
15
  # 0.58.0 and 0.57.0 don't seem super compatible with signatures, and
16
16
  # magit doesn't seem to want to use the bundled version at the moment,
17
17
  # so let's favor the more recent version...
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.133.0)
15
+ checkoff (0.135.0)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
@@ -22,7 +22,7 @@ PATH
22
22
  GEM
23
23
  remote: https://rubygems.org/
24
24
  specs:
25
- activesupport (7.1.1)
25
+ activesupport (7.1.2)
26
26
  base64
27
27
  bigdecimal
28
28
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -104,7 +104,7 @@ GEM
104
104
  mixlib-shellout
105
105
  method_source (1.0.0)
106
106
  mini_portile2 (2.8.2)
107
- minitest (5.18.0)
107
+ minitest (5.20.0)
108
108
  minitest-profile (0.0.2)
109
109
  minitest-reporters (1.5.0)
110
110
  ansi
@@ -116,7 +116,8 @@ GEM
116
116
  tomlrb
117
117
  mixlib-shellout (3.2.7)
118
118
  chef-utils
119
- mocha (2.0.0.alpha.1)
119
+ mocha (2.1.0)
120
+ ruby2_keywords (>= 0.0.5)
120
121
  multi_json (1.15.0)
121
122
  multi_xml (0.6.0)
122
123
  multipart-post (2.1.1)
@@ -232,7 +233,7 @@ DEPENDENCIES
232
233
  mdl
233
234
  minitest-profile
234
235
  minitest-reporters
235
- mocha (~> 2.0.0.alpha.1)
236
+ mocha (>= 2)
236
237
  overcommit (>= 0.60.0, < 0.61.0)
237
238
  pry
238
239
  punchlist
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'forwardable'
6
+ require 'cache_method'
7
+ require_relative 'internal/config_loader'
8
+ require_relative 'internal/asana_event_filter'
9
+ require_relative 'workspaces'
10
+ require_relative 'clients'
11
+
12
+ # https://developers.asana.com/reference/events
13
+
14
+ module Checkoff
15
+ # Methods related to the Asana events / webhooks APIs
16
+ class Events
17
+ # @!parse
18
+ # extend CacheMethod::ClassMethods
19
+
20
+ MINUTE = 60
21
+ private_constant :MINUTE
22
+ HOUR = MINUTE * 60
23
+ private_constant :HOUR
24
+ DAY = 24 * HOUR
25
+ private_constant :DAY
26
+ REALLY_LONG_CACHE_TIME = HOUR * 1
27
+ private_constant :REALLY_LONG_CACHE_TIME
28
+ LONG_CACHE_TIME = MINUTE * 15
29
+ private_constant :LONG_CACHE_TIME
30
+ SHORT_CACHE_TIME = MINUTE
31
+ private_constant :SHORT_CACHE_TIME
32
+
33
+ # @param config [Hash]
34
+ # @param workspaces [Checkoff::Workspaces]
35
+ # @param clients [Checkoff::Clients]
36
+ # @param client [Asana::Client]
37
+ # @param asana_event_filter_class [Class<Checkoff::Internal::AsanaEventFilter>]
38
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
39
+ workspaces: Checkoff::Workspaces.new(config: config),
40
+ clients: Checkoff::Clients.new(config: config),
41
+ client: clients.client,
42
+ asana_event_filter_class: Checkoff::Internal::AsanaEventFilter)
43
+ @workspaces = workspaces
44
+ @client = client
45
+ @asana_event_filter_class = asana_event_filter_class
46
+ end
47
+
48
+ # @param filters [Array<Hash>, nil] The filters to match against
49
+ # @param asana_events [Array<Hash>] The events that Asana sent
50
+ #
51
+ # @return [Array<Hash>] The events that should be acted on
52
+ def filter_asana_events(filters, asana_events)
53
+ asana_event_filter = @asana_event_filter_class.new(filters: filters)
54
+ asana_events.select { |event| asana_event_filter.matches?(event) }
55
+ end
56
+
57
+ private
58
+
59
+ # @return [Checkoff::Workspaces]
60
+ attr_reader :workspaces
61
+
62
+ # @return [Asana::Client]
63
+ attr_reader :client
64
+
65
+ # bundle exec ./events.rb
66
+ # :nocov:
67
+ class << self
68
+ # @return [void]
69
+ def run
70
+ # @sg-ignore
71
+ # @type [String]
72
+ # workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
73
+ # @sg-ignore
74
+ # @type [String]
75
+ # event_name = ARGV[1] || raise('Please pass event name as second argument')
76
+ # events = Checkoff::Events.new
77
+ # event = events.event_or_raise(workspace_name, event_name)
78
+ # puts "Results: #{event}"
79
+ end
80
+ end
81
+ # :nocov:
82
+ end
83
+ end
84
+
85
+ # :nocov:
86
+ abs_program_name = File.expand_path($PROGRAM_NAME)
87
+ Checkoff::Events.run if abs_program_name == File.expand_path(__FILE__)
88
+ # :nocov:
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'logging'
4
+
5
+ module Checkoff
6
+ module Internal
7
+ # Uses an enhanced version of Asana event filter configuration
8
+ #
9
+ # See https://developers.asana.com/reference/createwebhook | body
10
+ # params | data | filters for a general description of the scheme.
11
+ #
12
+ # Additional supported filter keys:
13
+ #
14
+ # * 'checkoff:parent.gid' - requires that the 'gid' key in the 'parent' object
15
+ # match the given value
16
+ class AsanaEventFilter
17
+ include Logging
18
+
19
+ # @param filters [Array<Hash>, nil] The filters to match against
20
+ def initialize(filters:)
21
+ @filters = filters
22
+ end
23
+
24
+ # @param asana_event [Hash] The event that Asana sent
25
+ def matches?(asana_event)
26
+ logger.debug { "Filtering using #{@filters.inspect}" }
27
+ return true if @filters.nil?
28
+
29
+ @filters.any? do |filter|
30
+ out = filter_matches_asana_event?(filter, asana_event)
31
+ logger.debug { "Filter #{filter.inspect} matched? #{out} against event #{asana_event.inspect}" }
32
+ out
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # @param filter [Hash]
39
+ # @param asana_event [Hash]
40
+ #
41
+ # @sg-ignore
42
+ # @return [Boolean]
43
+ def filter_matches_asana_event?(filter, asana_event)
44
+ # @param key [String]
45
+ # @param value [String, Array<String>]
46
+ filter.all? do |key, value|
47
+ asana_event_matches_filter_item?(key, value, asana_event)
48
+ end
49
+ end
50
+
51
+ # @param key [String]
52
+ # @param value [String, Array<String>]
53
+ # @param asana_event [Hash]
54
+ #
55
+ # @sg-ignore
56
+ # @return [Boolean]
57
+ def asana_event_matches_filter_item?(key, value, asana_event)
58
+ case key
59
+ when 'resource_type'
60
+ asana_event.fetch('resource', {})['resource_type'] == value
61
+ when 'resource_subtype'
62
+ asana_event.fetch('resource', {})['resource_subtype'] == value
63
+ when 'action'
64
+ asana_event['action'] == value
65
+ when 'fields'
66
+ value.include? asana_event.fetch('change', {})['field']
67
+ when 'checkoff:parent.gid'
68
+ asana_event.fetch('parent', {})['gid'] == value
69
+ else
70
+ raise "Unknown filter key #{key}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ # include this to add ability to log at different levels
6
+ module Logging
7
+ # @return [::Logger]
8
+ def logger
9
+ # @sg-ignore
10
+ @logger ||= if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
11
+ # @sg-ignore
12
+ Rails.logger
13
+ else
14
+ ::Logger.new($stdout, level: log_level)
15
+ end
16
+ end
17
+
18
+ # @param message [String,nil]
19
+ #
20
+ # @return [void]
21
+ def error(message = nil, &block)
22
+ logger.error(message, &block)
23
+ end
24
+
25
+ # @param message [String,nil]
26
+ #
27
+ # @return [void]
28
+ def warn(message = nil, &block)
29
+ logger.warn(message, &block)
30
+ end
31
+
32
+ # @param message [String,nil]
33
+ #
34
+ # @return [void]
35
+ def info(message = nil, &block)
36
+ logger.info(message, &block)
37
+ end
38
+
39
+ # @param message [String,nil]
40
+ #
41
+ # @return [void]
42
+ def debug(message = nil, &block)
43
+ logger.debug(message, &block)
44
+ end
45
+
46
+ # @param message [String,nil]
47
+ #
48
+ # @return [void]
49
+ def finer(message = nil, &block)
50
+ # No such level by default
51
+ #
52
+ # logger.finer(message, &block)
53
+ end
54
+
55
+ private
56
+
57
+ # @sg-ignore
58
+ # @return [Symbol]
59
+ def log_level
60
+ # @sg-ignore
61
+ ENV.fetch('LOG_LEVEL', 'INFO').downcase.to_sym
62
+ end
63
+ end
@@ -127,11 +127,16 @@ module Checkoff
127
127
  #
128
128
  # @param task_gid [String]
129
129
  # @param extra_fields [Array<String>]
130
+ # @param only_uncompleted [Boolean]
131
+ #
130
132
  # @return [Asana::Resources::Task, nil]
131
133
  def task_by_gid(task_gid,
132
- extra_fields: [])
134
+ extra_fields: [],
135
+ only_uncompleted: false)
136
+ # @type [Hash]
133
137
  options = projects.task_options.fetch(:options, {})
134
138
  options[:fields] += extra_fields
139
+ options[:completed_since] = '9999-12-01' if only_uncompleted
135
140
  client.tasks.find_by_id(task_gid, options: options)
136
141
  end
137
142
  cache_method :task_by_gid, SHORT_CACHE_TIME
@@ -182,9 +187,10 @@ module Checkoff
182
187
  # the completion status of dependencies, so we need to do this
183
188
  # regardless:
184
189
  parent_task_gid = parent_task_info.fetch('gid')
185
- parent_task = @asana_task.find_by_id(client, parent_task_gid,
186
- options: { fields: ['completed'] })
187
- !parent_task.completed
190
+
191
+ parent_task = task_by_gid(parent_task_gid,
192
+ only_uncompleted: false)
193
+ parent_task.completed_at.nil?
188
194
  end
189
195
  end
190
196
 
@@ -3,5 +3,5 @@
3
3
  # Command-line and gem client for Asana (unofficial)
4
4
  module Checkoff
5
5
  # Version of library
6
- VERSION = '0.133.0'
6
+ VERSION = '0.135.0'
7
7
  end
data/lib/checkoff.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'checkoff/version'
4
4
  require 'checkoff/clients'
5
+ require 'checkoff/events'
5
6
  require 'checkoff/workspaces'
6
7
  require 'checkoff/portfolios'
7
8
  require 'checkoff/projects'
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.133.0
4
+ version: 0.135.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: 2023-11-09 00:00:00.000000000 Z
11
+ date: 2023-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -131,8 +131,11 @@ files:
131
131
  - lib/checkoff/clients.rb
132
132
  - lib/checkoff/create-entity.sh
133
133
  - lib/checkoff/custom_fields.rb
134
+ - lib/checkoff/events.rb
135
+ - lib/checkoff/internal/asana_event_filter.rb
134
136
  - lib/checkoff/internal/config_loader.rb
135
137
  - lib/checkoff/internal/create-class.sh
138
+ - lib/checkoff/internal/logging.rb
136
139
  - lib/checkoff/internal/project_hashes.rb
137
140
  - lib/checkoff/internal/project_selector_evaluator.rb
138
141
  - lib/checkoff/internal/project_timing.rb