checkoff 0.161.0 → 0.162.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: f83187f80678558975069a3181a34814c3859b81a9a19f0d29606e9ba37e62c4
4
- data.tar.gz: 8e53186cd5ac43fa6b2fb6f9f4f13a4da3259ab75d8792a43459657e8dcaf8bc
3
+ metadata.gz: 79b74e311c0a9146b3dd2d501940eee17ff14519bdf422ae031808cdd64afef9
4
+ data.tar.gz: 2b464f0fc470ac543a24683dc6d58f2b7131cf6094ab8a9f84813dbae0726d04
5
5
  SHA512:
6
- metadata.gz: 4e4415a477513de35f11579dd45544071ca1b223a5b7a646c2bba0adb2b3becddf7409d4bd69cf0c14d2f0e8fcf55b10014f7d1b90f33d0b667b442aa25dba63
7
- data.tar.gz: 823f046f8f55e05be6d3368cd878abbb02e9632c9bdc7bf5ca5e46160155bee5845ff742550579c55cd1c5013de7e632c828e84c3861ada4998feca0ef29c34a
6
+ metadata.gz: cc73effc84d1d49b688a494ff729b07507d94b29bb8647a96f207cde22d72a39006d9e756e6a35bc218eb603d2736de578914c90871fef595f4301fad193a0ed
7
+ data.tar.gz: 9825765f922b7ceac77884761cef8e937eb39313ff8daf9daed4d478c3a6b63ea497eb0aa9e3f9727483c6657307d85317c03e22d08e36b71d5dd971f6487916
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.162.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,203 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'faraday'
6
+ require 'faraday/multipart'
7
+ require 'forwardable'
8
+ require 'cache_method'
9
+ require 'mime/types'
10
+ require 'net/http'
11
+ require 'net/http/response'
12
+ require 'net/http/responses'
13
+ require 'tempfile'
14
+ require_relative 'internal/config_loader'
15
+ require_relative 'internal/logging'
16
+ require_relative 'workspaces'
17
+ require_relative 'tasks'
18
+ require_relative 'clients'
19
+
20
+ # https://developers.asana.com/reference/attachments
21
+
22
+ module Checkoff
23
+ # Manage attachments in Asana
24
+ class Attachments
25
+ # @!parse
26
+ # extend CacheMethod::ClassMethods
27
+
28
+ include Logging
29
+
30
+ MINUTE = 60
31
+ private_constant :MINUTE
32
+ HOUR = MINUTE * 60
33
+ private_constant :HOUR
34
+ DAY = 24 * HOUR
35
+ private_constant :DAY
36
+ REALLY_LONG_CACHE_TIME = HOUR * 1
37
+ private_constant :REALLY_LONG_CACHE_TIME
38
+ LONG_CACHE_TIME = MINUTE * 15
39
+ private_constant :LONG_CACHE_TIME
40
+ SHORT_CACHE_TIME = MINUTE
41
+ private_constant :SHORT_CACHE_TIME
42
+
43
+ # @param config [Hash]
44
+ # @param workspaces [Checkoff::Workspaces]
45
+ # @param clients [Checkoff::Clients]
46
+ # @param client [Asana::Client]
47
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
48
+ workspaces: Checkoff::Workspaces.new(config: config),
49
+ clients: Checkoff::Clients.new(config: config),
50
+ client: clients.client)
51
+ @workspaces = workspaces
52
+ @client = client
53
+ end
54
+
55
+ # @param url [String]
56
+ # @param resource [Asana::Resources::Resource]
57
+ # @param attachment_name [String,nil]
58
+ # @param just_the_url [Boolean]
59
+ #
60
+ # @return [Asana::Resources::Attachment]
61
+ def create_attachment_from_url!(url,
62
+ resource,
63
+ attachment_name: nil,
64
+ just_the_url: false)
65
+ if just_the_url
66
+ create_attachment_from_url_alone!(url, resource, attachment_name: attachment_name)
67
+ else
68
+ create_attachment_from_downloaded_url!(url, resource, attachment_name: attachment_name)
69
+ end
70
+ end
71
+
72
+ # Writes contents of URL to a temporary file with the same
73
+ # extension as the URL using Net::HTTP, raising an exception if
74
+ # not succesful
75
+ #
76
+ # @param uri [URI]
77
+ #
78
+ # @return [Object]
79
+ # @sg-ignore
80
+ def download_uri(uri, &block)
81
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
82
+ # @sg-ignore
83
+ request = Net::HTTP::Get.new(uri)
84
+ http.request(request) do |response|
85
+ # use a block to ensure the file is closed after we're done with it
86
+ raise("Unexpected response code: #{response.code}") unless response.code == '200'
87
+
88
+ write_tempfile_from_response(response) do |tempfile|
89
+ block.yield tempfile
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # @sg-ignore
96
+ # @param response [Net::HTTPResponse]
97
+ #
98
+ # @yields [IO]
99
+ #
100
+ # @return [Object]
101
+ def write_tempfile_from_response(response)
102
+ Tempfile.create('checkoff') do |tempfile|
103
+ tempfile.binmode
104
+ # @sg-ignore
105
+ response.read_body do |chunk|
106
+ tempfile.write(chunk)
107
+ end
108
+ tempfile.rewind
109
+
110
+ yield tempfile
111
+ end
112
+ end
113
+
114
+ # @param url [String]
115
+ # @param resource [Asana::Resources::Resource]
116
+ # @param attachment_name [String,nil]
117
+ #
118
+ # @return [Asana::Resources::Attachment]
119
+ def create_attachment_from_downloaded_url!(url, resource, attachment_name:)
120
+ uri = URI(url)
121
+ attachment_name ||= File.basename(uri.path)
122
+ download_uri(uri) do |tempfile|
123
+ content_type ||= content_type_from_filename(attachment_name)
124
+ content_type ||= content_type_from_filename(uri.path)
125
+
126
+ resource.attach(filename: attachment_name, mime: content_type,
127
+ io: tempfile)
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ # @param url [String]
134
+ # @param resource [Asana::Resources::Resource]
135
+ # @param attachment_name [String,nil]
136
+ #
137
+ # @return [Asana::Resources::Attachment,nil]
138
+ def create_attachment_from_url_alone!(url, resource, attachment_name:)
139
+ with_params = {
140
+ 'parent' => resource.gid,
141
+ 'url' => url,
142
+ 'resource_subtype' => 'external',
143
+ 'name' => attachment_name,
144
+ }
145
+ options = {}
146
+ Asana::Resource.new(parse(client.post('/attachments', body: with_params, options: options)).first,
147
+ client: client)
148
+ end
149
+
150
+ # @param filename [String]
151
+ #
152
+ # @return [String,nil]
153
+ # @sg-ignore
154
+ def content_type_from_filename(filename)
155
+ # @sg-ignore
156
+ MIME::Types.type_for(filename)&.first&.content_type
157
+ end
158
+
159
+ # https://github.com/Asana/ruby-asana/blob/master/lib/asana/resource_includes/response_helper.rb#L7
160
+ # @param response [Faraday::Response]
161
+ #
162
+ # @return [Array<Hash, Hash>]
163
+ def parse(response)
164
+ data = response.body.fetch('data') do
165
+ raise("Unexpected response body: #{response.body}")
166
+ end
167
+ extra = response.body.except('data')
168
+ [data, extra]
169
+ end
170
+
171
+ # @return [Checkoff::Workspaces]
172
+ attr_reader :workspaces
173
+
174
+ # @return [Asana::Client]
175
+ attr_reader :client
176
+
177
+ # bundle exec ./attachments.rb
178
+ # :nocov:
179
+ class << self
180
+ # @return [void]
181
+ def run
182
+ # @sg-ignore
183
+ # @type [String]
184
+ gid = ARGV[0] || raise('Please pass task gid as first argument')
185
+ # @sg-ignore
186
+ # @type [String]
187
+ url = ARGV[1] || raise('Please pass attachment URL as second argument')
188
+
189
+ tasks = Checkoff::Tasks.new
190
+ attachments = Checkoff::Attachments.new
191
+ task = tasks.task_by_gid(gid)
192
+ attachment = attachments.create_attachment_from_url!(url, task)
193
+ puts "Results: #{attachment.inspect}"
194
+ end
195
+ end
196
+ # :nocov:
197
+ end
198
+ end
199
+
200
+ # :nocov:
201
+ abs_program_name = File.expand_path($PROGRAM_NAME)
202
+ Checkoff::Attachments.run if abs_program_name == File.expand_path(__FILE__)
203
+ # :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.162.0'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
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.162.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vince Broz
@@ -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