checkoff 0.185.0 → 0.186.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: 2c144884f931d0a839d66654dbb1ce02487d6024bbd32ca3a56ed749dfb650fa
4
- data.tar.gz: fbce0b5063ca8dbec172cc2f3d062a266daae30af1d70d090586cb8eee485b03
3
+ metadata.gz: ab93a442000f1d706e304c5d574a9266c19dcff4be222107dc943f6c1d3ecd3f
4
+ data.tar.gz: 373ce95b10e181827c2423bfce1a0a37f43a12b32ebebd79ea5b8e98dcc0787f
5
5
  SHA512:
6
- metadata.gz: c77b6170bf7eaa08e532eb38013ff25ae581d7363543128516a98d1cf354bc5137e6caa24e8f99d5d2ec52747ed300fc3e9ab279a3a209e89eee5a64b5900b10
7
- data.tar.gz: 928dba035f252897d2a400b1b55ba05bf98dc91f676818395a6d070f14ed0f179a8e09421f4580f9c7c3bff15d1570eee7b7561d0a4d01b0d3dfcd68d0f6ab83
6
+ metadata.gz: b4f7267e3254bb4d22593bca64557ec58641b69ff95ceb7890c34a5c87512b27a5dda4fecf7db78ee5088692dbcc8c5380f673459a9df3c1edd744746215d364
7
+ data.tar.gz: '0680cf901961be8001249375c81ceaa77ed903e0acb2f06ecc3b62093a110d7d4bcc225e706d46b31838a50d08d0f8811f4e636166715f5d48b73e33a940f521'
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.185.0)
15
+ checkoff (0.186.0)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
@@ -50,6 +50,8 @@
50
50
  # def due_at; end
51
51
  # # @return [String,nil]
52
52
  # def due_on; end
53
+ # # @return [String,nil]
54
+ # def name; end
53
55
  # # @return [Hash<String, String>, nil]
54
56
  # def assignee; end
55
57
  # # @return [String, nil]
@@ -61,8 +63,14 @@
61
63
  # def create(client, assignee:, workspace:, name:); end
62
64
  # end
63
65
  # end
66
+ # class Section
67
+ # # @return [String,nil]
68
+ # def name; end
69
+ # end
64
70
  # class Project
65
71
  # # @return [String,nil]
72
+ # def name; end
73
+ # # @return [String,nil]
66
74
  # def due_date; end
67
75
  # end
68
76
  # class Portfolio
@@ -70,6 +78,9 @@
70
78
  # def get_items(options = {}); end
71
79
  # end
72
80
  # end
81
+ # module Errors
82
+ # class NotFound < StandardError; end
83
+ # end
73
84
  # module Resources
74
85
  # class Workspace
75
86
  # # @return [String, nil]
@@ -7,6 +7,7 @@ require 'cache_method'
7
7
  require_relative 'internal/config_loader'
8
8
  require_relative 'internal/logging'
9
9
  require_relative 'internal/asana_event_filter'
10
+ require_relative 'internal/asana_event_enrichment'
10
11
  require_relative 'workspaces'
11
12
  require_relative 'clients'
12
13
 
@@ -34,19 +35,28 @@ module Checkoff
34
35
  # @param config [Hash]
35
36
  # @param workspaces [Checkoff::Workspaces]
36
37
  # @param tasks [Checkoff::Tasks]
38
+ # @param sections [Checkoff::Sections]
39
+ # @param projects [Checkoff::Projects]
37
40
  # @param clients [Checkoff::Clients]
38
41
  # @param client [Asana::Client]
39
42
  # @param asana_event_filter_class [Class<Checkoff::Internal::AsanaEventFilter>]
43
+ # @param asana_event_enrichment [Checkoff::Internal::AsanaEventEnrichment]
40
44
  def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
41
45
  workspaces: Checkoff::Workspaces.new(config: config),
42
46
  tasks: Checkoff::Tasks.new(config: config),
47
+ sections: Checkoff::Sections.new(config: config),
48
+ projects: Checkoff::Projects.new(config: config),
43
49
  clients: Checkoff::Clients.new(config: config),
44
50
  client: clients.client,
45
- asana_event_filter_class: Checkoff::Internal::AsanaEventFilter)
51
+ asana_event_filter_class: Checkoff::Internal::AsanaEventFilter,
52
+ asana_event_enrichment: Checkoff::Internal::AsanaEventEnrichment.new)
46
53
  @workspaces = workspaces
47
54
  @tasks = tasks
55
+ @sections = sections
56
+ @projects = projects
48
57
  @client = client
49
58
  @asana_event_filter_class = asana_event_filter_class
59
+ @asana_event_enrichment = asana_event_enrichment
50
60
  end
51
61
 
52
62
  # @param filters [Array<Hash>, nil] The filters to match against
@@ -65,34 +75,35 @@ module Checkoff
65
75
  #
66
76
  # @return [Hash]
67
77
  def enrich_event(asana_event)
68
- finer { "Enriching event: #{asana_event}" }
69
- asana_event = asana_event.dup
70
- enrich_event_resource!(asana_event)
71
- asana_event
78
+ asana_event_enrichment.enrich_event(asana_event)
72
79
  end
73
80
 
74
- private
81
+ # @param filter [Hash<String,[String,Array<String>]>]
82
+ #
83
+ # @return [Hash<String,[String,Array<String>]>]
84
+ def enrich_filter(filter)
85
+ asana_event_enrichment.enrich_filter(filter)
86
+ end
75
87
 
76
- # @param asana_event [Hash{'resource' => Hash}]
88
+ # @param webhook_subscription [Hash] Hash of the request made to
89
+ # webhook POST endpoint - https://app.asana.com/api/1.0/webhooks
90
+ # https://developers.asana.com/reference/createwebhook
77
91
  #
78
92
  # @return [void]
79
- def enrich_event_resource!(asana_event)
80
- # @type [Hash{String => String }]
81
- resource = asana_event['resource']
82
- # @type [String]
83
- resource_type = resource.fetch('resource_type')
84
- # @type [String]
85
- gid = resource.fetch('gid')
86
-
87
- if resource_type == 'task'
88
- task = tasks.task_by_gid(gid, only_uncompleted: false)
89
- resource['checkoff:name'] = task.name if task
90
- end
91
- nil
93
+ def enrich_webhook_subscription!(webhook_subscription)
94
+ asana_event_enrichment.enrich_webhook_subscription!(webhook_subscription)
92
95
  end
93
96
 
97
+ private
98
+
94
99
  include Logging
95
100
 
101
+ # @return [Checkoff::Projects]
102
+ attr_reader :projects
103
+
104
+ # @return [Checkoff::Sections]
105
+ attr_reader :sections
106
+
96
107
  # @return [Checkoff::Tasks]
97
108
  attr_reader :tasks
98
109
 
@@ -102,6 +113,9 @@ module Checkoff
102
113
  # @return [Asana::Client]
103
114
  attr_reader :client
104
115
 
116
+ # @return [Checkoff::Internal::AsanaEventEnrichment]
117
+ attr_reader :asana_event_enrichment
118
+
105
119
  # bundle exec ./events.rb
106
120
  # :nocov:
107
121
  class << self
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'cache_method'
5
+ require 'checkoff/internal/config_loader'
6
+ require 'checkoff/internal/logging'
7
+ require 'checkoff/internal/asana_event_enrichment'
8
+ require 'checkoff/workspaces'
9
+ require 'checkoff/clients'
10
+
11
+ module Checkoff
12
+ module Internal
13
+ # Add useful info (like resource task names) into an Asana
14
+ # event/event filters/webhook subscription for human consumption
15
+ class AsanaEventEnrichment
16
+ # @param config [Hash]
17
+ # @param workspaces [Checkoff::Workspaces]
18
+ # @param tasks [Checkoff::Tasks]
19
+ # @param sections [Checkoff::Sections]
20
+ # @param projects [Checkoff::Projects]
21
+ # @param clients [Checkoff::Clients]
22
+ # @param client [Asana::Client]
23
+ # @param asana_event_enrichment [Checkoff::Internal::AsanaEventEnrichment]
24
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
25
+ workspaces: Checkoff::Workspaces.new(config: config),
26
+ tasks: Checkoff::Tasks.new(config: config),
27
+ sections: Checkoff::Sections.new(config: config),
28
+ projects: Checkoff::Projects.new(config: config),
29
+ clients: Checkoff::Clients.new(config: config),
30
+ client: clients.client)
31
+ @workspaces = workspaces
32
+ @tasks = tasks
33
+ @sections = sections
34
+ @projects = projects
35
+ @client = client
36
+ end
37
+
38
+ # Add useful info (like resource task names) into an event for
39
+ # human consumption
40
+ #
41
+ # @param asana_event [Hash]
42
+ #
43
+ # @return [Hash]
44
+ def enrich_event(asana_event)
45
+ finer { "Enriching event: #{asana_event}" }
46
+ asana_event = asana_event.dup
47
+ enrich_event_resource!(asana_event)
48
+ enrich_event_parent!(asana_event)
49
+ asana_event
50
+ end
51
+
52
+ # @param filter [Hash<String,[String,Array<String>]>]
53
+ #
54
+ # @return [Hash<String,[String,Array<String>]>]
55
+ def enrich_filter(filter)
56
+ filter = filter.dup
57
+ enrich_filter_section!(filter)
58
+ enrich_filter_resource!(filter)
59
+ enrich_filter_parent_gid!(filter)
60
+ filter
61
+ end
62
+
63
+ # @param webhook_subscription [Hash] Hash of the request made to
64
+ # webhook POST endpoint - https://app.asana.com/api/1.0/webhooks
65
+ # https://developers.asana.com/reference/createwebhook
66
+ #
67
+ # @return [void]
68
+ def enrich_webhook_subscription!(webhook_subscription)
69
+ webhook_subscription&.fetch('filters', nil)&.map! do |filter|
70
+ enrich_filter(filter)
71
+ end
72
+ resource = webhook_subscription&.fetch('resource', nil)
73
+ # @sg-ignore
74
+ name, resource_type = enrich_gid(resource) if resource
75
+ webhook_subscription['checkoff:enriched:name'] = name if name
76
+ webhook_subscription['checkoff:enriched:resource_type'] = resource_type if resource_type
77
+ end
78
+
79
+ private
80
+
81
+ # Attempt to look up a GID in situations where we don't have a
82
+ # resource type provided, and returns the name of the resource.
83
+ #
84
+ # @param gid [String]
85
+ # @param resource_type [String,nil]
86
+ #
87
+ # @return [Array<([String, nil], [String,nil])>]
88
+ def enrich_gid(gid, resource_type: nil)
89
+ %w[task section project].each do |resource_type_to_try|
90
+ next unless [resource_type_to_try, nil].include?(resource_type)
91
+
92
+ name = method(:"enrich_#{resource_type_to_try}_gid").call(gid)
93
+ return [name, resource_type_to_try] if name
94
+ end
95
+ [nil, nil]
96
+ end
97
+
98
+ # @param filter [Hash{String => String}]
99
+ #
100
+ # @return [String, nil]
101
+ def enrich_filter_parent_gid!(filter)
102
+ parent_gid = filter['checkoff:parent.gid']
103
+ return unless parent_gid
104
+
105
+ # @sg-ignore
106
+ name, resource_type = enrich_gid(parent_gid)
107
+ filter['checkoff:enriched:parent.name'] = name if name
108
+ filter['checkoff:enriched:parent.resource_type'] = resource_type if resource_type
109
+ end
110
+
111
+ # @param gid [String]
112
+ #
113
+ # @return [String, nil]
114
+ def enrich_task_gid(gid)
115
+ task = tasks.task_by_gid(gid, only_uncompleted: false)
116
+ task&.name
117
+ end
118
+
119
+ # @param section_gid [String]
120
+ #
121
+ # @return [String, nil]
122
+ def enrich_section_gid(section_gid)
123
+ section = sections.section_by_gid(section_gid)
124
+ section&.name
125
+ end
126
+
127
+ # @param project_gid [String]
128
+ #
129
+ # @return [String, nil]
130
+ def enrich_project_gid(project_gid)
131
+ project = projects.project_by_gid(project_gid)
132
+ project&.name
133
+ end
134
+
135
+ # @param filter [Hash{String => String}]
136
+ #
137
+ # @return [void]
138
+ def enrich_filter_resource!(filter)
139
+ resource_gid = filter['checkoff:resource.gid']
140
+
141
+ return unless resource_gid
142
+
143
+ task = tasks.task_by_gid(resource_gid)
144
+ filter['checkoff:enriched:resource.name'] = task.name if task
145
+ end
146
+
147
+ # @param filter [Hash{String => [String,Array<String>]}]
148
+ #
149
+ # @return [void]
150
+ def enrich_filter_section!(filter)
151
+ section_gid = filter['checkoff:fetched.section.gid']
152
+ return unless section_gid
153
+
154
+ name = enrich_section_gid(section_gid)
155
+ filter['checkoff:enriched:fetched.section.name'] = name if name
156
+ end
157
+
158
+ # @param asana_event [Hash{'resource' => Hash}]
159
+ #
160
+ # @return [void]
161
+ def enrich_event_parent!(asana_event)
162
+ # @type [Hash{String => String }]
163
+ parent = asana_event['parent']
164
+
165
+ return unless parent
166
+
167
+ # @type [String]
168
+ resource_type = parent.fetch('resource_type')
169
+ # @type [String]
170
+ gid = parent.fetch('gid')
171
+ # @sg-ignore
172
+ name, _resource_type = enrich_gid(gid, resource_type: resource_type)
173
+ parent['checkoff:enriched:name'] = name if name
174
+
175
+ nil
176
+ end
177
+
178
+ # @param asana_event [Hash{'resource' => Hash}]
179
+ #
180
+ # @return [void]
181
+ def enrich_event_resource!(asana_event)
182
+ # @type [Hash{String => String }]
183
+ resource = asana_event['resource']
184
+ # @type [String]
185
+ resource_type = resource.fetch('resource_type')
186
+
187
+ # @type [String]
188
+ gid = resource.fetch('gid')
189
+
190
+ # @sg-ignore
191
+ name, _resource_type = enrich_gid(gid, resource_type: resource_type)
192
+ resource['checkoff:enriched:name'] = name if name
193
+
194
+ nil
195
+ end
196
+
197
+ include Logging
198
+
199
+ # @return [Checkoff::Projects]
200
+ attr_reader :projects
201
+
202
+ # @return [Checkoff::Sections]
203
+ attr_reader :sections
204
+
205
+ # @return [Checkoff::Tasks]
206
+ attr_reader :tasks
207
+
208
+ # @return [Checkoff::Workspaces]
209
+ attr_reader :workspaces
210
+
211
+ # @return [Asana::Client]
212
+ attr_reader :client
213
+ end
214
+ end
215
+ end
@@ -3,6 +3,7 @@
3
3
  require_relative 'internal/config_loader'
4
4
  require_relative 'internal/project_hashes'
5
5
  require_relative 'internal/project_timing'
6
+ require_relative 'internal/logging'
6
7
  require_relative 'workspaces'
7
8
  require_relative 'clients'
8
9
  require_relative 'timing'
@@ -99,9 +100,12 @@ module Checkoff
99
100
  # @param gid [String]
100
101
  # @param [Array<String>] extra_fields
101
102
  #
102
- # @return [Asana::Resources::Project]
103
+ # @return [Asana::Resources::Project,nil]
103
104
  def project_by_gid(gid, extra_fields: [])
104
105
  projects.find_by_id(gid, options: { fields: %w[name] + extra_fields })
106
+ rescue Asana::Errors::NotFound => e
107
+ debug e
108
+ nil
105
109
  end
106
110
  cache_method :project_by_gid, REALLY_LONG_CACHE_TIME
107
111
 
@@ -192,8 +196,15 @@ module Checkoff
192
196
  timing.in_period?(project_date, period)
193
197
  end
194
198
 
199
+ # @return [Hash]
200
+ def as_cache_key
201
+ {}
202
+ end
203
+
195
204
  private
196
205
 
206
+ include Logging
207
+
197
208
  # @return [Checkoff::Timing]
198
209
  attr_reader :timing
199
210
 
@@ -5,6 +5,7 @@ require_relative 'projects'
5
5
  require_relative 'workspaces'
6
6
  require_relative 'clients'
7
7
  require_relative 'my_tasks'
8
+ require_relative 'internal/logging'
8
9
 
9
10
  module Checkoff
10
11
  # Query different sections of Asana projects
@@ -197,15 +198,26 @@ module Checkoff
197
198
 
198
199
  # @param gid [String]
199
200
  #
200
- # @return [Asana::Resources::Section]
201
+ # @return [Asana::Resources::Section, nil]
201
202
  def section_by_gid(gid)
202
203
  options = {}
203
204
  Asana::Resources::Section.new(parse(client.get("/sections/#{gid}", options: options)).first,
204
205
  client: client)
206
+ rescue Asana::Errors::NotFound => e
207
+ debug e
208
+ nil
209
+ end
210
+ cache_method :section_by_gid, SHORT_CACHE_TIME
211
+
212
+ # @return [Hash]
213
+ def as_cache_key
214
+ {}
205
215
  end
206
216
 
207
217
  private
208
218
 
219
+ include Logging
220
+
209
221
  # https://github.com/Asana/ruby-asana/blob/master/lib/asana/resource_includes/response_helper.rb#L7
210
222
  # @param response [Faraday::Response]
211
223
  #
@@ -161,6 +161,9 @@ module Checkoff
161
161
  options[:fields] += extra_fields
162
162
  options[:completed_since] = '9999-12-01' if only_uncompleted
163
163
  client.tasks.find_by_id(task_gid, options: options)
164
+ rescue Asana::Errors::NotFound => e
165
+ debug e
166
+ nil
164
167
  end
165
168
  cache_method :task_by_gid, SHORT_CACHE_TIME
166
169
 
@@ -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.185.0'
6
+ VERSION = '0.186.0'
7
7
  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.185.0
4
+ version: 0.186.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: 2024-01-26 00:00:00.000000000 Z
11
+ date: 2024-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -149,6 +149,7 @@ files:
149
149
  - lib/checkoff/create-entity.sh
150
150
  - lib/checkoff/custom_fields.rb
151
151
  - lib/checkoff/events.rb
152
+ - lib/checkoff/internal/asana_event_enrichment.rb
152
153
  - lib/checkoff/internal/asana_event_filter.rb
153
154
  - lib/checkoff/internal/config_loader.rb
154
155
  - lib/checkoff/internal/create-class.sh