asana 0.0.6 → 0.1.1

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.
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