checkoff 0.185.0 → 0.186.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 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