asana 0.0.6 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +9 -9
  2. data/.codeclimate.yml +4 -0
  3. data/.gitignore +12 -20
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +18 -0
  6. data/.travis.yml +12 -0
  7. data/.yardopts +5 -0
  8. data/CODE_OF_CONDUCT.md +13 -0
  9. data/Gemfile +17 -0
  10. data/Guardfile +85 -4
  11. data/LICENSE.txt +21 -0
  12. data/README.md +264 -135
  13. data/Rakefile +62 -7
  14. data/asana.gemspec +27 -21
  15. data/examples/Gemfile +6 -0
  16. data/examples/Gemfile.lock +56 -0
  17. data/examples/api_token.rb +21 -0
  18. data/examples/cli_app.rb +25 -0
  19. data/examples/events.rb +38 -0
  20. data/examples/omniauth_integration.rb +54 -0
  21. data/lib/asana.rb +8 -11
  22. data/lib/asana/authentication.rb +8 -0
  23. data/lib/asana/authentication/oauth2.rb +42 -0
  24. data/lib/asana/authentication/oauth2/access_token_authentication.rb +51 -0
  25. data/lib/asana/authentication/oauth2/bearer_token_authentication.rb +32 -0
  26. data/lib/asana/authentication/oauth2/client.rb +50 -0
  27. data/lib/asana/authentication/token_authentication.rb +20 -0
  28. data/lib/asana/client.rb +124 -0
  29. data/lib/asana/client/configuration.rb +165 -0
  30. data/lib/asana/errors.rb +90 -0
  31. data/lib/asana/http_client.rb +155 -0
  32. data/lib/asana/http_client/environment_info.rb +53 -0
  33. data/lib/asana/http_client/error_handling.rb +103 -0
  34. data/lib/asana/http_client/response.rb +32 -0
  35. data/lib/asana/resources.rb +11 -0
  36. data/lib/asana/resources/attachment.rb +44 -0
  37. data/lib/asana/resources/attachment_uploading.rb +33 -0
  38. data/lib/asana/resources/collection.rb +68 -0
  39. data/lib/asana/resources/event.rb +49 -0
  40. data/lib/asana/resources/event_subscription.rb +12 -0
  41. data/lib/asana/resources/events.rb +101 -0
  42. data/lib/asana/resources/project.rb +145 -19
  43. data/lib/asana/resources/registry.rb +62 -0
  44. data/lib/asana/resources/resource.rb +103 -0
  45. data/lib/asana/resources/response_helper.rb +14 -0
  46. data/lib/asana/resources/story.rb +58 -7
  47. data/lib/asana/resources/tag.rb +111 -19
  48. data/lib/asana/resources/task.rb +284 -57
  49. data/lib/asana/resources/team.rb +55 -0
  50. data/lib/asana/resources/user.rb +65 -10
  51. data/lib/asana/resources/workspace.rb +79 -34
  52. data/lib/asana/ruby2_0_0_compatibility.rb +3 -0
  53. data/lib/asana/version.rb +3 -1
  54. data/lib/templates/index.js +8 -0
  55. data/lib/templates/resource.ejs +225 -0
  56. data/package.json +7 -0
  57. metadata +91 -51
  58. data/LICENSE +0 -22
  59. data/lib/asana/config.rb +0 -23
  60. data/lib/asana/resource.rb +0 -52
  61. data/spec/asana/resources/project_spec.rb +0 -63
  62. data/spec/asana/resources/story_spec.rb +0 -39
  63. data/spec/asana/resources/tag_spec.rb +0 -63
  64. data/spec/asana/resources/task_spec.rb +0 -95
  65. data/spec/asana/resources/user_spec.rb +0 -64
  66. data/spec/asana/resources/workspace_spec.rb +0 -108
  67. data/spec/spec_helper.rb +0 -9
data/Rakefile CHANGED
@@ -1,10 +1,65 @@
1
- #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
3
- require 'rake/testtask'
1
+ require 'rspec/core/rake_task'
2
+ require 'rubocop/rake_task'
3
+ require 'yard'
4
4
 
5
- Rake::TestTask.new do |t|
6
- t.pattern = 'spec/**/*_spec.rb'
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
- desc "Run tests"
10
- task :default => :test
65
+ task default: [:codegen, :spec, :rubocop, :yard]
@@ -1,26 +1,32 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/asana/version', __FILE__)
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 |gem|
5
- gem.authors = ["Ryan Bright"]
6
- gem.email = ["ryan@rbright.net"]
7
- gem.description = %q{Ruby wrapper for the Asana REST API}
8
- gem.summary = %q{Ruby wrapper for the Asana REST API}
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
- gem.add_dependency 'activeresource', '>= 3.2.3'
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
- gem.add_development_dependency 'guard-minitest', '~> 1.0.0'
15
- gem.add_development_dependency 'minitest', '~> 2.12.1'
16
- gem.add_development_dependency 'rake', '~> 0.9.2.2'
17
- gem.add_development_dependency 'vcr', '~> 2.1.0'
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
- gem.files = `git ls-files`.split($\)
21
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
- gem.name = "asana"
24
- gem.require_paths = ["lib"]
25
- gem.version = Asana::VERSION
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
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'omniauth'
4
+ gem 'omniauth-asana'
5
+ gem 'sinatra'
6
+ gem 'ruby-asana', path: '../'
@@ -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
@@ -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
@@ -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
@@ -1,15 +1,12 @@
1
- require 'active_resource'
2
-
3
- require 'asana/config'
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
- require 'asana/resources/project'
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
- extend Config
11
+ include Asana::Resources
15
12
  end
@@ -0,0 +1,8 @@
1
+ require_relative 'authentication/oauth2'
2
+ require_relative 'authentication/token_authentication'
3
+
4
+ module Asana
5
+ # Public: Authentication strategies for the Asana API.
6
+ module Authentication
7
+ end
8
+ 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