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,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
+ }