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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +4 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +11 -0
  6. data/.travis.yml +12 -0
  7. data/.yardopts +5 -0
  8. data/CODE_OF_CONDUCT.md +13 -0
  9. data/Gemfile +21 -0
  10. data/Guardfile +86 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +355 -0
  13. data/Rakefile +65 -0
  14. data/examples/Gemfile +6 -0
  15. data/examples/Gemfile.lock +59 -0
  16. data/examples/api_token.rb +21 -0
  17. data/examples/cli_app.rb +25 -0
  18. data/examples/events.rb +38 -0
  19. data/examples/omniauth_integration.rb +54 -0
  20. data/lib/asana.rb +12 -0
  21. data/lib/asana/authentication.rb +8 -0
  22. data/lib/asana/authentication/oauth2.rb +42 -0
  23. data/lib/asana/authentication/oauth2/access_token_authentication.rb +51 -0
  24. data/lib/asana/authentication/oauth2/bearer_token_authentication.rb +32 -0
  25. data/lib/asana/authentication/oauth2/client.rb +50 -0
  26. data/lib/asana/authentication/token_authentication.rb +20 -0
  27. data/lib/asana/client.rb +124 -0
  28. data/lib/asana/client/configuration.rb +165 -0
  29. data/lib/asana/errors.rb +92 -0
  30. data/lib/asana/http_client.rb +155 -0
  31. data/lib/asana/http_client/environment_info.rb +53 -0
  32. data/lib/asana/http_client/error_handling.rb +103 -0
  33. data/lib/asana/http_client/response.rb +32 -0
  34. data/lib/asana/resource_includes/attachment_uploading.rb +33 -0
  35. data/lib/asana/resource_includes/collection.rb +68 -0
  36. data/lib/asana/resource_includes/event.rb +51 -0
  37. data/lib/asana/resource_includes/event_subscription.rb +14 -0
  38. data/lib/asana/resource_includes/events.rb +103 -0
  39. data/lib/asana/resource_includes/registry.rb +63 -0
  40. data/lib/asana/resource_includes/resource.rb +103 -0
  41. data/lib/asana/resource_includes/response_helper.rb +14 -0
  42. data/lib/asana/resources.rb +14 -0
  43. data/lib/asana/resources/attachment.rb +44 -0
  44. data/lib/asana/resources/project.rb +154 -0
  45. data/lib/asana/resources/story.rb +64 -0
  46. data/lib/asana/resources/tag.rb +120 -0
  47. data/lib/asana/resources/task.rb +300 -0
  48. data/lib/asana/resources/team.rb +55 -0
  49. data/lib/asana/resources/user.rb +72 -0
  50. data/lib/asana/resources/workspace.rb +91 -0
  51. data/lib/asana/ruby2_0_0_compatibility.rb +3 -0
  52. data/lib/asana/version.rb +5 -0
  53. data/lib/templates/index.js +8 -0
  54. data/lib/templates/resource.ejs +225 -0
  55. data/openc-asana.gemspec +32 -0
  56. data/package.json +7 -0
  57. metadata +200 -0
@@ -0,0 +1,63 @@
1
+ require_relative 'resource'
2
+ require 'set'
3
+
4
+ module Asana
5
+ module Resources
6
+ # Internal: Global registry of Resource subclasses. It provides lookup from
7
+ # singular and plural names to the actual class objects.
8
+ #
9
+ # Examples
10
+ #
11
+ # class Unicorn < Asana::Resources::Resource
12
+ # path '/unicorns'
13
+ # end
14
+ #
15
+ # Registry.lookup(:unicorn) # => Unicorn
16
+ # Registry.lookup_many(:unicorns) # => Unicorn
17
+ #
18
+ module Registry
19
+ class << self
20
+ # Public: Registers a new resource class.
21
+ #
22
+ # resource_klass - [Class] the resource class.
23
+ #
24
+ # Returns nothing.
25
+ def register(resource_klass)
26
+ resources << resource_klass
27
+ end
28
+
29
+ # Public: Looks up a resource class by its singular name.
30
+ #
31
+ # singular_name - [#to_s] the name of the resource, e.g :unicorn.
32
+ #
33
+ # Returns the resource class or {Asana::Resources::Resource}.
34
+ def lookup(singular_name)
35
+ resources.detect do |klass|
36
+ klass.singular_name.to_s == singular_name.to_s
37
+ end || Resource
38
+ end
39
+
40
+ # Public: Looks up a resource class by its plural name.
41
+ #
42
+ # plural_name - [#to_s] the plural name of the resource, e.g :unicorns.
43
+ #
44
+ # Returns the resource class or {Asana::Resources::Resource}.
45
+ def lookup_many(plural_name)
46
+ resources.detect do |klass|
47
+ klass.plural_name.to_s == plural_name.to_s
48
+ end || Resource
49
+ end
50
+
51
+ # Internal: A set of Resource classes.
52
+ #
53
+ # Returns the Set, defaulting to the empty set.
54
+ #
55
+ # Note: this object is a mutable singleton, so it should not be accessed
56
+ # from multiple threads.
57
+ def resources
58
+ @resources ||= Set.new
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,103 @@
1
+ require_relative 'registry'
2
+ require_relative 'response_helper'
3
+
4
+ module Asana
5
+ module Resources
6
+ # Public: The base resource class which provides some sugar over common
7
+ # resource functionality.
8
+ class Resource
9
+ include ResponseHelper
10
+ extend ResponseHelper
11
+
12
+ def self.inherited(base)
13
+ Registry.register(base)
14
+ end
15
+
16
+ def initialize(data, client: required('client'))
17
+ @_client = client
18
+ @_data = data
19
+ data.each do |k, v|
20
+ instance_variable_set(:"@#{k}", v) if respond_to?(k)
21
+ end
22
+ end
23
+
24
+ # If it has findById, it implements #refresh
25
+ def refresh
26
+ if self.class.respond_to?(:find_by_id)
27
+ self.class.find_by_id(client, id)
28
+ else
29
+ fail "#{self.class.name} does not respond to #find_by_id"
30
+ end
31
+ end
32
+
33
+ # Internal: Proxies method calls to the data, wrapping it accordingly and
34
+ # caching the result by defining a real reader method.
35
+ #
36
+ # Returns the value for the requested property.
37
+ #
38
+ # Raises a NoMethodError if the property doesn't exist.
39
+ def method_missing(m, *args)
40
+ super unless respond_to_missing?(m, *args)
41
+ cache(m, wrapped(to_h[m.to_s]))
42
+ end
43
+
44
+ # Internal: Guard for the method_missing proxy. Checks if the resource
45
+ # actually has a specific piece of data at all.
46
+ #
47
+ # Returns true if the resource has the property, false otherwise.
48
+ def respond_to_missing?(m, *)
49
+ to_h.key?(m.to_s)
50
+ end
51
+
52
+ # Public:
53
+ # Returns the raw Hash representation of the data.
54
+ def to_h
55
+ @_data
56
+ end
57
+
58
+ def to_s
59
+ attrs = to_h.map { |k, _| "#{k}: #{public_send(k).inspect}" }.join(', ')
60
+ "#<Asana::#{self.class.name.split('::').last} #{attrs}>"
61
+ end
62
+
63
+ alias_method :inspect, :to_s
64
+
65
+ private
66
+
67
+ # Internal: The Asana::Client instance.
68
+ def client
69
+ @_client
70
+ end
71
+
72
+ # Internal: Caches a property and a value by defining a reader method for
73
+ # it.
74
+ #
75
+ # property - [#to_s] the property
76
+ # value - [Object] the corresponding value
77
+ #
78
+ # Returns the value.
79
+ def cache(property, value)
80
+ field = :"@#{property}"
81
+ instance_variable_set(field, value)
82
+ define_singleton_method(property) { instance_variable_get(field) }
83
+ value
84
+ end
85
+
86
+ # Internal: Wraps a value in a more useful class if possible, namely a
87
+ # Resource or a Collection.
88
+ #
89
+ # Returns the wrapped value or the plain value if it couldn't be wrapped.
90
+ def wrapped(value)
91
+ case value
92
+ when Hash then Resource.new(value, client: client)
93
+ when Array then value.map(&method(:wrapped))
94
+ else value
95
+ end
96
+ end
97
+
98
+ def refresh_with(data)
99
+ self.class.new(data, client: @client)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,14 @@
1
+ module Asana
2
+ module Resources
3
+ # Internal: A helper to make response body parsing easier.
4
+ module ResponseHelper
5
+ def parse(response)
6
+ data = response.body.fetch('data') do
7
+ fail("Unexpected response body: #{response.body}")
8
+ end
9
+ extra = response.body.reject { |k, _| k == 'data' }
10
+ [data, extra]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'resource_includes/resource'
2
+ require_relative 'resource_includes/collection'
3
+
4
+ Dir[File.join(File.dirname(__FILE__), 'resource_includes', '*.rb')]
5
+ .each { |resource| require resource }
6
+
7
+ Dir[File.join(File.dirname(__FILE__), 'resources', '*.rb')]
8
+ .each { |resource| require resource }
9
+
10
+ module Asana
11
+ # Public: Contains all the resources that the Asana API can return.
12
+ module Resources
13
+ end
14
+ end
@@ -0,0 +1,44 @@
1
+ ### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
2
+ ### edit it manually.
3
+
4
+ module Asana
5
+ module Resources
6
+ # An _attachment_ object represents any file attached to a task in Asana,
7
+ # whether it's an uploaded file or one associated via a third-party service
8
+ # such as Dropbox or Google Drive.
9
+ class Attachment < Resource
10
+
11
+
12
+ attr_reader :id
13
+
14
+ class << self
15
+ # Returns the plural name of the resource.
16
+ def plural_name
17
+ 'attachments'
18
+ end
19
+
20
+ # Returns the full record for a single attachment.
21
+ #
22
+ # id - [Id] Globally unique identifier for the attachment.
23
+ #
24
+ # options - [Hash] the request I/O options.
25
+ def find_by_id(client, id, options: {})
26
+
27
+ self.new(parse(client.get("/attachments/#{id}", options: options)).first, client: client)
28
+ end
29
+
30
+ # Returns the compact records for all attachments on the task.
31
+ #
32
+ # task - [Id] Globally unique identifier for the task.
33
+ #
34
+ # per_page - [Integer] the number of records to fetch per page.
35
+ # options - [Hash] the request I/O options.
36
+ def find_by_task(client, task: required("task"), per_page: 20, options: {})
37
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
38
+ Collection.new(parse(client.get("/tasks/#{task}/attachments", params: params, options: options)), type: self, client: client)
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,154 @@
1
+ ### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
2
+ ### edit it manually.
3
+
4
+ module Asana
5
+ module Resources
6
+ # A _project_ represents a prioritized list of tasks in Asana. It exists in a
7
+ # single workspace or organization and is accessible to a subset of users in
8
+ # that workspace or organization, depending on its permissions.
9
+ #
10
+ # Projects in organizations are shared with a single team. You cannot currently
11
+ # change the team of a project via the API. Non-organization workspaces do not
12
+ # have teams and so you should not specify the team of project in a
13
+ # regular workspace.
14
+ class Project < Resource
15
+
16
+ include EventSubscription
17
+
18
+
19
+ attr_reader :id
20
+
21
+ class << self
22
+ # Returns the plural name of the resource.
23
+ def plural_name
24
+ 'projects'
25
+ end
26
+
27
+ # Creates a new project in a workspace or team.
28
+ #
29
+ # Every project is required to be created in a specific workspace or
30
+ # organization, and this cannot be changed once set. Note that you can use
31
+ # the `workspace` parameter regardless of whether or not it is an
32
+ # organization.
33
+ #
34
+ # If the workspace for your project _is_ an organization, you must also
35
+ # supply a `team` to share the project with.
36
+ #
37
+ # Returns the full record of the newly created project.
38
+ #
39
+ # workspace - [Id] The workspace or organization to create the project in.
40
+ # team - [Id] If creating in an organization, the specific team to create the
41
+ # project in.
42
+ #
43
+ # options - [Hash] the request I/O options.
44
+ # data - [Hash] the attributes to post.
45
+ def create(client, workspace: required("workspace"), team: nil, options: {}, **data)
46
+ with_params = data.merge(workspace: workspace, team: team).reject { |_,v| v.nil? || Array(v).empty? }
47
+ self.new(parse(client.post("/projects", body: with_params, options: options)).first, client: client)
48
+ end
49
+
50
+ # If the workspace for your project _is_ an organization, you must also
51
+ # supply a `team` to share the project with.
52
+ #
53
+ # Returns the full record of the newly created project.
54
+ #
55
+ # workspace - [Id] The workspace or organization to create the project in.
56
+ # options - [Hash] the request I/O options.
57
+ # data - [Hash] the attributes to post.
58
+ def create_in_workspace(client, workspace: required("workspace"), options: {}, **data)
59
+
60
+ self.new(parse(client.post("/workspaces/#{workspace}/projects", body: data, options: options)).first, client: client)
61
+ end
62
+
63
+ # Creates a project shared with the given team.
64
+ #
65
+ # Returns the full record of the newly created project.
66
+ #
67
+ # team - [Id] The team to create the project in.
68
+ # options - [Hash] the request I/O options.
69
+ # data - [Hash] the attributes to post.
70
+ def create_in_team(client, team: required("team"), options: {}, **data)
71
+
72
+ self.new(parse(client.post("/teams/#{team}/projects", body: data, options: options)).first, client: client)
73
+ end
74
+
75
+ # Returns the complete project record for a single project.
76
+ #
77
+ # id - [Id] The project to get.
78
+ # options - [Hash] the request I/O options.
79
+ def find_by_id(client, id, options: {})
80
+
81
+ self.new(parse(client.get("/projects/#{id}", options: options)).first, client: client)
82
+ end
83
+
84
+ # Returns the compact project records for some filtered set of projects.
85
+ # Use one or more of the parameters provided to filter the projects returned.
86
+ #
87
+ # workspace - [Id] The workspace or organization to filter projects on.
88
+ # team - [Id] The team to filter projects on.
89
+ # archived - [Boolean] Only return projects whose `archived` field takes on the value of
90
+ # this parameter.
91
+ #
92
+ # per_page - [Integer] the number of records to fetch per page.
93
+ # options - [Hash] the request I/O options.
94
+ def find_all(client, workspace: nil, team: nil, archived: nil, per_page: 20, options: {})
95
+ params = { workspace: workspace, team: team, archived: archived, limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
96
+ Collection.new(parse(client.get("/projects", params: params, options: options)), type: self, client: client)
97
+ end
98
+
99
+ # Returns the compact project records for all projects in the workspace.
100
+ #
101
+ # workspace - [Id] The workspace or organization to find projects in.
102
+ # archived - [Boolean] Only return projects whose `archived` field takes on the value of
103
+ # this parameter.
104
+ #
105
+ # per_page - [Integer] the number of records to fetch per page.
106
+ # options - [Hash] the request I/O options.
107
+ def find_by_workspace(client, workspace: required("workspace"), archived: nil, per_page: 20, options: {})
108
+ params = { archived: archived, limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
109
+ Collection.new(parse(client.get("/workspaces/#{workspace}/projects", params: params, options: options)), type: self, client: client)
110
+ end
111
+
112
+ # Returns the compact project records for all projects in the team.
113
+ #
114
+ # team - [Id] The team to find projects in.
115
+ # archived - [Boolean] Only return projects whose `archived` field takes on the value of
116
+ # this parameter.
117
+ #
118
+ # per_page - [Integer] the number of records to fetch per page.
119
+ # options - [Hash] the request I/O options.
120
+ def find_by_team(client, team: required("team"), archived: nil, per_page: 20, options: {})
121
+ params = { archived: archived, limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
122
+ Collection.new(parse(client.get("/teams/#{team}/projects", params: params, options: options)), type: self, client: client)
123
+ end
124
+ end
125
+
126
+ # A specific, existing project can be updated by making a PUT request on the
127
+ # URL for that project. Only the fields provided in the `data` block will be
128
+ # updated; any unspecified fields will remain unchanged.
129
+ #
130
+ # When using this method, it is best to specify only those fields you wish
131
+ # to change, or else you may overwrite changes made by another user since
132
+ # you last retrieved the task.
133
+ #
134
+ # Returns the complete updated project record.
135
+ #
136
+ # options - [Hash] the request I/O options.
137
+ # data - [Hash] the attributes to post.
138
+ def update(options: {}, **data)
139
+
140
+ refresh_with(parse(client.put("/projects/#{id}", body: data, options: options)).first)
141
+ end
142
+
143
+ # A specific, existing project can be deleted by making a DELETE request
144
+ # on the URL for that project.
145
+ #
146
+ # Returns an empty data record.
147
+ def delete()
148
+
149
+ client.delete("/projects/#{id}") && true
150
+ end
151
+
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,64 @@
1
+ ### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
2
+ ### edit it manually.
3
+
4
+ module Asana
5
+ module Resources
6
+ # A _story_ represents an activity associated with an object in the Asana
7
+ # system. Stories are generated by the system whenever users take actions such
8
+ # as creating or assigning tasks, or moving tasks between projects. _Comments_
9
+ # are also a form of user-generated story.
10
+ #
11
+ # Stories are a form of history in the system, and as such they are read-only.
12
+ # Once generated, it is not possible to modify a story.
13
+ class Story < Resource
14
+
15
+
16
+ attr_reader :id
17
+
18
+ class << self
19
+ # Returns the plural name of the resource.
20
+ def plural_name
21
+ 'stories'
22
+ end
23
+
24
+ # Returns the full record for a single story.
25
+ #
26
+ # id - [Id] Globally unique identifier for the team.
27
+ #
28
+ # options - [Hash] the request I/O options.
29
+ def find_by_id(client, id, options: {})
30
+
31
+ self.new(parse(client.get("/stories/#{id}", options: options)).first, client: client)
32
+ end
33
+
34
+ # Returns the compact records for all stories on the task.
35
+ #
36
+ # task - [Id] Globally unique identifier for the task.
37
+ #
38
+ # per_page - [Integer] the number of records to fetch per page.
39
+ # options - [Hash] the request I/O options.
40
+ def find_by_task(client, task: required("task"), per_page: 20, options: {})
41
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
42
+ Collection.new(parse(client.get("/tasks/#{task}/stories", params: params, options: options)), type: self, client: client)
43
+ end
44
+
45
+ # Adds a comment to a task. The comment will be authored by the
46
+ # currently authenticated user, and timestamped when the server receives
47
+ # the request.
48
+ #
49
+ # Returns the full record for the new story added to the task.
50
+ #
51
+ # task - [Id] Globally unique identifier for the task.
52
+ #
53
+ # text - [String] The plain text of the comment to add.
54
+ # options - [Hash] the request I/O options.
55
+ # data - [Hash] the attributes to post.
56
+ def create_on_task(client, task: required("task"), text: required("text"), options: {}, **data)
57
+ with_params = data.merge(text: text).reject { |_,v| v.nil? || Array(v).empty? }
58
+ self.new(parse(client.post("/tasks/#{task}/stories", body: with_params, options: options)).first, client: client)
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end