openc-asana 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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,72 @@
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 _user_ object represents an account in Asana that can be given access to
7
+ # various workspaces, projects, and tasks.
8
+ #
9
+ # Like other objects in the system, users are referred to by numerical IDs.
10
+ # However, the special string identifier `me` can be used anywhere
11
+ # a user ID is accepted, to refer to the current authenticated user.
12
+ class User < Resource
13
+
14
+
15
+ attr_reader :id
16
+
17
+ attr_reader :email
18
+
19
+ attr_reader :photo
20
+
21
+ attr_reader :workspaces
22
+
23
+ class << self
24
+ # Returns the plural name of the resource.
25
+ def plural_name
26
+ 'users'
27
+ end
28
+
29
+ # Returns the full user record for the currently authenticated user.
30
+ #
31
+ # options - [Hash] the request I/O options.
32
+ def me(client, options: {})
33
+
34
+ Resource.new(parse(client.get("/users/me", options: options)).first, client: client)
35
+ end
36
+
37
+ # Returns the full user record for a single user.
38
+ #
39
+ # id - [Id] Globally unique identifier for the user.
40
+ #
41
+ # options - [Hash] the request I/O options.
42
+ def find_by_id(client, id, options: {})
43
+
44
+ self.new(parse(client.get("/users/#{id}", options: options)).first, client: client)
45
+ end
46
+
47
+ # Returns the user records for all users in all workspaces and organizations
48
+ # accessible to the authenticated user.
49
+ #
50
+ # workspace - [Id] The workspace in which to get users.
51
+ # per_page - [Integer] the number of records to fetch per page.
52
+ # options - [Hash] the request I/O options.
53
+ def find_by_workspace(client, workspace: required("workspace"), per_page: 20, options: {})
54
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
55
+ Collection.new(parse(client.get("/workspaces/#{workspace}/users", params: params, options: options)), type: self, client: client)
56
+ end
57
+
58
+ # Returns the user records for all users in the specified workspace or
59
+ # organization.
60
+ #
61
+ # workspace - [Id] The workspace or organization to filter users on.
62
+ # per_page - [Integer] the number of records to fetch per page.
63
+ # options - [Hash] the request I/O options.
64
+ def find_all(client, workspace: nil, per_page: 20, options: {})
65
+ params = { workspace: workspace, limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
66
+ Collection.new(parse(client.get("/users", params: params, options: options)), type: self, client: client)
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,91 @@
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 _workspace_ is the highest-level organizational unit in Asana. All projects
7
+ # and tasks have an associated workspace.
8
+ #
9
+ # An _organization_ is a special kind of workspace that represents a company.
10
+ # In an organization, you can group your projects into teams. You can read
11
+ # more about how organizations work on the Asana Guide.
12
+ # To tell if your workspace is an organization or not, check its
13
+ # `is_organization` property.
14
+ #
15
+ # Over time, we intend to migrate most workspaces into organizations and to
16
+ # release more organization-specific functionality. We may eventually deprecate
17
+ # using workspace-based APIs for organizations. Currently, and until after
18
+ # some reasonable grace period following any further announcements, you can
19
+ # still reference organizations in any `workspace` parameter.
20
+ class Workspace < Resource
21
+
22
+
23
+ attr_reader :id
24
+
25
+ attr_reader :name
26
+
27
+ attr_reader :is_organization
28
+
29
+ class << self
30
+ # Returns the plural name of the resource.
31
+ def plural_name
32
+ 'workspaces'
33
+ end
34
+
35
+ # Returns the full workspace record for a single workspace.
36
+ #
37
+ # id - [Id] Globally unique identifier for the workspace or organization.
38
+ #
39
+ # options - [Hash] the request I/O options.
40
+ def find_by_id(client, id, options: {})
41
+
42
+ self.new(parse(client.get("/workspaces/#{id}", options: options)).first, client: client)
43
+ end
44
+
45
+ # Returns the compact records for all workspaces visible to the authorized user.
46
+ #
47
+ # per_page - [Integer] the number of records to fetch per page.
48
+ # options - [Hash] the request I/O options.
49
+ def find_all(client, per_page: 20, options: {})
50
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
51
+ Collection.new(parse(client.get("/workspaces", params: params, options: options)), type: self, client: client)
52
+ end
53
+ end
54
+
55
+ # Update properties on a workspace. Returns the complete, updated workspace record.
56
+ #
57
+ # options - [Hash] the request I/O options.
58
+ # data - [Hash] the attributes to post.
59
+ def update(options: {}, **data)
60
+
61
+ refresh_with(parse(client.put("/workspaces/#{id}", body: data, options: options)).first)
62
+ end
63
+
64
+ # Retrieves objects in the workspace based on an auto-completion/typeahead
65
+ # search algorithm. This feature is meant to provide results quickly, so do
66
+ # not rely on this API to provide extremely accurate search results. The
67
+ # result set is limited to a single page of results with a maximum size,
68
+ # so you won't be able to fetch large numbers of results.
69
+ #
70
+ # type - [Enum] The type of values the typeahead should return.
71
+ # Note that unlike in the names of endpoints, the types listed here are
72
+ # in singular form (e.g. `task`). Using multiple types is not yet supported.
73
+ #
74
+ # query - [String] The string that will be used to search for relevant objects. If an
75
+ # empty string is passed in, the API will currently return an empty
76
+ # result set.
77
+ #
78
+ # count - [Number] The number of results to return. The default is `20` if this
79
+ # parameter is omitted, with a minimum of `1` and a maximum of `100`.
80
+ # If there are fewer results found than requested, all will be returned.
81
+ #
82
+ # per_page - [Integer] the number of records to fetch per page.
83
+ # options - [Hash] the request I/O options.
84
+ def typeahead(type: required("type"), query: nil, count: nil, per_page: 20, options: {})
85
+ params = { type: type, query: query, count: count, limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
86
+ Collection.new(parse(client.get("/workspaces/#{id}/typeahead", params: params, options: options)), type: Resource, client: client)
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ def required(name)
2
+ fail(ArgumentError, "#{name} is a required keyword argument")
3
+ end
@@ -0,0 +1,5 @@
1
+ #:nodoc:
2
+ module Asana
3
+ # Public: Version of the gem.
4
+ VERSION = '0.1.2'
5
+ end
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ resource: {
3
+ template: 'resource.ejs',
4
+ filename: function(resource, helpers) {
5
+ return resource.name + '.rb';
6
+ }
7
+ }
8
+ };
@@ -0,0 +1,225 @@
1
+ <%
2
+ var singularName = resource.name,
3
+ pluralName = plural(singularName),
4
+ mixins = {
5
+ task: ["AttachmentUploading", "EventSubscription"],
6
+ project: ["EventSubscription"]
7
+ },
8
+ skip = { attachment: ["createOnTask"] },
9
+ formatComment = function formatComment(text, indentation) {
10
+ var indent = Array(indentation + 1).join(" ")
11
+ return text.trim().split("\n").map(function(line) {
12
+ return indent + (line.length > 0 ? "# " : "#") + line
13
+ }).join("\n")
14
+ }
15
+
16
+
17
+ function Action(action) {
18
+ var that = this
19
+ this.action = action
20
+ this.collection = action.collection == true
21
+ this.requiresData = action.method == "POST" || action.method == "PUT"
22
+ this.isInstanceAction = (action.method == "PUT" || action.method == "DELETE")
23
+ this.method = action.method
24
+ this.methodName = snake(action.name)
25
+ this.clientMethod = action.method.toLowerCase()
26
+ this.returnsUpdatedRecord = action.method == 'PUT' || action.comment.match(/Returns[a-z\W]+updated/) !== null
27
+ this.returnsNothing = action.comment.match(/Returns an empty/) !== null
28
+
29
+ // Params and idParams
30
+ var params = action.params || []
31
+ this.idParams = _.filter(params, function(p) { return p.type == "Id" })
32
+
33
+ // If it looks like an instance action but it's not, make it one
34
+ if (!this.isInstanceAction) {
35
+ var mainIdParam = _.find(this.idParams, function(p) { return p.name == singularName })
36
+ if (mainIdParam !== undefined && !action.name.match(/Id/)) {
37
+ this.isInstanceAction = true
38
+ this.mainIdParam = mainIdParam
39
+ }
40
+ }
41
+
42
+ if (this.idParams.length == 1 &&
43
+ action.path.match(/%d/) &&
44
+ (action.name.match(/Id/) || (this.isInstanceAction && this.mainIdParam == undefined))) {
45
+ var mainIdParam = this.idParams[0]
46
+ this.mainIdParam = mainIdParam
47
+ this.inferredReturnType = this.isInstanceAction ? 'self.class' : 'self'
48
+ }
49
+
50
+ if (mainIdParam !== undefined) {
51
+ this.params = _.reject(params, function(p) { return p.name == mainIdParam.name })
52
+ } else {
53
+ this.params = params
54
+ }
55
+
56
+ if (!this.inferredReturnType) {
57
+ // Infer return type
58
+ var name = action.path.match(/\/([a-zA-Z]+)$/)
59
+ if (name !== null) {
60
+ name = name[1]
61
+
62
+ // Desugarize 'addProject' to 'project'
63
+ var camelCaseTail = name.match(/.*([A-Z][a-z]+)$/)
64
+ if (camelCaseTail !== null) { name = decap(camelCaseTail[1]) }
65
+
66
+ name = single(name)
67
+
68
+ var explicit = _.find(resources, function(p) { return p == name })
69
+
70
+ if (name == singularName || name == 'parent' || name == 'children' || name.match(/^sub/) !== null) {
71
+ this.inferredReturnType = this.isInstanceAction ? 'self.class' : 'self'
72
+ } else if (explicit !== undefined) {
73
+ this.inferredReturnType = cap(explicit)
74
+ } else {
75
+ this.inferredReturnType = 'Resource'
76
+ }
77
+ } else {
78
+ this.inferredReturnType = 'Resource'
79
+ }
80
+ }
81
+
82
+ // Endpoint path
83
+ this.path = _.reduce(this.idParams, function(acc, id) {
84
+ var localName = that.mainIdParam == id ? "id" : id.name
85
+ return acc.replace("\%d", "#{" + localName + "}")
86
+ }, action.path)
87
+
88
+ // Extra params (not in the URL) to be passed in the body of the call
89
+ this.extraParams = _.reject(this.params, function(p) {
90
+ return that.path.match(new RegExp("#{" + p.name + "}"))
91
+ })
92
+
93
+ // Params processing
94
+ var paramsLocal = "data"
95
+ if (this.collection) { this.extraParams.push({ name: "per_page", apiParamName: "limit" }) }
96
+ if (this.extraParams.length > 0) {
97
+ var paramNames = _.map(this.extraParams, function(p) { return (p.apiParamName || p.name) + ": " + p.name })
98
+ if (this.requiresData) {
99
+ var paramsProcessing = "with_params = data.merge(" + paramNames.join(", ") + ")"
100
+ paramsLocal = "with_params"
101
+ } else {
102
+ var paramsProcessing = "params = { " + paramNames.join(", ") + " }"
103
+ paramsLocal = "params"
104
+ }
105
+ paramsProcessing += ".reject { |_,v| v.nil? || Array(v).empty? }"
106
+ }
107
+
108
+ this.paramsProcessing = paramsProcessing
109
+ this.paramsLocal = paramsLocal
110
+
111
+ // Method argument names
112
+ var argumentNames = Array()
113
+ if (!this.isInstanceAction) { argumentNames.push("client") }
114
+ if (this.mainIdParam !== undefined && !this.isInstanceAction) { argumentNames.push("id") }
115
+ _.forEach(this.params, function(param) {
116
+ argumentNames.push(param.name + ":" + (param.required ? " required(\"" + param.name + "\")" : " nil"))
117
+ })
118
+ if (this.collection) { argumentNames.push("per_page: 20") }
119
+ if (this.method != 'DELETE') { argumentNames.push("options: {}") }
120
+ if (this.requiresData) { argumentNames.push("**data") }
121
+ this.argumentNames = argumentNames
122
+
123
+ // API request params
124
+ var requestParams = Array()
125
+ requestParams.push('"' + this.path + '"')
126
+ if (this.paramsProcessing || this.argumentNames.indexOf("**data") != -1) {
127
+ var argument = this.requiresData ? "body" : "params"
128
+ requestParams.push(argument + ": " + paramsLocal)
129
+ }
130
+ if (this.method != 'DELETE') { requestParams.push("options: options") }
131
+ this.requestParams = requestParams
132
+ this.documentation = this.renderDocumentation()
133
+
134
+ // Constructor
135
+ this.constructor = function(body) {
136
+ var pre = '', post = ''
137
+ var wrapWithParsing = function(body) {
138
+ var pre = '', post = ''
139
+ if (!that.returnsNothing) {
140
+ pre = 'parse('
141
+ post = ')' + (that.collection ? '' : '.first')
142
+ }
143
+ return pre + body + post
144
+ }
145
+
146
+ if (!that.returnsNothing) {
147
+ if (that.isInstanceAction && that.returnsUpdatedRecord) {
148
+ pre = "refresh_with("
149
+ post = ')'
150
+ } else {
151
+ pre = that.collection ? "Collection.new(" : that.inferredReturnType + ".new("
152
+ post = (that.collection ? ', type: ' + that.inferredReturnType : '') + ', client: client)'
153
+ }
154
+ } else { post = ' && true' }
155
+ return pre + wrapWithParsing(body) + post
156
+ }
157
+
158
+ this.request = this.constructor("client." + this.clientMethod + "(" + this.requestParams.join(", ") + ")")
159
+ }
160
+
161
+ Action.prototype.renderDocumentation = function () {
162
+ var formatParamNotes = function(params) {
163
+ var trimmed = _.flatten(_.map(params, function(p) {
164
+ return _.map(p.notes, function(note) { return note.trim() })
165
+ }))
166
+ return (trimmed.length > 0 ? "\nNotes:\n\n" + trimmed.join("\n\n") : "")
167
+ }
168
+
169
+ var formatParam = function(p, name) {
170
+ return (name !== undefined ? name : p.name) + " - [" + p.type + "] " + p.comment
171
+ }
172
+ var lines = _.map(this.params, function(p) { return formatParam(p) })
173
+ if (this.mainIdParam !== undefined && !this.isInstanceAction) { lines.unshift(formatParam(this.mainIdParam, "id")) }
174
+ if (this.collection) { lines.push("per_page - [Integer] the number of records to fetch per page.") }
175
+ if (this.method != 'DELETE') { lines.push("options - [Hash] the request I/O options.") }
176
+ if (this.requiresData) { lines.push("data - [Hash] the attributes to post.") }
177
+ return this.action.comment + "\n" + lines.join("\n") + formatParamNotes(this.params)
178
+ }
179
+
180
+ var actionsToSkip = skip[resource.name] || []
181
+ var actionsToGen = _.reject(resource.actions, function(action) {
182
+ return actionsToSkip.indexOf(action.name) != -1
183
+ })
184
+
185
+ var allActions = _.map(actionsToGen, function(action) { return new Action(action) }),
186
+ instanceActions = _.filter(allActions, function(action) { return action.isInstanceAction }),
187
+ classActions = _.reject(allActions, function(action) { return action.isInstanceAction })
188
+
189
+ var mixinsToInclude = mixins[resource.name] || []
190
+
191
+ %>### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
192
+ ### edit it manually.
193
+
194
+ module Asana
195
+ module Resources
196
+ <%= formatComment(resource.comment, 4) %>
197
+ class <%= cap(singularName) %> < Resource
198
+ <% _.forEach(mixinsToInclude, function(mixin) { %>
199
+ include <%= mixin %>
200
+ <% }) %>
201
+ <% _.forEach(resource.properties, function(property) { %>
202
+ attr_reader :<%= property.name %>
203
+ <% }) %>
204
+ class << self
205
+ # Returns the plural name of the resource.
206
+ def plural_name
207
+ '<%= pluralName %>'
208
+ end
209
+ <% _.forEach(classActions, function(action) { %>
210
+ <%= formatComment(action.documentation, 8) %>
211
+ def <%= action.methodName %>(<%= action.argumentNames.join(", ") %>)
212
+ <% if (action.paramsProcessing) { %> <%= action.paramsProcessing %><% } %>
213
+ <%= action.request %>
214
+ end
215
+ <% }) %> end
216
+ <% _.forEach(instanceActions, function(action) { %>
217
+ <%= formatComment(action.documentation, 6) %>
218
+ def <%= action.methodName %>(<%= action.argumentNames.join(", ") %>)
219
+ <% if (action.paramsProcessing) { %> <%= action.paramsProcessing %><% } %>
220
+ <%= action.request %>
221
+ end
222
+ <% }) %>
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,32 @@
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'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "openc-asana"
8
+ spec.version = Asana::VERSION
9
+ spec.authors = ["Txus"]
10
+ spec.email = ["me@txus.io"]
11
+
12
+ spec.summary = %q{Fork of Official Ruby client for the Asana API}
13
+ spec.description = %q{Fork of Official Ruby client for the Asana API}
14
+ spec.homepage = "https://github.com/openc/ruby-asana"
15
+ spec.license = "MIT"
16
+
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"]
21
+
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"
32
+ end
data/package.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "devDependencies": {
3
+ "inflect": "^0.3.0",
4
+ "js-yaml": "^3.2.5",
5
+ "lodash": "^2.4.1"
6
+ }
7
+ }