openc-asana 0.1.2
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 +7 -0
- data/.codeclimate.yml +4 -0
- data/.gitignore +13 -0
- data/.rspec +4 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +12 -0
- data/.yardopts +5 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +21 -0
- data/Guardfile +86 -0
- data/LICENSE.txt +21 -0
- data/README.md +355 -0
- data/Rakefile +65 -0
- data/examples/Gemfile +6 -0
- data/examples/Gemfile.lock +59 -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 +12 -0
- 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 +92 -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/resource_includes/attachment_uploading.rb +33 -0
- data/lib/asana/resource_includes/collection.rb +68 -0
- data/lib/asana/resource_includes/event.rb +51 -0
- data/lib/asana/resource_includes/event_subscription.rb +14 -0
- data/lib/asana/resource_includes/events.rb +103 -0
- data/lib/asana/resource_includes/registry.rb +63 -0
- data/lib/asana/resource_includes/resource.rb +103 -0
- data/lib/asana/resource_includes/response_helper.rb +14 -0
- data/lib/asana/resources.rb +14 -0
- data/lib/asana/resources/attachment.rb +44 -0
- data/lib/asana/resources/project.rb +154 -0
- data/lib/asana/resources/story.rb +64 -0
- data/lib/asana/resources/tag.rb +120 -0
- data/lib/asana/resources/task.rb +300 -0
- data/lib/asana/resources/team.rb +55 -0
- data/lib/asana/resources/user.rb +72 -0
- data/lib/asana/resources/workspace.rb +91 -0
- data/lib/asana/ruby2_0_0_compatibility.rb +3 -0
- data/lib/asana/version.rb +5 -0
- data/lib/templates/index.js +8 -0
- data/lib/templates/resource.ejs +225 -0
- data/openc-asana.gemspec +32 -0
- data/package.json +7 -0
- metadata +200 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Asana
|
2
|
+
module Authentication
|
3
|
+
# Public: Represents an API token authentication mechanism.
|
4
|
+
class TokenAuthentication
|
5
|
+
def initialize(token)
|
6
|
+
@token = token
|
7
|
+
end
|
8
|
+
|
9
|
+
# Public: Configures a Faraday connection injecting its token as
|
10
|
+
# basic auth.
|
11
|
+
#
|
12
|
+
# builder - [Faraday::Connection] the Faraday connection instance.
|
13
|
+
#
|
14
|
+
# Returns nothing.
|
15
|
+
def configure(connection)
|
16
|
+
connection.basic_auth(@token, '')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/asana/client.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require_relative 'authentication'
|
2
|
+
require_relative 'client/configuration'
|
3
|
+
require_relative 'resources'
|
4
|
+
|
5
|
+
module Asana
|
6
|
+
# Public: A client to interact with the Asana API. It exposes all the
|
7
|
+
# available resources of the Asana API in idiomatic Ruby.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# # Authentication with an API token
|
12
|
+
# Asana::Client.new do |client|
|
13
|
+
# client.authentication :api_token, '...'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # OAuth2 with a plain bearer token (doesn't support auto-refresh)
|
17
|
+
# Asana::Client.new do |client|
|
18
|
+
# client.authentication :oauth2, bearer_token: '...'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# # OAuth2 with a plain refresh token and client credentials
|
22
|
+
# Asana::Client.new do |client|
|
23
|
+
# client.authentication :oauth2,
|
24
|
+
# refresh_token: '...',
|
25
|
+
# client_id: '...',
|
26
|
+
# client_secret: '...',
|
27
|
+
# redirect_uri: '...'
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # OAuth2 with an ::OAuth2::AccessToken object
|
31
|
+
# Asana::Client.new do |client|
|
32
|
+
# client.authentication :oauth2, my_oauth2_access_token_object
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # Use a custom Faraday network adapter
|
36
|
+
# Asana::Client.new do |client|
|
37
|
+
# client.authentication ...
|
38
|
+
# client.adapter :typhoeus
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# # Use a custom user agent string
|
42
|
+
# Asana::Client.new do |client|
|
43
|
+
# client.authentication ...
|
44
|
+
# client.user_agent '...'
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# # Pass in custom configuration to the Faraday connection
|
48
|
+
# Asana::Client.new do |client|
|
49
|
+
# client.authentication ...
|
50
|
+
# client.configure_faraday { |conn| conn.use MyMiddleware }
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
class Client
|
54
|
+
# Internal: Proxies Resource classes to implement a fluent API on the Client
|
55
|
+
# instances.
|
56
|
+
class ResourceProxy
|
57
|
+
def initialize(client: required('client'), resource: required('resource'))
|
58
|
+
@client = client
|
59
|
+
@resource = resource
|
60
|
+
end
|
61
|
+
|
62
|
+
def method_missing(m, *args, &block)
|
63
|
+
@resource.public_send(m, *([@client] + args), &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def respond_to_missing?(m, *)
|
67
|
+
@resource.respond_to?(m)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Public: Initializes a new client.
|
72
|
+
#
|
73
|
+
# Yields a {Asana::Client::Configuration} object as a configuration
|
74
|
+
# DSL. See {Asana::Client} for usage examples.
|
75
|
+
def initialize
|
76
|
+
config = Configuration.new.tap { |c| yield c }.to_h
|
77
|
+
@http_client =
|
78
|
+
HttpClient.new(authentication: config.fetch(:authentication),
|
79
|
+
adapter: config[:faraday_adapter],
|
80
|
+
user_agent: config[:user_agent],
|
81
|
+
debug_mode: config[:debug_mode],
|
82
|
+
&config[:faraday_config])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Public: Performs a GET request against an arbitrary Asana URL. Allows for
|
86
|
+
# the user to interact with the API in ways that haven't been
|
87
|
+
# reflected/foreseen in this library.
|
88
|
+
def get(url, *args)
|
89
|
+
@http_client.get(url, *args)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Public: Performs a POST request against an arbitrary Asana URL. Allows for
|
93
|
+
# the user to interact with the API in ways that haven't been
|
94
|
+
# reflected/foreseen in this library.
|
95
|
+
def post(url, *args)
|
96
|
+
@http_client.post(url, *args)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Public: Performs a PUT request against an arbitrary Asana URL. Allows for
|
100
|
+
# the user to interact with the API in ways that haven't been
|
101
|
+
# reflected/foreseen in this library.
|
102
|
+
def put(url, *args)
|
103
|
+
@http_client.put(url, *args)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Public: Performs a DELETE request against an arbitrary Asana URL. Allows
|
107
|
+
# for the user to interact with the API in ways that haven't been
|
108
|
+
# reflected/foreseen in this library.
|
109
|
+
def delete(url, *args)
|
110
|
+
@http_client.delete(url, *args)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Public: Exposes queries for all top-evel endpoints.
|
114
|
+
#
|
115
|
+
# E.g. #users will query /users and return a
|
116
|
+
# Asana::Resources::Collection<User>.
|
117
|
+
Resources::Registry.resources.each do |resource_class|
|
118
|
+
define_method(resource_class.plural_name) do
|
119
|
+
ResourceProxy.new(client: @http_client,
|
120
|
+
resource: resource_class)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Asana
|
2
|
+
class Client
|
3
|
+
# Internal: Represents a configuration DSL for an Asana::Client.
|
4
|
+
#
|
5
|
+
# Examples
|
6
|
+
#
|
7
|
+
# config = Configuration.new
|
8
|
+
# config.authentication :api_token, 'my_api_token'
|
9
|
+
# config.adapter :typhoeus
|
10
|
+
# config.configure_faraday { |conn| conn.use MyMiddleware }
|
11
|
+
# config.to_h
|
12
|
+
# # => { authentication: #<Authentication::TokenAuthentication>,
|
13
|
+
# faraday_adapter: :typhoeus,
|
14
|
+
# faraday_configuration: #<Proc> }
|
15
|
+
#
|
16
|
+
class Configuration
|
17
|
+
# Public: Initializes an empty configuration object.
|
18
|
+
def initialize
|
19
|
+
@configuration = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Sets an authentication strategy.
|
23
|
+
#
|
24
|
+
# type - [:oauth2, :api_token] the kind of authentication strategy to use
|
25
|
+
# value - [::OAuth2::AccessToken, String, Hash] the configuration for the
|
26
|
+
# chosen authentication strategy.
|
27
|
+
#
|
28
|
+
# Returns nothing.
|
29
|
+
#
|
30
|
+
# Raises ArgumentError if the arguments are invalid.
|
31
|
+
def authentication(type, value)
|
32
|
+
auth = case type
|
33
|
+
when :oauth2 then oauth2(value)
|
34
|
+
when :api_token then api_token(value)
|
35
|
+
else error "unsupported authentication type #{type}"
|
36
|
+
end
|
37
|
+
@configuration[:authentication] = auth
|
38
|
+
end
|
39
|
+
|
40
|
+
# Public: Sets a custom network adapter for Faraday.
|
41
|
+
#
|
42
|
+
# adapter - [Symbol, Proc] the adapter.
|
43
|
+
#
|
44
|
+
# Returns nothing.
|
45
|
+
def faraday_adapter(adapter)
|
46
|
+
@configuration[:faraday_adapter] = adapter
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Sets a custom configuration block for the Faraday connection.
|
50
|
+
#
|
51
|
+
# config - [Proc] the configuration block.
|
52
|
+
#
|
53
|
+
# Returns nothing.
|
54
|
+
def configure_faraday(&config)
|
55
|
+
@configuration[:faraday_configuration] = config
|
56
|
+
end
|
57
|
+
|
58
|
+
# Public: Configures the client in debug mode, which will print verbose
|
59
|
+
# information on STDERR.
|
60
|
+
#
|
61
|
+
# Returns nothing.
|
62
|
+
def debug_mode
|
63
|
+
@configuration[:debug_mode] = true
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public:
|
67
|
+
# Returns the configuration [Hash].
|
68
|
+
def to_h
|
69
|
+
@configuration
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Internal: Configures an OAuth2 authentication strategy from either an
|
75
|
+
# OAuth2 access token object, or a plain refresh token, or a plain bearer
|
76
|
+
# token.
|
77
|
+
#
|
78
|
+
# value - [::OAuth::AccessToken, String] the value to configure the
|
79
|
+
# strategy from.
|
80
|
+
#
|
81
|
+
# Returns [Asana::Authentication::OAuth2::AccessTokenAuthentication,
|
82
|
+
# Asana::Authentication::OAuth2::BearerTokenAuthentication]
|
83
|
+
# the OAuth2 authentication strategy.
|
84
|
+
#
|
85
|
+
# Raises ArgumentError if the OAuth2 configuration arguments are invalid.
|
86
|
+
#
|
87
|
+
# rubocop:disable Metrics/MethodLength
|
88
|
+
def oauth2(value)
|
89
|
+
case value
|
90
|
+
when ::OAuth2::AccessToken
|
91
|
+
from_access_token(value)
|
92
|
+
when -> v { v.is_a?(Hash) && v[:refresh_token] }
|
93
|
+
from_refresh_token(value)
|
94
|
+
when -> v { v.is_a?(Hash) && v[:bearer_token] }
|
95
|
+
from_bearer_token(value[:bearer_token])
|
96
|
+
else
|
97
|
+
error 'Invalid OAuth2 configuration: pass in either an ' \
|
98
|
+
'::OAuth2::AccessToken object of your own or a hash ' \
|
99
|
+
'containing :refresh_token or :bearer_token.'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
# rubocop:enable Metrics/MethodLength
|
103
|
+
|
104
|
+
# Internal: Configures a TokenAuthentication strategy.
|
105
|
+
#
|
106
|
+
# token - [String] the API token
|
107
|
+
#
|
108
|
+
# Returns a [Authentication::TokenAuthentication] strategy.
|
109
|
+
def api_token(token)
|
110
|
+
Authentication::TokenAuthentication.new(token)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Internal: Configures an OAuth2 AccessTokenAuthentication strategy.
|
114
|
+
#
|
115
|
+
# access_token - [::OAuth2::AccessToken] the OAuth2 access token object
|
116
|
+
#
|
117
|
+
# Returns a [Authentication::OAuth2::AccessTokenAuthentication] strategy.
|
118
|
+
def from_access_token(access_token)
|
119
|
+
Authentication::OAuth2::AccessTokenAuthentication
|
120
|
+
.new(access_token)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Internal: Configures an OAuth2 AccessTokenAuthentication strategy.
|
124
|
+
#
|
125
|
+
# hash - The configuration hash:
|
126
|
+
# :refresh_token - [String] the OAuth2 refresh token
|
127
|
+
# :client_id - [String] the OAuth2 client id
|
128
|
+
# :client_secret - [String] the OAuth2 client secret
|
129
|
+
# :redirect_uri - [String] the OAuth2 redirect URI
|
130
|
+
#
|
131
|
+
# Returns a [Authentication::OAuth2::AccessTokenAuthentication] strategy.
|
132
|
+
def from_refresh_token(hash)
|
133
|
+
refresh_token, client_id, client_secret, redirect_uri =
|
134
|
+
requiring(hash, :refresh_token, :client_id,
|
135
|
+
:client_secret, :redirect_uri)
|
136
|
+
|
137
|
+
Authentication::OAuth2::AccessTokenAuthentication
|
138
|
+
.from_refresh_token(refresh_token,
|
139
|
+
client_id: client_id,
|
140
|
+
client_secret: client_secret,
|
141
|
+
redirect_uri: redirect_uri)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Internal: Configures an OAuth2 BearerTokenAuthentication strategy.
|
145
|
+
#
|
146
|
+
# bearer_token - [String] the plain OAuth2 bearer token
|
147
|
+
#
|
148
|
+
# Returns a [Authentication::OAuth2::BearerTokenAuthentication] strategy.
|
149
|
+
def from_bearer_token(bearer_token)
|
150
|
+
Authentication::OAuth2::BearerTokenAuthentication
|
151
|
+
.new(bearer_token)
|
152
|
+
end
|
153
|
+
|
154
|
+
def requiring(hash, *keys)
|
155
|
+
missing_keys = keys.select { |k| !hash.key?(k) }
|
156
|
+
missing_keys.any? && error("Missing keys: #{missing_keys.join(', ')}")
|
157
|
+
keys.map { |k| hash[k] }
|
158
|
+
end
|
159
|
+
|
160
|
+
def error(msg)
|
161
|
+
fail ArgumentError, msg
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/lib/asana/errors.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
module Asana
|
2
|
+
# Public: Defines the different errors that the Asana API may throw, which the
|
3
|
+
# client code may want to catch.
|
4
|
+
module Errors
|
5
|
+
# Public: A generic, catch-all API error. It contains the whole response
|
6
|
+
# object for debugging purposes.
|
7
|
+
#
|
8
|
+
# Note: This exception should never be raised when there exists a more
|
9
|
+
# specific subclass.
|
10
|
+
APIError = Class.new(StandardError) do
|
11
|
+
attr_accessor :response
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
'An unknown API error ocurred.'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: A 401 error. Raised when the credentials used are invalid and the
|
19
|
+
# user could not be authenticated.
|
20
|
+
NotAuthorized = Class.new(APIError) do
|
21
|
+
def to_s
|
22
|
+
'A valid API key was not provided with the request, so the API could '\
|
23
|
+
'not associate a user with the request.'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: A 403 error. Raised when the user doesn't have permission to
|
28
|
+
# access the requested resource or to perform the requested action on it.
|
29
|
+
Forbidden = Class.new(APIError) do
|
30
|
+
def to_s
|
31
|
+
'The API key and request syntax was valid but the server is refusing '\
|
32
|
+
'to complete the request. This can happen if you try to read or write '\
|
33
|
+
'to objects or properties that the user does not have access to.'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: A 404 error. Raised when the requested resource doesn't exist.
|
38
|
+
NotFound = Class.new(APIError) do
|
39
|
+
def to_s
|
40
|
+
'Either the request method and path supplied do not specify a known '\
|
41
|
+
'action in the API, or the object specified by the request does not '\
|
42
|
+
'exist.'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: A 500 error. Raised when there is a problem in the Asana API
|
47
|
+
# server. It contains a unique phrase that can be used to identify the
|
48
|
+
# problem when contacting developer support.
|
49
|
+
ServerError = Class.new(APIError) do
|
50
|
+
attr_accessor :phrase
|
51
|
+
|
52
|
+
def initialize(phrase)
|
53
|
+
@phrase = phrase
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
"There has been an error on Asana's end. Use this unique phrase to "\
|
58
|
+
'identify the problem when contacting developer support: ' +
|
59
|
+
%("#{@phrase}")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public: A 400 error. Raised when the request was malformed or missing some
|
64
|
+
# parameters. It contains a list of errors indicating the specific problems.
|
65
|
+
InvalidRequest = Class.new(APIError) do
|
66
|
+
attr_accessor :errors
|
67
|
+
|
68
|
+
def initialize(errors)
|
69
|
+
@errors = errors
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
errors.join(', ')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Public: A 429 error. Raised when the Asana API enforces rate-limiting on
|
78
|
+
# the client to avoid overload. It contains the number of seconds to wait
|
79
|
+
# before retrying the operation.
|
80
|
+
RateLimitEnforced = Class.new(APIError) do
|
81
|
+
attr_accessor :retry_after_seconds
|
82
|
+
|
83
|
+
def initialize(retry_after_seconds)
|
84
|
+
@retry_after_seconds = retry_after_seconds
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_s
|
88
|
+
"Retry your request after #{@retry_after_seconds} seconds."
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'faraday_middleware/multi_json'
|
4
|
+
|
5
|
+
require_relative 'http_client/error_handling'
|
6
|
+
require_relative 'http_client/environment_info'
|
7
|
+
require_relative 'http_client/response'
|
8
|
+
|
9
|
+
module Asana
|
10
|
+
# Internal: Wrapper over Faraday that abstracts authentication, request
|
11
|
+
# parsing and common options.
|
12
|
+
class HttpClient
|
13
|
+
# Internal: The API base URI.
|
14
|
+
BASE_URI = 'https://app.asana.com/api/1.0'
|
15
|
+
|
16
|
+
# Public: Initializes an HttpClient to make requests to the Asana API.
|
17
|
+
#
|
18
|
+
# authentication - [Asana::Authentication] An authentication strategy.
|
19
|
+
# adapter - [Symbol, Proc] A Faraday adapter, eiter a Symbol for
|
20
|
+
# registered adapters or a Proc taking a builder for a
|
21
|
+
# custom one. Defaults to Faraday.default_adapter.
|
22
|
+
# user_agent - [String] The user agent. Defaults to "ruby-asana vX.Y.Z".
|
23
|
+
# config - [Proc] An optional block that yields the Faraday builder
|
24
|
+
# object for customization.
|
25
|
+
def initialize(authentication: required('authentication'),
|
26
|
+
adapter: nil,
|
27
|
+
user_agent: nil,
|
28
|
+
debug_mode: false,
|
29
|
+
&config)
|
30
|
+
@authentication = authentication
|
31
|
+
@adapter = adapter || Faraday.default_adapter
|
32
|
+
@environment_info = EnvironmentInfo.new(user_agent)
|
33
|
+
@debug_mode = debug_mode
|
34
|
+
@config = config
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Performs a GET request against the API.
|
38
|
+
#
|
39
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
40
|
+
# URL, e.g "/users/me".
|
41
|
+
# params - [Hash] the request parameters
|
42
|
+
# options - [Hash] the request I/O options
|
43
|
+
#
|
44
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
45
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
46
|
+
def get(resource_uri, params: {}, options: {})
|
47
|
+
opts = options.reduce({}) do |acc, (k, v)|
|
48
|
+
acc.tap do |hash|
|
49
|
+
hash[:"opt_#{k}"] = v.is_a?(Array) ? v.join(',') : v
|
50
|
+
end
|
51
|
+
end
|
52
|
+
perform_request(:get, resource_uri, params.merge(opts))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Public: Performs a PUT request against the API.
|
56
|
+
#
|
57
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
58
|
+
# URL, e.g "/users/me".
|
59
|
+
# body - [Hash] the body to PUT.
|
60
|
+
# options - [Hash] the request I/O options
|
61
|
+
#
|
62
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
63
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
64
|
+
def put(resource_uri, body: {}, options: {})
|
65
|
+
params = { data: body }.merge(options.empty? ? {} : { options: options })
|
66
|
+
perform_request(:put, resource_uri, params)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: Performs a POST request against the API.
|
70
|
+
#
|
71
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
72
|
+
# URL, e.g "/tags".
|
73
|
+
# body - [Hash] the body to POST.
|
74
|
+
# upload - [Faraday::UploadIO] an upload object to post as multipart.
|
75
|
+
# Defaults to nil.
|
76
|
+
# options - [Hash] the request I/O options
|
77
|
+
#
|
78
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
79
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
80
|
+
def post(resource_uri, body: {}, upload: nil, options: {})
|
81
|
+
params = { data: body }.merge(options.empty? ? {} : { options: options })
|
82
|
+
if upload
|
83
|
+
perform_request(:post, resource_uri, params.merge(file: upload)) do |c|
|
84
|
+
c.request :multipart
|
85
|
+
end
|
86
|
+
else
|
87
|
+
perform_request(:post, resource_uri, params)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Performs a DELETE request against the API.
|
92
|
+
#
|
93
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
94
|
+
# URL, e.g "/tags".
|
95
|
+
#
|
96
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
97
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
98
|
+
def delete(resource_uri)
|
99
|
+
perform_request(:delete, resource_uri)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def connection(&request_config)
|
105
|
+
Faraday.new do |builder|
|
106
|
+
@authentication.configure(builder)
|
107
|
+
@environment_info.configure(builder)
|
108
|
+
request_config.call(builder) if request_config
|
109
|
+
configure_format(builder)
|
110
|
+
add_middleware(builder)
|
111
|
+
@config.call(builder) if @config
|
112
|
+
use_adapter(builder, @adapter)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def perform_request(method, resource_uri, body = {}, &request_config)
|
117
|
+
handling_errors do
|
118
|
+
url = BASE_URI + resource_uri
|
119
|
+
log_request(method, url, body) if @debug_mode
|
120
|
+
Response.new(connection(&request_config).public_send(method, url, body))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def configure_format(builder)
|
125
|
+
builder.request :multi_json
|
126
|
+
builder.response :multi_json
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_middleware(builder)
|
130
|
+
builder.use Faraday::Response::RaiseError
|
131
|
+
builder.use FaradayMiddleware::FollowRedirects
|
132
|
+
end
|
133
|
+
|
134
|
+
def use_adapter(builder, adapter)
|
135
|
+
case adapter
|
136
|
+
when Symbol
|
137
|
+
builder.adapter(adapter)
|
138
|
+
when Proc
|
139
|
+
adapter.call(builder)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def handling_errors(&request)
|
144
|
+
ErrorHandling.handle(&request)
|
145
|
+
end
|
146
|
+
|
147
|
+
def log_request(method, url, body)
|
148
|
+
STDERR.puts format('[%s] %s %s (%s)',
|
149
|
+
self.class,
|
150
|
+
method.to_s.upcase,
|
151
|
+
url,
|
152
|
+
body.inspect)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|