asana 0.0.6 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +9 -9
- data/.codeclimate.yml +4 -0
- data/.gitignore +12 -20
- data/.rspec +4 -0
- data/.rubocop.yml +18 -0
- data/.travis.yml +12 -0
- data/.yardopts +5 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +17 -0
- data/Guardfile +85 -4
- data/LICENSE.txt +21 -0
- data/README.md +264 -135
- data/Rakefile +62 -7
- data/asana.gemspec +27 -21
- data/examples/Gemfile +6 -0
- data/examples/Gemfile.lock +56 -0
- data/examples/api_token.rb +21 -0
- data/examples/cli_app.rb +25 -0
- data/examples/events.rb +38 -0
- data/examples/omniauth_integration.rb +54 -0
- data/lib/asana.rb +8 -11
- data/lib/asana/authentication.rb +8 -0
- data/lib/asana/authentication/oauth2.rb +42 -0
- data/lib/asana/authentication/oauth2/access_token_authentication.rb +51 -0
- data/lib/asana/authentication/oauth2/bearer_token_authentication.rb +32 -0
- data/lib/asana/authentication/oauth2/client.rb +50 -0
- data/lib/asana/authentication/token_authentication.rb +20 -0
- data/lib/asana/client.rb +124 -0
- data/lib/asana/client/configuration.rb +165 -0
- data/lib/asana/errors.rb +90 -0
- data/lib/asana/http_client.rb +155 -0
- data/lib/asana/http_client/environment_info.rb +53 -0
- data/lib/asana/http_client/error_handling.rb +103 -0
- data/lib/asana/http_client/response.rb +32 -0
- data/lib/asana/resources.rb +11 -0
- data/lib/asana/resources/attachment.rb +44 -0
- data/lib/asana/resources/attachment_uploading.rb +33 -0
- data/lib/asana/resources/collection.rb +68 -0
- data/lib/asana/resources/event.rb +49 -0
- data/lib/asana/resources/event_subscription.rb +12 -0
- data/lib/asana/resources/events.rb +101 -0
- data/lib/asana/resources/project.rb +145 -19
- data/lib/asana/resources/registry.rb +62 -0
- data/lib/asana/resources/resource.rb +103 -0
- data/lib/asana/resources/response_helper.rb +14 -0
- data/lib/asana/resources/story.rb +58 -7
- data/lib/asana/resources/tag.rb +111 -19
- data/lib/asana/resources/task.rb +284 -57
- data/lib/asana/resources/team.rb +55 -0
- data/lib/asana/resources/user.rb +65 -10
- data/lib/asana/resources/workspace.rb +79 -34
- data/lib/asana/ruby2_0_0_compatibility.rb +3 -0
- data/lib/asana/version.rb +3 -1
- data/lib/templates/index.js +8 -0
- data/lib/templates/resource.ejs +225 -0
- data/package.json +7 -0
- metadata +91 -51
- data/LICENSE +0 -22
- data/lib/asana/config.rb +0 -23
- data/lib/asana/resource.rb +0 -52
- data/spec/asana/resources/project_spec.rb +0 -63
- data/spec/asana/resources/story_spec.rb +0 -39
- data/spec/asana/resources/tag_spec.rb +0 -63
- data/spec/asana/resources/task_spec.rb +0 -95
- data/spec/asana/resources/user_spec.rb +0 -64
- data/spec/asana/resources/workspace_spec.rb +0 -108
- data/spec/spec_helper.rb +0 -9
data/Rakefile
CHANGED
@@ -1,10 +1,65 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require '
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require 'rubocop/rake_task'
|
3
|
+
require 'yard'
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
RuboCop::RakeTask.new
|
8
|
+
|
9
|
+
YARD::Rake::YardocTask.new do |t|
|
10
|
+
t.stats_options = ['--list-undoc']
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Generates a test resource from a YAML using the resource template.'
|
14
|
+
task :codegen do
|
15
|
+
`node spec/support/codegen.js`
|
16
|
+
end
|
17
|
+
|
18
|
+
namespace :bump do
|
19
|
+
def read_version
|
20
|
+
File.readlines('./lib/asana/version.rb')
|
21
|
+
.detect { |l| l =~ /VERSION/ }
|
22
|
+
.scan(/VERSION = '([^']+)/).flatten.first.split('.')
|
23
|
+
.map { |n| Integer(n) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# rubocop:disable Metrics/MethodLength
|
27
|
+
def write_version(major, minor, patch)
|
28
|
+
str = <<-EOS
|
29
|
+
#:nodoc:
|
30
|
+
module Asana
|
31
|
+
# Public: Version of the gem.
|
32
|
+
VERSION = '#{major}.#{minor}.#{patch}'
|
33
|
+
end
|
34
|
+
EOS
|
35
|
+
File.open('./lib/asana/version.rb', 'w') do |f|
|
36
|
+
f.write str
|
37
|
+
end
|
38
|
+
|
39
|
+
new_version = "#{major}.#{minor}.#{patch}"
|
40
|
+
system('git add lib/asana/version.rb')
|
41
|
+
system(%(git commit -m "Bumped to #{new_version}" && ) +
|
42
|
+
%(git tag -a v#{new_version} -m "Version #{new_version}"))
|
43
|
+
puts "\nRun git push --tags to release."
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Bumps a patch version'
|
47
|
+
task :patch do
|
48
|
+
major, minor, patch = read_version
|
49
|
+
write_version major, minor, patch + 1
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'Bumps a minor version'
|
53
|
+
task :minor do
|
54
|
+
major, minor, = read_version
|
55
|
+
write_version major, minor + 1, 0
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'Bumps a major version'
|
59
|
+
task :major do
|
60
|
+
major, = read_version
|
61
|
+
write_version major + 1, 0, 0
|
62
|
+
end
|
7
63
|
end
|
8
64
|
|
9
|
-
|
10
|
-
task :default => :test
|
65
|
+
task default: [:codegen, :spec, :rubocop, :yard]
|
data/asana.gemspec
CHANGED
@@ -1,26 +1,32 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'asana/version'
|
3
5
|
|
4
|
-
Gem::Specification.new do |
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
gem.homepage = "http://github.com/rbright/asana"
|
10
|
-
gem.license = 'MIT'
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "asana"
|
8
|
+
spec.version = Asana::VERSION
|
9
|
+
spec.authors = ["Txus"]
|
10
|
+
spec.email = ["me@txus.io"]
|
11
11
|
|
12
|
-
|
12
|
+
spec.summary = %q{Official Ruby client for the Asana API}
|
13
|
+
spec.description = %q{Official Ruby client for the Asana API}
|
14
|
+
spec.homepage = "https://github.com/asana/ruby-asana"
|
15
|
+
spec.license = "MIT"
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
gem.add_development_dependency 'webmock', '~> 1.8.6'
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
spec.required_ruby_version = '~> 2.0'
|
23
|
+
|
24
|
+
spec.add_dependency "oauth2", "~> 1.0"
|
25
|
+
spec.add_dependency "faraday", "~> 0.9"
|
26
|
+
spec.add_dependency "faraday_middleware", "~> 0.9"
|
27
|
+
spec.add_dependency "faraday_middleware-multi_json", "~> 0.0"
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
spec.add_development_dependency "rspec", "~> 3.2"
|
26
32
|
end
|
data/examples/Gemfile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
ruby-asana (0.1.0)
|
5
|
+
faraday (~> 0.9)
|
6
|
+
faraday_middleware (~> 0.9)
|
7
|
+
faraday_middleware-multi_json (~> 0.0)
|
8
|
+
oauth2 (~> 1.0)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
faraday (0.9.1)
|
14
|
+
multipart-post (>= 1.2, < 3)
|
15
|
+
faraday_middleware (0.9.1)
|
16
|
+
faraday (>= 0.7.4, < 0.10)
|
17
|
+
faraday_middleware-multi_json (0.0.6)
|
18
|
+
faraday_middleware
|
19
|
+
multi_json
|
20
|
+
hashie (3.4.1)
|
21
|
+
jwt (1.5.0)
|
22
|
+
multi_json (1.11.0)
|
23
|
+
multi_xml (0.5.5)
|
24
|
+
multipart-post (2.0.0)
|
25
|
+
oauth2 (1.0.0)
|
26
|
+
faraday (>= 0.8, < 0.10)
|
27
|
+
jwt (~> 1.0)
|
28
|
+
multi_json (~> 1.3)
|
29
|
+
multi_xml (~> 0.5)
|
30
|
+
rack (~> 1.2)
|
31
|
+
omniauth (1.2.2)
|
32
|
+
hashie (>= 1.2, < 4)
|
33
|
+
rack (~> 1.0)
|
34
|
+
omniauth-asana (0.0.1)
|
35
|
+
omniauth (~> 1.0)
|
36
|
+
omniauth-oauth2 (~> 1.1)
|
37
|
+
omniauth-oauth2 (1.3.0)
|
38
|
+
oauth2 (~> 1.0)
|
39
|
+
omniauth (~> 1.2)
|
40
|
+
rack (1.6.1)
|
41
|
+
rack-protection (1.5.3)
|
42
|
+
rack
|
43
|
+
sinatra (1.4.6)
|
44
|
+
rack (~> 1.4)
|
45
|
+
rack-protection (~> 1.4)
|
46
|
+
tilt (>= 1.3, < 3)
|
47
|
+
tilt (2.0.1)
|
48
|
+
|
49
|
+
PLATFORMS
|
50
|
+
ruby
|
51
|
+
|
52
|
+
DEPENDENCIES
|
53
|
+
omniauth
|
54
|
+
omniauth-asana
|
55
|
+
ruby-asana!
|
56
|
+
sinatra
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
3
|
+
require 'asana'
|
4
|
+
|
5
|
+
api_token = ENV['ASANA_API_TOKEN']
|
6
|
+
unless api_token
|
7
|
+
abort "Run this program with the env var ASANA_API_TOKEN.\n" \
|
8
|
+
"Go to http://app.asana.com/-/account_api to see your API token."
|
9
|
+
end
|
10
|
+
|
11
|
+
client = Asana::Client.new do |c|
|
12
|
+
c.authentication :api_token, api_token
|
13
|
+
end
|
14
|
+
|
15
|
+
puts "My Workspaces:"
|
16
|
+
client.workspaces.find_all.each do |workspace|
|
17
|
+
puts "\t* #{workspace.name} - tags:"
|
18
|
+
client.tags.find_by_workspace(workspace: workspace.id).each do |tag|
|
19
|
+
puts "\t\t- #{tag.name}"
|
20
|
+
end
|
21
|
+
end
|
data/examples/cli_app.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
3
|
+
require 'asana'
|
4
|
+
|
5
|
+
id, secret = ENV['ASANA_CLIENT_ID'], ENV['ASANA_CLIENT_SECRET']
|
6
|
+
unless id && secret
|
7
|
+
abort "Run this program with the env vars ASANA_CLIENT_ID and ASANA_CLIENT_SECRET.\n" \
|
8
|
+
"Refer to https://asana.com/developers/documentation/getting-started/authentication "\
|
9
|
+
"to get your credentials." \
|
10
|
+
"The redirect URI for your application should be \"urn:ietf:wg:oauth:2.0:oob\"."
|
11
|
+
end
|
12
|
+
|
13
|
+
access_token = Asana::Authentication::OAuth2.offline_flow(client_id: id,
|
14
|
+
client_secret: secret)
|
15
|
+
client = Asana::Client.new do |c|
|
16
|
+
c.authentication :oauth2, access_token
|
17
|
+
end
|
18
|
+
|
19
|
+
puts "My Workspaces:"
|
20
|
+
client.workspaces.find_all.each do |workspace|
|
21
|
+
puts "\t* #{workspace.name} - tags:"
|
22
|
+
client.tags.find_by_workspace(workspace: workspace.id).each do |tag|
|
23
|
+
puts "\t\t- #{tag.name}"
|
24
|
+
end
|
25
|
+
end
|
data/examples/events.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.require
|
4
|
+
require 'asana'
|
5
|
+
|
6
|
+
api_token = ENV['ASANA_API_TOKEN']
|
7
|
+
unless api_token
|
8
|
+
abort "Run this program with the env var ASANA_API_TOKEN.\n" \
|
9
|
+
"Go to http://app.asana.com/-/account_api to see your API token."
|
10
|
+
end
|
11
|
+
|
12
|
+
client = Asana::Client.new do |c|
|
13
|
+
c.authentication :api_token, api_token
|
14
|
+
end
|
15
|
+
|
16
|
+
workspace = client.workspaces.find_all.first
|
17
|
+
task = client.tasks.find_all(assignee: "me", workspace: workspace.id).first
|
18
|
+
unless task
|
19
|
+
task = client.tasks.create(workspace: workspace.id, name: "Hello world!", assignee: "me")
|
20
|
+
end
|
21
|
+
|
22
|
+
Thread.abort_on_exception = true
|
23
|
+
|
24
|
+
Thread.new do
|
25
|
+
puts "Listening for 'changed' events on #{task} in one thread..."
|
26
|
+
task.events(wait: 2).lazy.select { |event| event.action == 'changed' }.each do |event|
|
27
|
+
puts "#{event.user.name} changed #{event.resource}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Thread.new do
|
32
|
+
puts "Listening for non-'changed' events on #{task} in another thread..."
|
33
|
+
task.events(wait: 1).lazy.reject { |event| event.action == 'changed' }.each do |event|
|
34
|
+
puts "'#{event.action}' event: #{event}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sleep
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
3
|
+
require 'asana'
|
4
|
+
|
5
|
+
class SinatraApp < Sinatra::Base
|
6
|
+
id, secret = ENV['ASANA_CLIENT_ID'], ENV['ASANA_CLIENT_SECRET']
|
7
|
+
unless id && secret
|
8
|
+
abort "Run this program with the env vars ASANA_CLIENT_ID and ASANA_CLIENT_SECRET.\n" \
|
9
|
+
"Refer to https://asana.com/developers/documentation/getting-started/authentication "\
|
10
|
+
"to get your credentials."
|
11
|
+
end
|
12
|
+
|
13
|
+
use OmniAuth::Strategies::Asana, id, secret
|
14
|
+
|
15
|
+
enable :sessions
|
16
|
+
|
17
|
+
get '/' do
|
18
|
+
if $client
|
19
|
+
'<a href="/workspaces">My Workspaces</a>'
|
20
|
+
else
|
21
|
+
'<a href="/sign_in">sign in to asana</a>'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
get '/workspaces' do
|
26
|
+
if $client
|
27
|
+
"<h1>My Workspaces</h1>" \
|
28
|
+
"<ul>" + $client.workspaces.find_all.map { |w| "<li>#{w.name}</li>" }.join + "</ul>"
|
29
|
+
else
|
30
|
+
redirect '/sign_in'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
get '/auth/:name/callback' do
|
35
|
+
creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
|
36
|
+
strategy = request.env["omniauth.strategy"]
|
37
|
+
access_token = OAuth2::AccessToken.from_hash(strategy.client, creds).refresh!
|
38
|
+
$client = Asana::Client.new do |c|
|
39
|
+
c.authentication :oauth2, access_token
|
40
|
+
end
|
41
|
+
redirect '/workspaces'
|
42
|
+
end
|
43
|
+
|
44
|
+
get '/sign_in' do
|
45
|
+
redirect '/auth/asana'
|
46
|
+
end
|
47
|
+
|
48
|
+
get '/sign_out' do
|
49
|
+
$client = nil
|
50
|
+
redirect '/'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
SinatraApp.run! if __FILE__ == $0
|
data/lib/asana.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
require 'asana/
|
1
|
+
require 'asana/ruby2_0_0_compatibility'
|
2
|
+
require 'asana/authentication'
|
3
|
+
require 'asana/resources'
|
4
|
+
require 'asana/client'
|
5
|
+
require 'asana/errors'
|
6
|
+
require 'asana/http_client'
|
4
7
|
require 'asana/version'
|
5
8
|
|
6
|
-
|
7
|
-
require 'asana/resources/tag'
|
8
|
-
require 'asana/resources/story'
|
9
|
-
require 'asana/resources/task'
|
10
|
-
require 'asana/resources/user'
|
11
|
-
require 'asana/resources/workspace'
|
12
|
-
|
9
|
+
# Public: Top-level namespace of the Asana API Ruby client.
|
13
10
|
module Asana
|
14
|
-
|
11
|
+
include Asana::Resources
|
15
12
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'oauth2/bearer_token_authentication'
|
2
|
+
require_relative 'oauth2/access_token_authentication'
|
3
|
+
require_relative 'oauth2/client'
|
4
|
+
|
5
|
+
module Asana
|
6
|
+
module Authentication
|
7
|
+
# Public: Deals with OAuth2 authentication. Contains a function to get an
|
8
|
+
# access token throught a browserless authentication flow, needed for some
|
9
|
+
# applications such as CLI applications.
|
10
|
+
module OAuth2
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# Public: Retrieves an access token through an offline authentication
|
14
|
+
# flow. If your application can receive HTTP requests, you might want to
|
15
|
+
# opt for a browser-based flow and use the omniauth-asana gem instead.
|
16
|
+
#
|
17
|
+
# Your registered application's redirect_uri should be exactly
|
18
|
+
# "urn:ietf:wg:oauth:2.0:oob".
|
19
|
+
#
|
20
|
+
# client_id - [String] the client id of the registered Asana API
|
21
|
+
# application.
|
22
|
+
# client_secret - [String] the client secret of the registered Asana API
|
23
|
+
# application.
|
24
|
+
#
|
25
|
+
# Returns an ::OAuth2::AccessToken object.
|
26
|
+
#
|
27
|
+
# Note: This function reads from STDIN and writes to STDOUT. It is meant
|
28
|
+
# to be used only within the context of a CLI application.
|
29
|
+
def offline_flow(client_id: required('client_id'),
|
30
|
+
client_secret: required('client_secret'))
|
31
|
+
client = Client.new(client_id: client_id,
|
32
|
+
client_secret: client_secret,
|
33
|
+
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob')
|
34
|
+
STDOUT.puts '1. Go to the following URL to authorize the ' \
|
35
|
+
" application: #{client.authorize_url}"
|
36
|
+
STDOUT.puts '2. Paste the authorization code here: '
|
37
|
+
auth_code = STDIN.gets.chomp
|
38
|
+
client.token_from_auth_code(auth_code)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Asana
|
2
|
+
module Authentication
|
3
|
+
module OAuth2
|
4
|
+
# Public: A mechanism to authenticate with an OAuth2 access token (a
|
5
|
+
# bearer token and a refresh token) or just a refresh token.
|
6
|
+
class AccessTokenAuthentication
|
7
|
+
# Public: Builds an AccessTokenAuthentication from a refresh token and
|
8
|
+
# client credentials, by refreshing into a new one.
|
9
|
+
#
|
10
|
+
# refresh_token - [String] a refresh token
|
11
|
+
# client_id - [String] the client id of the registered Asana API
|
12
|
+
# Application.
|
13
|
+
# client_secret - [String] the client secret of the registered Asana API
|
14
|
+
# Application.
|
15
|
+
# redirect_uri - [String] the redirect uri of the registered Asana API
|
16
|
+
# Application.
|
17
|
+
#
|
18
|
+
# Returns an [AccessTokenAuthentication] instance with a refreshed
|
19
|
+
# access token.
|
20
|
+
def self.from_refresh_token(refresh_token,
|
21
|
+
client_id: required('client_id'),
|
22
|
+
client_secret: required('client_secret'),
|
23
|
+
redirect_uri: required('redirect_uri'))
|
24
|
+
client = Client.new(client_id: client_id,
|
25
|
+
client_secret: client_secret,
|
26
|
+
redirect_uri: redirect_uri)
|
27
|
+
new(client.token_from_refresh_token(refresh_token))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Public: Initializes a new AccessTokenAuthentication.
|
31
|
+
#
|
32
|
+
# access_token - [::OAuth2::AccessToken] An ::OAuth2::AccessToken
|
33
|
+
# object.
|
34
|
+
def initialize(access_token)
|
35
|
+
@token = access_token
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Configures a Faraday connection injecting a bearer token,
|
39
|
+
# auto-refreshing it when needed.
|
40
|
+
#
|
41
|
+
# connection - [Faraday::Connection] the Faraday connection instance.
|
42
|
+
#
|
43
|
+
# Returns nothing.
|
44
|
+
def configure(connection)
|
45
|
+
@token = @token.refresh! if @token.expired?
|
46
|
+
connection.request :oauth2, @token.token
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|