Giter VIP home page Giter VIP logo

ruby-asana's Introduction

Asana

Gem Version Build Status Code Climate

A Ruby client for the 1.0 version of the Asana API.

Supported rubies:

  • MRI 2.7.x
  • MRI 3.0.x
  • MRI 3.1.x

Gem Installation

Add this line to your application's Gemfile:

gem 'asana'

And then execute:

$ bundle

Or install it yourself as:

$ gem install asana

Usage

To do anything, you'll need always an instance of Asana::Client configured with your preferred authentication method (see the Authentication section below for more complex scenarios) and other options.

The most minimal example would be as follows:

require 'asana'

client = Asana::Client.new do |c|
  c.authentication :access_token, 'personal_access_token'
end

client.workspaces.get_workspaces.first

A full-blown customized client using OAuth2 wih a previously obtained refresh token, Typhoeus as a Faraday adapter, a custom user agent and custom Faraday middleware:

require 'asana'

client = Asana::Client.new do |c|
  c.authentication :oauth2,
                   refresh_token: 'abc',
                   client_id: 'bcd',
                   client_secret: 'cde',
                   redirect_uri: 'http://example.org/auth'
  c.faraday_adapter :typhoeus
  c.configure_faraday { |conn| conn.use SomeFaradayMiddleware }
end

workspace = client.workspaces.get_workspace(12)
workspace.users
# => #<Asana::Collection<User> ...>
client.tags.create_in_workspace(workspace: workspace.id, name: 'foo')
# => #<Asana::Tag id: ..., name: "foo">

All resources are exposed as methods on the Asana::Client instance. Check out the documentation for each of them.

Authentication

This gem supports authenticating against the Asana API with either an API token or through OAuth2.

Personal Access Token

Asana::Client.new do |c|
  c.authentication :access_token, 'personal_access_token'
end

OAuth2

Authenticating through OAuth2 is preferred. There are many ways you can do this.

With a plain bearer token (doesn't support auto-refresh)

If you have a plain bearer token obtained somewhere else and you don't mind not having your token auto-refresh, you can authenticate with it as follows:

Asana::Client.new do |c|
  c.authentication :oauth2, bearer_token: 'my_bearer_token'
end
With a refresh token and client credentials

If you obtained a refresh token, you can use it together with your client credentials to authenticate:

Asana::Client.new do |c|
  c.authentication :oauth2,
                   refresh_token: 'abc',
                   client_id: 'bcd',
                   client_secret: 'cde',
                   redirect_uri: 'http://example.org/auth'
end
With an ::OAuth2::AccessToken object (from omniauth-asana for example)

If you use omniauth-asana or a browser-based OAuth2 authentication strategy in general, possibly because your application is a web application, you can reuse those credentials to authenticate with this API client. Here's how to do it from the callback method:

# assuming we're using Sinatra and omniauth-asana
get '/auth/:name/callback' do
  creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
  strategy = request.env["omniauth.strategy"]

  # We need to refresh the omniauth OAuth2 token
  access_token = OAuth2::AccessToken.from_hash(strategy.client, creds).refresh!

  $client = Asana::Client.new do |c|
    c.authentication :oauth2, access_token
  end

  redirect '/'
end

See examples/omniauth_integration.rb for a working example of this.

Using an OAuth2 offline authentication flow (for CLI applications)

If your application can't receive HTTP requests and thus you can't use omniauth-asana, for example if it's a CLI application, you can authenticate as follows:

access_token = Asana::Authentication::OAuth2.offline_flow(client_id: ...,
                                                          client_secret: ...)
client = Asana::Client.new do |c|
  c.authentication :oauth2, access_token
end

client.tasks.get_task(12)

This will print an authorization URL on STDOUT, and block until you paste in the authorization code, which you can get by visiting that URL and granting the necessary permissions.

Pagination

Whenever you ask for a collection of resources, you can provide a number of results per page to fetch, between 1 and 100. If you don't provide any, it defaults to 20.

my_tasks = client.tasks.get_tasks_for_tag(tag: tag_id, per_page: 5)
# => #<Asana::Collection<Task> ...>

An Asana::Collection is a paginated collection -- it holds the first per_page results, and a reference to the next page if any.

When you iterate an Asana::Collection, it'll transparently keep fetching all the pages, and caching them along the way:

my_tasks.size # => 23, not 5
my_tasks.take(14)
# => [#<Asana::Task ...>, #<Asana::Task ...>, ... until 14]

Manual pagination

If you only want to deal with one page at a time and manually paginate, you can get the elements of the current page with #elements and ask for the next page with #next_page, which will return an Asana::Collection with the next page of elements:

my_tasks.elements # => [#<Asana::Task ...>, #<Asana::Task ...>, ... until 5]
my_tasks.next_page # => #<Asana::Collection ...>

Lazy pagination

Because an Asana::Collection represents the entire collection, it is often handy to just take what you need from it, rather than let it fetch all its contents from the network. You can accomplish this by turning it into a lazy collection with #lazy:

# let my_tasks be an Asana::Collection of 10 pages of 100 elements each
my_tasks.lazy.drop(120).take(15).to_a
# Fetches only 2 pages, enough to get elements 120 to 135
# => [#<Asana::Task ...>, #<Asana::Task ...>, ...]

Error handling

In any request against the Asana API, there a number of errors that could arise. Those are well documented in the Asana API Documentation, and are represented as exceptions under the namespace Asana::Errors.

The Asana client automatically handles and retries requests upon receipt of 500 (Internal Server Error) responses from the Asana API. If you want to handle any 4xx errors, you will have to do that yourself; you can do this by rescuing Asana::Errors::APIError, the parent class of all Asana API errors.

I/O options

All requests (except DELETE) accept extra I/O options as documented in the API docs. Just pass an extra options hash to any request:

client.tasks.get_task(12, options: { expand: ['workspace'] })

Attachment uploading

To attach a file to a task or a project, you just need its absolute path on your filesystem and its MIME type, and the file will be uploaded for you:

task = client.tasks.get_task(12)
attachment = task.attach(filename: '/absolute/path/to/my/file.png',
                         mime: 'image/png')
attachment.name # => 'file.png'

Event streams

To subscribe to an event stream of a task or a project, just call #events on it:

task = client.tasks.get_task(12)
task.events # => #<Asana::Events ...>

# You can do the same with only the task id:
events = client.events.for(task.id)

An Asana::Events object is an infinite collection of Asana::Event instances. Be warned that if you call #each on it, it will block forever!

Note that, by default, an event stream will wait at least 1 second between polls, but that's configurable with the wait parameter:

# wait at least 3 and a half seconds between each poll to the API
task.events(wait: 3.5) # => #<Asana::Events ...>

There are some interesting things you can do with an event stream, as it is a normal Ruby Enumerable. Read below to get some ideas.

Subscribe to the event stream with a callback, polling every 2 seconds

# Run this in another thread so that we don't block forever
events = client.tasks.get_task(12).events(wait: 2)
Thread.new do
  events.each do |event|
    notify_someone "New event arrived! #{event}"
  end
end

Make the stream lazy and filter it by a specific pattern

To do that we need to call #lazy on the Events instance, just like with any other Enumerable.

events = client.tasks.get_task(12).events
only_change_events = events.lazy.select { |event| event.action == 'changed' }
Thread.new do
  only_change_events.each do |event|
    notify_someone "New change event arrived! #{event}"
  end
end

Asana Change Warnings

You will receive warning logs if performing requests that may be affected by a deprecation. The warning contains a link that explains the deprecation.

If you receive one of these warnings, you should:

  • Read about the deprecation.
  • Resolve sections of your code that would be affected by the deprecation.
  • Add the deprecation flag to your "asana-enable" header.

You can add global headers, by setting default_headers

c.default_headers "asana-enable" => "string_ids"

Or you can add a header field to the options of each request.

If you would rather suppress these warnings, you can set

c.log_asana_change_warnings false

Development

You'll need Ruby 2.7+ and Node v0.10.26+ / NPM 1.4.3+ installed.

After checking out the repo, run bin/setup to install dependencies. Then, run bin/console for an interactive prompt that will allow you to experiment.

Run the build with rake. This is equivalent to:

$ rake spec && rake rubocop && rake yard

To install this gem onto your local machine, run bundle exec rake install.

Releasing a new version

Prerequisite: Before deployment, make sure you have Ruby version 2.7 installed

Automatic Deployment

First, install dependencies:

bundle install

Then, to release a new version, run one of these commands:

rake bump:patch
rake bump:minor
rake bump:major

This will: update lib/asana/version.rb and VERSION, commit and tag the commit. Then you just need to push --tags to let GitHub Actions build and release the new version to Rubygems:

git push --tags origin master:master

Manual Deployment

  1. Merge in the desired changes into the master branch and commit them.
  2. Clone the repo, work on master.
  3. Edit package version in lib/asana/version.rb and VERSION to indicate the semantic version change.
  4. Commit the change
  5. Tag the commit with v plus the same version number you set in the file. git tag v1.2.3
  6. Push changes to origin, including tags: git push --tags origin master:master

Code generation

The specific Asana resource classes (Tag, Workspace, Task, etc) are generated code, hence they shouldn't be modified by hand. The code that generates it lives in lib/templates/resource.ejs, and is tested by generating spec/templates/unicorn.rb and running spec/templates/unicorn_spec.rb as part of the build.

If you wish to make changes on the code generation script:

  1. Add/modify a spec on spec/templates/unicorn_spec.rb
  2. Add your new feature or change to lib/templates/resource.ejs
  3. Run rake or, more granularly, rake codegen && rspec spec/templates/unicorn_spec.rb

Once you're sure your code works, submit a pull request and ask the maintainer to make a release, as they'll need to run a release script.

Contributing

  1. Fork it ( https://github.com/[my-github-username]/asana/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

ruby-asana's People

Contributors

agnoster avatar asanabot avatar aw-asana avatar bogdanrada avatar dependabot[bot] avatar igor-alexandrov avatar jjschnei avatar joetrollo avatar joshuasp avatar jv-asana avatar kavu avatar leoarnold avatar markchua avatar praecipula avatar rocco-haro avatar rossgrambo-zz avatar slobak avatar spadachris avatar stanhu avatar theaeolianmachine avatar thimios avatar txus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ruby-asana's Issues

Add Members to Project

Hi, this gem's documentation lack details. I'm trying to read the source code / the asana api documentation to understand the methods available..sorry if i miss something.

How can i add a member to a project ?

Thanks.
Brice

Unnecessary #get calls

Say I attempt to get tasks and pass opt_fields = { fields: [tag.name] }

1 - tasks = AsanaClient.tasks.by_project(project: 1, options: opts)
2 - tasks.each do |t|
3 -  tags = t.tags
4 - end

When the line 3 is executed, the client performs a #get to tasks/{id}/tags. Why is this done? The information for tags was already returned in line 1, and this #get call returns data: [] most of the time. As a result, our job spends time wasted making this call, and creates unnecessary load to the Asana API.

Is there a way to prevent certain calls from being made?

ruby2_0_0_compatibility.rb#required Breaks FactoryGirl on models that have a `required` attribute.

Hi there,

Trying to integrate the asana API client into a project I am working on. Simply adding the gem to my Gemfile and bundling breaks my test suite, because I have factories that create models with a required attribute, and the the #required method in ruby-asana/lib/asana/ruby2_0_0_compatibility.rb interferes.

Stack Trace (note line 4):

$ rspec
/Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/definition_proxy.rb:42:in `add_attribute': wrong number of arguments (given 3, expected 1..2) (ArgumentError)
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/definition_proxy.rb:102:in `method_missing'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/asana-0.5.0/lib/asana/ruby2_0_0_compatibility.rb:2:in `required'
from /Users/me/projects/project/spec/factories/content_block_factories.rb:5:in `block (2 levels) in <top (required)>'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/syntax/default.rb:18:in `instance_eval'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/syntax/default.rb:18:in `factory'
from /Users/me/projects/project/spec/factories/content_block_factories.rb:3:in `block in <top (required)>'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/syntax/default.rb:49:in `instance_eval'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/syntax/default.rb:49:in `run'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/syntax/default.rb:7:in `define'
from /Users/me/projects/project/spec/factories/content_block_factories.rb:1:in `<top (required)>'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:268:in `load'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:268:in `block in load'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:240:in `load_dependency'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:268:in `load'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/find_definitions.rb:20:in `block (2 levels) in find_definitions'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/find_definitions.rb:19:in `each'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/find_definitions.rb:19:in `block in find_definitions'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/find_definitions.rb:15:in `each'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl-4.5.0/lib/factory_girl/find_definitions.rb:15:in `find_definitions'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/factory_girl_rails-4.5.0/lib/factory_girl_rails/railtie.rb:21:in `block in <class:Railtie>'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/lazy_load_hooks.rb:36:in `execute_hook'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/lazy_load_hooks.rb:45:in `block in run_load_hooks'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/lazy_load_hooks.rb:44:in `each'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.6/lib/active_support/lazy_load_hooks.rb:44:in `run_load_hooks'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/railties-4.2.6/lib/rails/application/finisher.rb:62:in `block in <module:Finisher>'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/railties-4.2.6/lib/rails/initializable.rb:30:in `instance_exec'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/railties-4.2.6/lib/rails/initializable.rb:30:in `run'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/railties-4.2.6/lib/rails/initializable.rb:55:in `block in run_initializers'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:228:in `block in tsort_each'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:431:in `each_strongly_connected_component_from'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:349:in `block in each_strongly_connected_component'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:347:in `each'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:347:in `call'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:347:in `each_strongly_connected_component'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:226:in `tsort_each'
from /Users/me/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/tsort.rb:205:in `tsort_each'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/railties-4.2.6/lib/rails/initializable.rb:54:in `run_initializers'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/railties-4.2.6/lib/rails/application.rb:352:in `initialize!'
from /Users/me/projects/project/config/environment.rb:5:in `<top (required)>'
from /Users/me/projects/project/spec/rails_helper.rb:3:in `require'
from /Users/me/projects/project/spec/rails_helper.rb:3:in `<top (required)>'
from /Users/me/projects/project/spec/controllers/api/comments_controller_spec.rb:1:in `require'
from /Users/me/projects/project/spec/controllers/api/comments_controller_spec.rb:1:in `<top (required)>'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `block in load_spec_files'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1325:in `each'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1325:in `load_spec_files'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:102:in `setup'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:88:in `run'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:73:in `run'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:41:in `invoke'
from /Users/me/.rvm/gems/ruby-2.3.1/gems/rspec-core-3.3.2/exe/rspec:4:in `<top (required)>'
from /Users/me/.rvm/gems/ruby-2.3.1/bin/rspec:23:in `load'
from /Users/me/.rvm/gems/ruby-2.3.1/bin/rspec:23:in `<main>'
from /Users/me/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `eval'
from /Users/me/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `<main>'

To quickly reproduce, create a model with a boolean required attribute in a rails project that uses FactoryGirl, and create a simple Factory for it, then try to run rspec:


# spec/factories/my_model_factories.rb
FactoryGirl.define do

factory :my_model do
 required true
 end

end

Is there a way I can work around this?

Thanks!

┆Issue is synchronized with this Asana task

Support Rich Text response

I was building integration with the Asana::Resources::Task, and it seems the api server didn't respond a html_notes attribute, even I added Asana-Enable: new_rich_text to the request's header.

The update (put) is fine, the api server can accept html_notes, but I couldn't see the response since the attribute was missing.

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 23 Oct 2018 23:58:52 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 1352
Connection: close
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Content-Security-Policy: report-uri https://app.asana.com/-/csp_report;default-src 'none';frame-src 'none';frame-ancestors 'none'
X-Asana-Api-Version: 1.1
Asana-Change: name=security_headers;info=https://asa.na/api-sh;affected=true
Asana-Change: name=new_rich_text;info=https://asa.na/api-rich-text
Asana-Change: name=new_task_subtypes;info=https://asa.na/api-task-subtypes
X-Robots-Tag: none
Strict-Transport-Security: max-age=31536000; includeSubDomains
Datacenter-Time-End: 1540339132.081
X-LoadBalancer: prod-lb005.ec2

{"data":{"id":xxxxx,"gid":"xxxxx","assignee":{"id":xxxxx,"gid":"xxxxx","name":"alex","resource_type":"user"},"assignee_status":"inbox","completed":false,"completed_at":null,"created_at":"2018-10-02T23:19:56.571Z","due_at":null,"due_on":"2018-10-24","followers":[{"id":xxxxx,"gid":"xxxxx","name":"alex","resource_type":"user"}],"hearted":false,"hearts":[],"liked":false,"likes":[],"memberships":[{"project":{"id":xxxxx,"gid":"xxxxx","name":"Platform","resource_type":"project"},"section":{"id":xxxxx,"gid":"xxxxx","name":"Order workflow:","resource_type":"section"}}],"modified_at":"2018-10-23T21:27:00.187Z","name":"Draft","notes":"Draft is an orders initial status, which allow sales team to enter the products and service.\n\nWhen an admin user (usually sales team) creating a new order, the order will be in the draft stage. Also, the system will create a order task on the asana.\nIntegrate with Asana\nTest","num_hearts":0,"num_likes":0,"parent":null,"projects":[{"id":xxxxx,"gid":"xxxxx","name":"Platform","resource_type":"project"}],"resource_type":"task","start_on":null,"tags":[],"resource_subtype":"default_task","workspace":{"id":xxxxx,"gid":"xxxxx","name":"xxxxx.com","resource_type":"workspace"}}}

Searching task with custom fields paramaters.

Hey,

TasksBase#search_tasks_for_workspace does not allow to filter results based on custom fields.

Though, the documentation indicates it's a possibility here: https://developers.asana.com/docs/search-tasks-in-a-workspace chapter "Custom field parameters".

In other words, I would like to be able to write:

result = client.tasks.search_tasks_for_workspace workspace_gid: 'wrk123', teams_any: 'team123,team456', 'custom_fields.123.value': '456'

The end-point accepts this syntax, so why the API is not allowing custom_fields... parameters?

I could write and propose a PR, but I need to know if such a feature would be accepted.

Thanks

task#search_in_workspace : Sections.any failure

Say I want to search for a task by modified date.

I do the following:

w = AsanaClient.workspaces.find_all.first
t1 = Time.now - 4.days
t2 = Time.now - 2.days
t1 = t1.iso8601
t2 = t2.iso8601
options = {  fields: ['created_at', 'modified_at', 'completed_at', 'category', 'tags'], params: { 'modified_at.after': t1 ,  'modified_at.before': t2  } } 
 AsanaClient.tasks.search_in_workspace({workspace: w.id, options: options})

Works well and produces the url:

GET http://app.asana.com:443/api/1.0/workspaces/<redacted_id>/tasks/search?limit=20&modified_at.after=2019-09-01T17%3A20%3A30-07%3A00&modified_at.before=2019-09-03T17%3A20%3A30-07%3A00&opt_fields=created_at%2Cmodified_at%2Ccompleted_at%2Ccategory%2Ctags&opt_params=%7B%3A%22modified_at.after%22%3D%3E%222019-09-01T17%3A20%3A30-07%3A00%22%2C+%3A%22modified_at.before%22%3D%3E%222019-09-03T17%3A20%3A30-07%3A00%22%7D

Now if I want to filter by sections, I modify options to be the following:

options = {  fields: ['created_at', 'modified_at', 'completed_at', 'category', 'tags'], params: { 'modified_at.after': t1 , 'sections.any': <redacted_id> , 'modified_at.before': t2  } } 

When I execute

 AsanaClient.tasks.search_in_workspace({workspace: w.id, options: options})

the following url

GET http://app.asana.com:443/api/1.0/workspaces/<redacted_id>/tasks/search?limit=20&modified_at.after=2019-09-01T17%3A20%3A30-07%3A00&modified_at.before=2019-09-03T17%3A20%3A30-07%3A00&opt_fields=created_at%2Cmodified_at%2Ccompleted_at%2Ccategory%2Ctags&opt_params=%7B%3A%22modified_at.after%22%3D%3E%222019-09-01T17%3A20%3A30-07%3A00%22%2C+%3A%22sections.any%22%3D%3E1135740032216942%2C+%3A%22modified_at.before%22%3D%3E%222019-09-03T17%3A20%3A30-07%3A00%22%7D&sections.any=<redacted_id>

I get an error claiming "message":"sections.any: Not a recognized ID: <redacted_id>"

Now I know that the error is incorrect because I am able to get a 200 response when I don't use the gem:

  def build_task_request(path)
    uri = URI.parse(@@base_url + path)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    req = Net::HTTP::Get.new(uri.request_uri)
    req['Authorization'] = 'Bearer ' + '<redacted_token>'
    [http, req]
  end

    fields_of_interest = ['created_at', 'modified_at', 'completed_at', 'category', 'tags']
    time = Time.now - 1.year
    time = time.iso8601

    # by section 
    http, req = build_task_request('/workspaces/<redacted_id>/tasks/search?limit=100&sections.any=<redacted_id>&opt_fields='+fields_of_interest.join(',')+'&opt_params=%7B%3A%22modified_at.after%22%3D%3E%222019-09-01T15%3A40%3A12-07%3A00%22%2C+%3A%22modified_at.before%22%3D%3E%222019-09-03T15%3A40%3A12-07%3A00%22%2C+%3A%22sections.any%22%3D%3E1135740032216942%7D&sections.any=<redacted_id>')

    res = JSON(http.request(req).body)
    tasks = res['data']

produces the following url:

GET http://app.asana.com:443/api/1.0/workspaces/<redacted_id>/tasks/search?limit=100&sections.any=1135740032216942&opt_fields=created_at,modified_at,completed_at,category,tags&opt_params=%7B%3A%22modified_at.after%22%3D%3E%222019-09-01T15%3A40%3A12-07%3A00%22%2C+%3A%22modified_at.before%22%3D%3E%222019-09-03T15%3A40%3A12-07%3A00%22%2C+%3A%22sections.any%22%3D%3E1135740032216942%7D&sections.any=<redacted_id>

and a response with no errors, and a list of tasks.

I've triple checked that I was clear of typos with respect to workspace and section ids.

What i've noticed is that the URLs get built differently. Sections.any is not included along with modified_at.after or modified_at.before. I don't know if that's the issue or not, but I would appreciate some investigation into this.

Kind regards.

Faraday Warning on `authorization`

When using the latest version of the gem (with an updated version of Faraday), I see several of these messages:

WARNING: `Faraday::Connection#authorization` is deprecated; it will be removed in version 2.0.
While initializing your connection, use `#request(:authorization, ...)` instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.

tasks_base.rb#get_tasks() pagination broken

/tasks appears to be a legacy endpoint which requires limit be specified if the number of tasks returned would require pagination:

Unfortunately, 'limit' is not sent in automatically in the Ruby client library, nor is it allowed to be sent in by the client library user. Arguably it should be a keyword argument that defaults to 20 and can be overridden.

As a result, in Ruby, client.tasks.get_tasks() cannot be used when it would return a great number of items.

In addition, the documentation is wrong, as it suggests you can pass in 'limit' as part of the options - this has no effect, as it gets translated to 'opt_limit', which is not recognized by the API.

Disable Multiple "Asana Change Warnings"

We are trying to set up the client to not show warnings for "string_ids" and "new_sections".

What is the correct syntax to define multiple values for the "asana-enable" header?

Our current code:

Asana::Client.new do |c|

  c.default_headers "asana-enable" => "string_ids"

  c.authentication :access_token, 'XXXXX'

end

We tried already these without success:
c.default_headers "asana-enable" => ["string_ids", "new_sections"]
c.default_headers "asana-enable" => "string_ids new_sections"
c.default_headers "asana-enable" => "string_ids, new_sections"

and even (which, of course, does not work)

c.default_headers "asana-enable" => "string_ids"
c.default_headers "asana-enable" => "new_sections"

Is there a way to set multiple values?

Typo in Section.find_by_id

There's currently a typo in the Asana::Resources::Section.find_by_id method. In the method declaration, the section id is declared as id, but the method body tries to refer to it as the gid.

The resulting error message is NameError: undefined local variable or method 'gid' for Asana::Resources::Section:Class.

Here's a link to the line of code in question

The code is currently:

def find_by_id(client, id, options: {})
  self.new(parse(client.get("/sections/#{gid}", options: options)).first, client: client)
end

But it should be:

def find_by_id(client, id, options: {})
  self.new(parse(client.get("/sections/#{id}", options: options)).first, client: client)
end

API Deprecation Warnings with 0.10.3

Creating a task with the currently available gem version from rubygems.org (0.10.3) creates the following deprecation output on the console:

This request is affected by the "new_project_templates" deprecation. Please visit this url for more info: https://forum.asana.com/t/a-new-api-for-project-templates/156432
Adding "new_project_templates" to your "Asana-Enable" or "Asana-Disable" header will opt in/out to this deprecation and suppress this warning.
This request is affected by the "new_user_task_lists" deprecation. Please visit this url for more info: https://forum.asana.com/t/update-on-our-planned-api-changes-to-user-task-lists-a-k-a-my-tasks/103828
Adding "new_user_task_lists" to your "Asana-Enable" or "Asana-Disable" header will opt in/out to this deprecation and suppress this warning.

The new_project_templates deprecation requires new api endpoints to be addressed, so does not seem to be anything users of the gem can do by themselves.

The new_user_task_list deprecation simply returns a field less in GET responses, if I read the linked forum entry correctly, so shouldn't be a big issue, however it would be nice to have the deprecation notice suppressed.

Act on the behalf of another user for simple Asana actions

As you know, these Ruby bindings are used by GitLab which parse commit message:

  • to add a comment
  • or close an Asana task

It works well on overall but with one particular drawback: This two actions are realized on the behalf of the user registering the GitLab integration what often makes no sense: If I just setup the integration using my Asana account, all comments and tasks (or likes done on a completed tasks) coming from GitLab commits are authored by me and (with my icon) what is misleading.

(It would be easy to immediately throw the problem to GitLab, but it's not realistic to expect one to configure one token/access per commiter. Some commiters may not even exist in Asana.)

I'm not sure if it's something the core API could solve but I thought this repository could be a nice place to discuss and identify the right component to modify in order to resolve / workaround the problem.

expand tasks doesn't seem to work!

I am trying to avoid querying Asana while cloning a project Template, so was thinking the expand would let me avoid lots of calls back to get each task.

I'm trying:

client.projects.find_by_id(project_id, options: { expand: ['tasks'] })

But not getting any errors, or expanded tasks!
Thoughts?

Searching workspace bugs/confusion

The code generation for the search_tasks_for_workspace method on TasksBase translates the periods in all of the parameters that have them into underscores, causing the wrong params to be emitted.

So this call fails:

Asana::Resources::TasksBase.search_tasks_for_workspace(
  @asana_client,
  workspace_gid: @workspace_gid,
  followers_any: @asana_user_id
)

There is another place this endpoint is surfaced in the gem, in Asana::Resources::Task.search_in_workspace. This doesn't have first-class params for the search parameters, just a params and options hash (which to me seemed less helpful than the other method, which is why I tried it first). But it does allow for string-symbol keys that are accurate:

Asana::Resources::Task.search_in_workspace(
  @asana_client,
  workspace: @workspace_gid,
  options: {params: {"followers.any": @asana_user_id}
  }
)

I didn't see anything in the docs for this issue, so if I've overlooked something, happy to learn! :)

find a tag by its name

Hi
I'm trying to add tags to tasks I create. To do so I first query the existing tags to verify if it already exist / not, and if it doesn't, I create it.

def find_or_create_tag(tag_name)
  tag_object = @client.tags.find_by_workspace(workspace: workspace.gid).select do |tag|
    tag.name == tag_name
  end.first

  if tag_object.nil?
    tag_object = create_tag(tag_name)
  end

  return tag_object
end

def create_tag(tag_name)
  @client.tags.create(
    workspace: workspace.gid,
    name: tag_name
    )
end

Unfortunately, it seems that the tags.find_by_workspace function doesn't return the full array of tags, so my find_or_create_tag function doesn't work.

How would you achieve this goal ?

Thanks !

Task methods return 404

I can successfully return a Task using client.tasks.find_by_id(my_task.gid). However I get a 404 when attempting to perform any action on the same Task.

For example:
client.tasks.find_by_id(my_task.gid) returns the task I would like to modify, but
client.tasks.find_by_id(my_task.gid).delete() and client.tasks.find_by_id(my_task.gid).update({completed:true})
return 404 errors.

These called used to work, but stopped working sometime in the last ~1 month. Attached is a stack trace.

asana_staketrace

Stuck when creating new Porfolio

When running client.portfolios.create(workspace: 'workspace_gid', name: "portfolio_name", color: color_array.sample) on IRB/PRY the call gets stuck and I have to exit using control+C.

The first two lines of the error message are:

(pry) output error: Interrupt
/Users/duartemartins/.asdf/installs/ruby/3.0.0/lib/ruby/3.0.0/net/protocol.rb:219:in `wait_readable'

The Faraday adapter middleware can not be configured properly because the block from ResourceProxy method_missing is lost inside the Resource class instead of being passed to the Asana::HttpClient

Example i am trying to create a task in asana like this:

request_options = {:assignee=>"me", :assignee_status=>"today", workspace: 'some_workspace_id'}
client.tasks.create(**request_options)

This method uses ResourceProxy to call the create method from the Asana::Resources::Task which used this code:

  def method_missing(m, *args, &block)
        @resource.public_send(m, *([@client] + args), &block)
      end

however the create method from the Asana::Resources::Task does not send to the Asana::HttpClient the block

      def create(client, workspace: nil, options: {}, **data)
          with_params = data.merge(workspace: workspace).reject { |_,v| v.nil? || Array(v).empty? }
          self.new(parse(client.post("/tasks", body: with_params, options: options,)).first, client: client)
        end

In that method, there should be a new parameter &block that could be sent to Asana::HttpClient to the request method ( POST in this case)

Also the Asana::HttpClient should send this block to the connection method ( which currently it doesn;t because the post method does not accept a block parameter)

def post(resource_uri, body: {}, upload: nil, options: {})

This is because the perform_request does accept a block parameter

def perform_request(method, resource_uri, body = {}, &request_config)

This block is needed because this is used in the connection method:

  def connection(&request_config)

And because this block parameter is lost in the Asana::Resources::Task (for this particular example), the Faraday::Connection is not sent to the faraday adapter middleware, causing the middleware to try to execute methods on NIL value , because the block is lost in the resource .

I think this issue happens for all resources, could this be fixed somehow? Maybe there could be a easier fix than modifying all methods from all resources.

Prevent duplication task

Hi, I am just using asana gem for task creation on asana when any test suit failed (i.e. job status failed) then I am able to create a task. I have a job which run the test on a daily basis and if the same test again gets failed then I don't want to create a new task but want to add a comment on the existing task if the old task is open and status is not completed. Right now if the same test gets failed then it's creating a new task.

How I am able to achieve this.
Thanks!

search_in_workspace for tasks appears to be incomplete

In the php version, params is in the method signature: https://github.com/Asana/php-asana/blob/master/src/Asana/Resources/Gen/TasksBase.php#L203

Yet it is missing here https://github.com/Asana/ruby-asana/blob/master/lib/asana/resources/task.rb#L233 , rendering it useless other than for searching by resource subtype.

I was considering just settling for find_all, but it restricts to filtering by modified_since, when what we need is modified_at.after.

can't launch asana gem

Hi there
I'm trying to use the asana gem to start a new project, but can't even launch it...do you know how to proceed ?

➜  ~ gem install asana
Successfully installed asana-0.8.1
1 gem installed
➜  ~ irb
irb(main):001:0> require 'asana'
Gem::LoadError: can't activate faraday-0.15.4, already activated faraday-0.12.2
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/specification.rb:2289:in `check_version_conflict'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/specification.rb:1411:in `activate'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:86:in `block in require'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:85:in `each'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:85:in `require'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/asana-0.8.1/lib/asana/http_client.rb:1:in `<top (required)>'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:65:in `require'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:65:in `require'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/asana-0.8.1/lib/asana.rb:6:in `<top (required)>'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:130:in `require'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:130:in `rescue in require'
	from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:35:in `require'
	from (irb):1
	from /usr/local/var/rbenv/versions/2.3.0/bin/irb:11:in `<main>'

How to bypass the faraday error mentioned below ?

Thanks

Stories returned by webhook not found

I'm using the library's webhooks methods to receive updates when tasks are marked as complete and incomplete. Upon receiving webhook events, I'm filtering by events of type story and kicking off a Sidekiq job to retrieve the story and check whether its subtype matches marked_complete or marked_incomplete.

Roughly 90% of the time I reopen a task, I get the following message back when attempting to call client.stories.find_by_id(story_id):

Either the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.

The other 10% of the time, the library returns a story of subtype marked_incomplete. I've noticed that this happens less often for other types of stories, but story availability in general seems to be intermittent.

Let me know if you need more information - happy to provide. Thank you!

Full power of Asana API is hidden

Asana API is pretty flexible, but this gem cut offs its flexibility. For example, we cannot use query tasks by a project && completed_since fields or a tag && completed_since fields.

┆Issue is synchronized with this Asana task

task.rb#add_project() and #add_subtask() broken parameters

The insert_after and insert_before parameters to task.rb#add_project() need to differentiate between the user not passing in a parameter and the user passing in nil.

Per the API documentation, at most one of insert_before, insert_after or section should be specified, but null is a valid value for insert_after/insert_before that has different semantics from simply not passing the parameter: https://developers.asana.com/docs/add-a-project-to-a-task (be sure the expand the 'data' parameter in the docs to see this detail)

A similar issue affects the 'parent' parameter in task.rb#set_parent()

These two situations hit a use case for me and I've had to apply this diff to be able to pass in nil to the insert_before/insert_after parameters:

https://github.com/Asana/ruby-asana/compare/master...apiology:insert_before_after?expand=1

I'd be happy to submit that as a PR, but perhaps the more correct fix is upstream in the code generation? Let me know what you think.

NameError (uninitialized constant Asana::Client)

This seems like a dumb error but I have this Gemfile:

source 'https://rubygems.org'

gem 'asana'

I run:
bundle install
bundle exec irb
require 'asana' => true
Asana::Client.new
and I get NameError (uninitialized constant Asana::Client)

ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-dar

Undefined method ‘project_templates’

Hi,

Strange one here. I’m using the Ruby API to automate the creation of projects from project templates and it appears the ‘project_templates’ method can’t be found.

I’m just doing a simple get template as defined in the Asana API docs with:

result = client.project_templates.get_project_template(project_template_gid: ‘xxxxxxxxxxxxxxxxxxx’, options: {pretty: true})

And the error I get back is that ‘project_templates’ is undefined as in:

<main>': undefined method project_templates’ for #<Asana::Client:0x000000011115c720 @http_client=#<Asana::HttpClient:0x000000011115c3b0 @authentication=#<Asana::Authentication::OAuth2::BearerTokenAuthentication:0x000000011115c518 @token=“xxxxxxxxxxxxxxxxx”>, @adapter=:net_http, @environment_info=#<Asana::HttpClient::EnvironmentInfo:0x000000011115c2c0 @user_agent=“ruby-asana v2.0.0”, @openssl_version=“OpenSSL 1.1.1n 15 Mar 2022”, @client_version=“2.0.0”, @os=“darwin”>, @debug_mode=nil, @log_asana_change_warnings=true, @default_headers=nil, @config=nil>> (NoMethodError) result = client.project_templates.get_project_template(project_template_gid: ‘1203358303276849’, options: {pretty: true}) ^^^^^^^^^^^^^^^^^^ Did you mean? project_statuses

This is strange. I’ve checked I’m using the latest version of the ruby-asana library (v2.0.0 see GitHub - Asana/ruby-asana: Official Ruby client library for the Asana API v1) and I’ve also checked the libraries that this method is there (ruby-asana/project_templates_base.rb at 99920daab813c8b4a127fbee0c10017210efd7e0 · Asana/ruby-asana · GitHub). Everything seems in order but it’s not working.

Any ideas why Asana is throwing this error?

Thanks,
Ian

how do I get a project

Is there example code on how to get a project from the workspace?

this is giving me an argument error 2 for 1

  workspace = client.workspaces.find_by_id(someId)
  projects = client.projects.find_by_workspace(workspace)

Cached task dependencies not retrievable

When fetching a large number of tasks, one should be able to request fields like dependencies be pulled as part of the bulk pull. Those fields should be usable later without the information being refetched.

How to reproduce:

tasks = client.tasks.find_all(project: project_gid, options: { fields: 'dependencies' })
tasks_with_dependencies = tasks.select { |task| task.dependencies.size == 0 }

Expected result:

Second line does not cause extra HTTP GET on the API, as the dependency information was fetched with the original client.tasks.find_all() call.

Actual result:

Second line creates O(n) extra HTTP get requests.

Workaround

Users of the SDK can call task.instance_variable_get(:@dependencies) to check for cached dependencies instead of calling task.dependencies.

Commentary

This bug is causing 15% of the Asana API requests in my project. While the @Dependencies variable is saved upon the initial fetch from the project, it appears the generated API code for dependencies() does not look for @dependencies or save it afterwards:

https://github.com/Asana/ruby-asana/blob/master/lib/asana/resources/task.rb#L283-L286

Response object of `task.update` seems to fail though actual update succeeds

When I run the following code to update a given task, the response is a Asana::Resources::Task object, but some functions on that object fail (projects, tags):

task = Asana::Client.new(...).tasks.find_by_id "asana_task_id"
response = task.update(completed: true)

In the rails console, response results in:

(pry) output error: #<NoMethodError: undefined method `get' for nil:NilClass
Did you mean?  gem>

and response.to_s results in:

NoMethodError: undefined method `get' for nil:NilClass
Did you mean?  gem
from /app/vendor/bundle/ruby/2.4.0/gems/asana-0.8.1/lib/asana/resources/task.rb:316:in `projects'

Looking at task.rb:316 it looks like client is nil

Note that the update succeeds and I see the tasked marked as complete on asana.com.

Custom field parameters not available in tasks.search_tasks_for_workspace()

GET /workspaces/{workspace_gid}/tasks/search allows you to search based on custom field attributes - see the 'Custom field parameters' section here: https://developers.asana.com/docs/search-tasks-in-a-workspace

Unfortunately, the generated ruby-asana SDK does not support these parameters, forcing people to hand-craft their API requests:

  • # Search tasks in a workspace
    #
    # workspace_gid - [str] (required) Globally unique identifier for the workspace or organization.
    # text - [str] Performs full-text search on both task name and description
    # resource_subtype - [str] Filters results by the task's resource_subtype
    # assignee_any - [str] Comma-separated list of user identifiers
    # assignee_not - [str] Comma-separated list of user identifiers
    # portfolios_any - [str] Comma-separated list of portfolio IDs
    # projects_any - [str] Comma-separated list of project IDs
    # projects_not - [str] Comma-separated list of project IDs
    # projects_all - [str] Comma-separated list of project IDs
    # sections_any - [str] Comma-separated list of section or column IDs
    # sections_not - [str] Comma-separated list of section or column IDs
    # sections_all - [str] Comma-separated list of section or column IDs
    # tags_any - [str] Comma-separated list of tag IDs
    # tags_not - [str] Comma-separated list of tag IDs
    # tags_all - [str] Comma-separated list of tag IDs
    # teams_any - [str] Comma-separated list of team IDs
    # followers_not - [str] Comma-separated list of user identifiers
    # created_by_any - [str] Comma-separated list of user identifiers
    # created_by_not - [str] Comma-separated list of user identifiers
    # assigned_by_any - [str] Comma-separated list of user identifiers
    # assigned_by_not - [str] Comma-separated list of user identifiers
    # liked_by_not - [str] Comma-separated list of user identifiers
    # commented_on_by_not - [str] Comma-separated list of user identifiers
    # due_on_before - [date] ISO 8601 date string
    # due_on_after - [date] ISO 8601 date string
    # due_on - [date] ISO 8601 date string or `null`
    # due_at_before - [datetime] ISO 8601 datetime string
    # due_at_after - [datetime] ISO 8601 datetime string
    # start_on_before - [date] ISO 8601 date string
    # start_on_after - [date] ISO 8601 date string
    # start_on - [date] ISO 8601 date string or `null`
    # created_on_before - [date] ISO 8601 date string
    # created_on_after - [date] ISO 8601 date string
    # created_on - [date] ISO 8601 date string or `null`
    # created_at_before - [datetime] ISO 8601 datetime string
    # created_at_after - [datetime] ISO 8601 datetime string
    # completed_on_before - [date] ISO 8601 date string
    # completed_on_after - [date] ISO 8601 date string
    # completed_on - [date] ISO 8601 date string or `null`
    # completed_at_before - [datetime] ISO 8601 datetime string
    # completed_at_after - [datetime] ISO 8601 datetime string
    # modified_on_before - [date] ISO 8601 date string
    # modified_on_after - [date] ISO 8601 date string
    # modified_on - [date] ISO 8601 date string or `null`
    # modified_at_before - [datetime] ISO 8601 datetime string
    # modified_at_after - [datetime] ISO 8601 datetime string
    # is_blocking - [bool] Filter to incomplete tasks with dependents
    # is_blocked - [bool] Filter to tasks with incomplete dependencies
    # has_attachment - [bool] Filter to tasks with attachments
    # completed - [bool] Filter to completed tasks
    # is_subtask - [bool] Filter to subtasks
    # sort_by - [str] One of `due_date`, `created_at`, `completed_at`, `likes`, or `modified_at`, defaults to `modified_at`
    # sort_ascending - [bool] Default `false`
    # options - [Hash] the request I/O options
    # > opt_fields - [list[str]] Defines fields to return. Some requests return *compact* representations of objects in order to conserve resources and complete the request more efficiently. Other times requests return more information than you may need. This option allows you to list the exact set of fields that the API should be sure to return for the objects. The field names should be provided as paths, described below. The id of included objects will always be returned, regardless of the field options.
    # > opt_pretty - [bool] Provides “pretty” output. Provides the response in a “pretty” format. In the case of JSON this means doing proper line breaking and indentation to make it readable. This will take extra time and increase the response size so it is advisable only to use this during debugging.
    def search_tasks_for_workspace(client, workspace_gid: required("workspace_gid"), text: nil, resource_subtype: nil, assignee_any: nil, assignee_not: nil, portfolios_any: nil, projects_any: nil, projects_not: nil, projects_all: nil, sections_any: nil, sections_not: nil, sections_all: nil, tags_any: nil, tags_not: nil, tags_all: nil, teams_any: nil, followers_not: nil, created_by_any: nil, created_by_not: nil, assigned_by_any: nil, assigned_by_not: nil, liked_by_not: nil, commented_on_by_not: nil, due_on_before: nil, due_on_after: nil, due_on: nil, due_at_before: nil, due_at_after: nil, start_on_before: nil, start_on_after: nil, start_on: nil, created_on_before: nil, created_on_after: nil, created_on: nil, created_at_before: nil, created_at_after: nil, completed_on_before: nil, completed_on_after: nil, completed_on: nil, completed_at_before: nil, completed_at_after: nil, modified_on_before: nil, modified_on_after: nil, modified_on: nil, modified_at_before: nil, modified_at_after: nil, is_blocking: nil, is_blocked: nil, has_attachment: nil, completed: nil, is_subtask: nil, sort_by: nil, sort_ascending: nil, options: {})
    path = "/workspaces/{workspace_gid}/tasks/search"
    path["{workspace_gid}"] = workspace_gid
    params = { text: text, resource_subtype: resource_subtype, "assignee.any": assignee_any, "assignee.not": assignee_not, "portfolios.any": portfolios_any, "projects.any": projects_any, "projects.not": projects_not, "projects.all": projects_all, "sections.any": sections_any, "sections.not": sections_not, "sections.all": sections_all, "tags.any": tags_any, "tags.not": tags_not, "tags.all": tags_all, "teams.any": teams_any, "followers.not": followers_not, "created_by.any": created_by_any, "created_by.not": created_by_not, "assigned_by.any": assigned_by_any, "assigned_by.not": assigned_by_not, "liked_by.not": liked_by_not, "commented_on_by.not": commented_on_by_not, due_on_before: due_on_before, due_on_after: due_on_after, due_on: due_on, due_at_before: due_at_before, due_at_after: due_at_after, start_on_before: start_on_before, start_on_after: start_on_after, start_on: start_on, created_on_before: created_on_before, created_on_after: created_on_after, created_on: created_on, created_at_before: created_at_before, created_at_after: created_at_after, completed_on_before: completed_on_before, completed_on_after: completed_on_after, completed_on: completed_on, completed_at_before: completed_at_before, completed_at_after: completed_at_after, modified_on_before: modified_on_before, modified_on_after: modified_on_after, modified_on: modified_on, modified_at_before: modified_at_before, modified_at_after: modified_at_after, is_blocking: is_blocking, is_blocked: is_blocked, has_attachment: has_attachment, completed: completed, is_subtask: is_subtask, sort_by: sort_by, sort_ascending: sort_ascending }.reject { |_,v| v.nil? || Array(v).empty? }
    Collection.new(parse(client.get(path, params: params, options: options)), type: Task, client: client)
    end

Release later than 0.10.2? Attachment IO, please!

Hi there,

I'm really keen to get the attachment IO code into the gem so I can retain the file's existing filename when uploading from a form_with file_field.

Alternatively, if there is currently a way to retain the original filename, I'd gladly implement that.

At the moment I'm using this code:

unless @params[:files].count.zero?
  @params[:files].each do |file|
    task.attach(
      filename: file.tempfile.path,
      mime: file.content_type
    )
  end
end

But I'd like to do the following (which looks possible in 11f7f33):

unless @params[:files].count.zero?
  @params[:files].each do |file|
    task.attach(
      io: file.to_io,
      filename: file.original_filename,
      mime: file.content_type
    )
  end
end

New version won't publish to rubygems

I saw we released v0.10.9 in this repo but the maximum version in rubygems.org remains at v0.10.3. Can you help check the release bot?

Captured from Rubygems

Screen Shot 2021-10-27 at 16 19 59

Problem with initializing Gem

I got the error: uninitialized constant Asana::Resources::Task::AttachmentUploading

I used the Asana gem at https://github.com/rbright/asana before, but I uninstalled this gem and double checked if the correct Asana gem is assigned in my Gemfile.lock (0.1.0) and in my bundle show. Also using irb I get the same error message when typing require 'asana'

Ruby version 2.1.4p265

.each broken

I have a very simple snippet:

tasks = ASANA.tasks.find_by_project(projectId: ASANA_A_PROJECT, per_page: 100)
tasks.each do |t|
 if t.name == 'www.google.com'
 task_id = t.id
 end
end

A couple of days ago it started crashing with:
Asana::Errors::NotFound: Either the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.

I can see .each is making a request to:
https://app.asana.com/api/1.0/api/1.0/projects/PRJ_ID/tasks?limit=100&offset=offset

Notice "/api/1.0/" twice.

┆Issue is synchronized with this Asana task

Impliment assignee_status field for client.task.find_all

This might be rolled into Issue 16 if it makes sense to include it with additional functionality in the API.

I'm working on a simple task dashboard and I'd love to use the assignee_status field with task.find_all to limit the returned data from Asana to only the tasks users have set to be completed "Today" or "Upcomming". Right now ruby-asana doesn't expose this field even though the Asana API supports it.

I'll try poking around on my own to see if I can add this, if the fix is easy I'll look at providing it to the codebase.

Question about using project as an option with tasks.find_all

Hey guys!

I'm trying to use tasks.find_all to get all tasks in a project that haven't been completed. I'm having some trouble though; it seems like project isn't an accepted default param (assignee and workspace are, but tag and project aren't). I made a change locally to add project in as a parameter, which works, but I'm also having trouble getting all tasks that are incomplete (options: {completed_since: "now" }). Did I forget something/misread the documentation?

[27] pry(main)> ASANA.tasks.find_all(options:{projectId:"id"})
Asana::Errors::InvalidRequest: Must specify exactly one of project, tag, or assignee + workspace

┆Issue is synchronized with this Asana task

Deprecation warnings with ruby 2.7

With ruby 2.7 a more strict separation of hash and keyword arguments was introduced along with aggressive deprecation notifications.

With the ruby-asana gem version 0.9.3 and ruby 2.7.0 I'm getting:

/.../lib/ruby/gems/2.7.0/gems/asana-0.9.3/lib/asana/client.rb:63: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/.../lib/ruby/gems/2.7.0/gems/asana-0.9.3/lib/asana/resources/task.rb:99: warning: The called method `create' is defined here

The culprit is a method_missing definition, and at first glance I'm not sure what the correct fix would be.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.