checkoff 0.161.0 → 0.163.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: f83187f80678558975069a3181a34814c3859b81a9a19f0d29606e9ba37e62c4
4
- data.tar.gz: 8e53186cd5ac43fa6b2fb6f9f4f13a4da3259ab75d8792a43459657e8dcaf8bc
3
+ metadata.gz: 4e069f1f67cf8145e42254912651cb575323ca8c5ec0b2e94e8929b6c2e1cb33
4
+ data.tar.gz: 6e0f8039d9e956c0918b03807cc04cf65853232e4b3a97f7d6d5cca72b38b593
5
5
  SHA512:
6
- metadata.gz: 4e4415a477513de35f11579dd45544071ca1b223a5b7a646c2bba0adb2b3becddf7409d4bd69cf0c14d2f0e8fcf55b10014f7d1b90f33d0b667b442aa25dba63
7
- data.tar.gz: 823f046f8f55e05be6d3368cd878abbb02e9632c9bdc7bf5ca5e46160155bee5845ff742550579c55cd1c5013de7e632c828e84c3861ada4998feca0ef29c34a
6
+ metadata.gz: 9f696c01862a1febb0f973bf90a39ce8afbc3be48d0b7df2a57494f9333c8894c2b17a0b3c2c4954fd2014c572d8e6bc78fdbb128e197f9714809e64e5207d45
7
+ data.tar.gz: 0d2d8a45685bf264c00dee9e7fedfbfb7974c0a0e3dcd580618dafb99e936ef52d621411fa462a9a75bc4a54a0290c143cea459b549305131d8c8de02a36c253
data/Gemfile.lock CHANGED
@@ -12,12 +12,13 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.161.0)
15
+ checkoff (0.163.0)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
19
19
  dalli
20
20
  gli
21
+ mime-types
21
22
 
22
23
  GEM
23
24
  remote: https://rubygems.org/
@@ -110,6 +111,9 @@ GEM
110
111
  mixlib-config (>= 2.2.1, < 4)
111
112
  mixlib-shellout
112
113
  method_source (1.0.0)
114
+ mime-types (3.5.1)
115
+ mime-types-data (~> 3.2015)
116
+ mime-types-data (3.2023.1205)
113
117
  minitest (5.20.0)
114
118
  minitest-profile (0.0.2)
115
119
  minitest-reporters (1.6.1)
data/Makefile CHANGED
@@ -49,7 +49,6 @@ clear_metrics: ## remove or reset result artifacts created by tests and quality
49
49
 
50
50
  clean: clear_metrics ## remove all built artifacts
51
51
 
52
-
53
52
  citest: test ## Run unit tests from CircleCI
54
53
 
55
54
  overcommit: ## run precommit quality checks
@@ -69,6 +68,14 @@ repl: ## Load up checkoff in pry
69
68
  clean-coverage:
70
69
  @bundle exec rake clear_metrics
71
70
 
71
+ clean-typecheck: ## Refresh information that type checking depends on
72
+ bundle install
73
+ bundle exec solargraph clear
74
+ rm -fr .yardoc/
75
+ bundle exec yard gems
76
+ bundle exec solargraph scan
77
+ echo all clear
78
+
72
79
  coverage: test report-coverage ## check code coverage
73
80
  @bundle exec rake undercover
74
81
 
data/checkoff.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_runtime_dependency 'cache_method'
30
30
  spec.add_runtime_dependency 'dalli'
31
31
  spec.add_runtime_dependency 'gli'
32
+ spec.add_runtime_dependency 'mime-types'
32
33
 
33
34
  spec.metadata = {
34
35
  'rubygems_mfa_required' => 'false',
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'faraday'
6
+ require 'forwardable'
7
+ require 'cache_method'
8
+ require 'mime/types'
9
+ require 'net/http'
10
+ require 'net/http/response'
11
+ require 'net/http/responses'
12
+ require 'tempfile'
13
+ require_relative 'internal/config_loader'
14
+ require_relative 'internal/logging'
15
+ require_relative 'workspaces'
16
+ require_relative 'tasks'
17
+ require_relative 'clients'
18
+
19
+ # https://developers.asana.com/reference/attachments
20
+
21
+ module Checkoff
22
+ # Manage attachments in Asana
23
+ class Attachments
24
+ # @!parse
25
+ # extend CacheMethod::ClassMethods
26
+
27
+ include Logging
28
+
29
+ MINUTE = 60
30
+ private_constant :MINUTE
31
+ HOUR = MINUTE * 60
32
+ private_constant :HOUR
33
+ DAY = 24 * HOUR
34
+ private_constant :DAY
35
+ REALLY_LONG_CACHE_TIME = HOUR * 1
36
+ private_constant :REALLY_LONG_CACHE_TIME
37
+ LONG_CACHE_TIME = MINUTE * 15
38
+ private_constant :LONG_CACHE_TIME
39
+ SHORT_CACHE_TIME = MINUTE
40
+ private_constant :SHORT_CACHE_TIME
41
+
42
+ # @param config [Hash]
43
+ # @param workspaces [Checkoff::Workspaces]
44
+ # @param clients [Checkoff::Clients]
45
+ # @param client [Asana::Client]
46
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
47
+ workspaces: Checkoff::Workspaces.new(config: config),
48
+ clients: Checkoff::Clients.new(config: config),
49
+ client: clients.client)
50
+ @workspaces = workspaces
51
+ @client = client
52
+ end
53
+
54
+ # @param url [String]
55
+ # @param resource [Asana::Resources::Resource]
56
+ # @param attachment_name [String,nil]
57
+ # @param just_the_url [Boolean]
58
+ #
59
+ # @return [Asana::Resources::Attachment]
60
+ def create_attachment_from_url!(url,
61
+ resource,
62
+ attachment_name: nil,
63
+ just_the_url: false)
64
+ if just_the_url
65
+ create_attachment_from_url_alone!(url, resource, attachment_name: attachment_name)
66
+ else
67
+ create_attachment_from_downloaded_url!(url, resource, attachment_name: attachment_name)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ # Writes contents of URL to a temporary file with the same
74
+ # extension as the URL using Net::HTTP, raising an exception if
75
+ # not succesful
76
+ #
77
+ # @param uri [URI]
78
+ #
79
+ # @return [Object]
80
+ # @sg-ignore
81
+ def download_uri(uri, &block)
82
+ out = nil
83
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
84
+ # @sg-ignore
85
+ request = Net::HTTP::Get.new(uri)
86
+ http.request(request) do |response|
87
+ # use a block to ensure the file is closed after we're done with it
88
+ raise("Unexpected response code: #{response.code}") unless response.code == '200'
89
+
90
+ write_tempfile_from_response(response) do |tempfile|
91
+ out = block.yield tempfile
92
+ end
93
+ end
94
+ end
95
+ out
96
+ end
97
+
98
+ # @sg-ignore
99
+ # @param response [Net::HTTPResponse]
100
+ #
101
+ # @yields [IO]
102
+ #
103
+ # @return [Object]
104
+ def write_tempfile_from_response(response)
105
+ Tempfile.create('checkoff') do |tempfile|
106
+ tempfile.binmode
107
+ # @sg-ignore
108
+ response.read_body do |chunk|
109
+ tempfile.write(chunk)
110
+ end
111
+ tempfile.rewind
112
+
113
+ yield tempfile
114
+ end
115
+ end
116
+
117
+ # @param url [String]
118
+ # @param resource [Asana::Resources::Resource]
119
+ # @param attachment_name [String,nil]
120
+ #
121
+ # @return [Asana::Resources::Attachment]
122
+ def create_attachment_from_downloaded_url!(url, resource, attachment_name:)
123
+ uri = URI(url)
124
+ attachment_name ||= File.basename(uri.path)
125
+ download_uri(uri) do |tempfile|
126
+ content_type ||= content_type_from_filename(attachment_name)
127
+ content_type ||= content_type_from_filename(uri.path)
128
+
129
+ resource.attach(filename: attachment_name, mime: content_type,
130
+ io: tempfile)
131
+ end
132
+ end
133
+
134
+ # @param url [String]
135
+ # @param resource [Asana::Resources::Resource]
136
+ # @param attachment_name [String,nil]
137
+ #
138
+ # @return [Asana::Resources::Attachment,nil]
139
+ def create_attachment_from_url_alone!(url, resource, attachment_name:)
140
+ with_params = {
141
+ 'parent' => resource.gid,
142
+ 'url' => url,
143
+ 'resource_subtype' => 'external',
144
+ 'name' => attachment_name,
145
+ }
146
+ options = {}
147
+ Asana::Resource.new(parse(client.post('/attachments', body: with_params, options: options)).first,
148
+ client: client)
149
+ end
150
+
151
+ # @param filename [String]
152
+ #
153
+ # @return [String,nil]
154
+ # @sg-ignore
155
+ def content_type_from_filename(filename)
156
+ # @sg-ignore
157
+ MIME::Types.type_for(filename)&.first&.content_type
158
+ end
159
+
160
+ # https://github.com/Asana/ruby-asana/blob/master/lib/asana/resource_includes/response_helper.rb#L7
161
+ # @param response [Faraday::Response]
162
+ #
163
+ # @return [Array<Hash, Hash>]
164
+ def parse(response)
165
+ data = response.body.fetch('data') do
166
+ raise("Unexpected response body: #{response.body}")
167
+ end
168
+ extra = response.body.except('data')
169
+ [data, extra]
170
+ end
171
+
172
+ # @return [Checkoff::Workspaces]
173
+ attr_reader :workspaces
174
+
175
+ # @return [Asana::Client]
176
+ attr_reader :client
177
+
178
+ # bundle exec ./attachments.rb
179
+ # :nocov:
180
+ class << self
181
+ # @return [void]
182
+ def run
183
+ # @sg-ignore
184
+ # @type [String]
185
+ gid = ARGV[0] || raise('Please pass task gid as first argument')
186
+ # @sg-ignore
187
+ # @type [String]
188
+ url = ARGV[1] || raise('Please pass attachment URL as second argument')
189
+
190
+ tasks = Checkoff::Tasks.new
191
+ attachments = Checkoff::Attachments.new
192
+ task = tasks.task_by_gid(gid)
193
+ attachment = attachments.create_attachment_from_url!(url, task)
194
+ puts "Results: #{attachment.inspect}"
195
+ end
196
+ end
197
+ # :nocov:
198
+ end
199
+ end
200
+
201
+ # :nocov:
202
+ abs_program_name = File.expand_path($PROGRAM_NAME)
203
+ Checkoff::Attachments.run if abs_program_name == File.expand_path(__FILE__)
204
+ # :nocov:
@@ -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.161.0'
6
+ VERSION = '0.163.0'
7
7
  end
data/lib/checkoff.rb CHANGED
@@ -17,3 +17,4 @@ require 'checkoff/task_selectors'
17
17
  require 'checkoff/project_selectors'
18
18
  require 'checkoff/section_selectors'
19
19
  require 'checkoff/task_searches'
20
+ require 'checkoff/attachments'
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.161.0
4
+ version: 0.163.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-12-26 00:00:00.000000000 Z
11
+ date: 2023-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: mime-types
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description:
84
98
  email:
85
99
  - vince@broz.cc
@@ -129,6 +143,7 @@ files:
129
143
  - exe/checkoff
130
144
  - fix.sh
131
145
  - lib/checkoff.rb
146
+ - lib/checkoff/attachments.rb
132
147
  - lib/checkoff/cli.rb
133
148
  - lib/checkoff/clients.rb
134
149
  - lib/checkoff/create-entity.sh