├── src
├── templates
│ ├── api_reference
│ │ ├── partials
│ │ │ ├── pre_description.ejs
│ │ │ ├── resource_description.ejs
│ │ │ ├── curl_example.ejs
│ │ │ ├── static_site_header.ejs
│ │ │ └── field_table_row.ejs
│ │ ├── index.js
│ │ └── resource.ejs
│ ├── ruby
│ │ ├── index.js
│ │ └── resource.ejs
│ ├── js
│ │ ├── index.js
│ │ └── resource.ejs
│ ├── python
│ │ ├── index.js
│ │ └── resource.ejs
│ ├── api_explorer
│ │ ├── index.js
│ │ └── resource.ejs
│ ├── php
│ │ ├── index.js
│ │ └── resource.ejs
│ └── java
│ │ ├── index.js
│ │ └── resource.ejs
├── resource.js
├── resources
│ ├── job.yaml
│ ├── project_membership.yaml
│ ├── workspace_membership.yaml
│ ├── portfolio_memberships.yaml
│ ├── organization_export.yaml
│ ├── custom_field_settings.yaml
│ ├── user_task_list.yaml
│ ├── team.yaml
│ ├── event.yaml
│ ├── user.yaml
│ ├── project_status.yaml
│ ├── attachment.yaml
│ ├── tag.yaml
│ ├── story.yaml
│ ├── section.yaml
│ ├── workspace.yaml
│ ├── webhook.yaml
│ ├── portfolio.yaml
│ ├── custom_fields.yaml
│ └── project.yaml
└── helpers.js
├── .npmignore
├── .travis.yml
├── .gitignore
├── LICENSE
├── package.json
├── CODE_OF_CONDUCT.md
├── test
├── resource_spec.js
└── resource_schema.json
├── README.md
└── gulpfile.js
/src/templates/api_reference/partials/pre_description.ejs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | docs
2 | examples
3 | coverage
4 | test
5 | .gitignore
6 | .jshintrc
7 | .travis.yml
8 |
--------------------------------------------------------------------------------
/src/templates/ruby/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resource: {
3 | template: 'resource.ejs',
4 | filename: function(resource, helpers) {
5 | return resource.name + '.rb';
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/src/templates/js/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resource: {
3 | template: 'resource.ejs',
4 | filename: function(resource, helpers) {
5 | return helpers.plural(resource.name) + '.js';
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/src/templates/python/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resource: {
3 | template: 'resource.ejs',
4 | filename: function(resource, helpers) {
5 | return helpers.plural(resource.name) + '.py';
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/src/templates/api_explorer/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resource: {
3 | template: 'resource.ejs',
4 | filename: function(resource, helpers) {
5 | return helpers.plural(resource.name) + '.ts';
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/src/templates/php/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resource: {
3 | template: 'resource.ejs',
4 | filename: function(resource, helpers) {
5 | return helpers.plural(helpers.classify(resource.name)) + 'Base.php';
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/src/templates/java/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resource: {
3 | template: 'resource.ejs',
4 | filename: function(resource, helpers) {
5 | return helpers.plural(helpers.classify(resource.name)) + 'Base.java';
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/src/templates/api_reference/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | resource: {
3 | template: 'resource.ejs',
4 | filename: function(resource, helpers) {
5 | return helpers.plural(resource.name) + '.md';
6 | }
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/src/templates/api_reference/partials/resource_description.ejs:
--------------------------------------------------------------------------------
1 | ### <%= thisType%>
2 | <%=partial(resource.name, "pre_description", {resource: resource})%>
3 | <%=removeLineBreaks(resource.comment)%>
4 | <%if (resource.notes){%><% _.forEach(resource.notes, function(note) {%>
5 | <%=removeLineBreaks(note)%>
6 | <%})};%>
7 |
--------------------------------------------------------------------------------
/src/templates/api_reference/partials/curl_example.ejs:
--------------------------------------------------------------------------------
1 | <%_.forEach(curl, function(example) {%><%if (example.description) {%>**<%= example.description%>**
2 |
3 | <%}%>
4 | ~~~
5 | # Request
6 | <%= example.request%> \
7 | <%= example.url%><%if (example.dataForRequest.length > 0) {%> \<%}%>
8 | <%_.forEach(example.dataForRequest, function(data, index) {
9 | %><%= data%><%if (index < example.dataForRequest.length -1) {%> \<%}%>
10 | <%});%>
11 | # Response
12 | <%= example.responseStatus%>
13 | <%= indent(example.response, '', true)%>
14 | ~~~
15 | <%});%>
16 |
--------------------------------------------------------------------------------
/src/templates/api_explorer/resource.ejs:
--------------------------------------------------------------------------------
1 | ///
2 | <%
3 | // This won't be used by the API explorer, so no reason to clutter the resources.
4 | delete resource.templates;
5 | %>
6 | /**
7 | * This file is auto-generated by the `asana-api-meta` NodeJS package.
8 | * We try to keep the generated code pretty clean but there will be lint
9 | * errors that are just not worth fixing.
10 | */
11 | /* tslint:disable:max-line-length */
12 | /* tslint:disable:eofline */
13 | var resource = <%= JSON.stringify(resource, null, 2) %>;
14 | export = resource;
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '6'
4 | sudo: false
5 | after_success:
6 | if [[ ! -z "$TRAVIS_TAG" && "$TRAVIS_PULL_REQUEST" == "false" ]]; then
7 | echo "Deploying, issuing pull requests to api-meta-incoming";
8 | git config --global user.email "git@asana.com";
9 | git config --global user.name "Asana";
10 | gulp deploy;
11 | else
12 | echo "Skipping deployment (no tag or was a pull request)";
13 | fi
14 | env:
15 | global:
16 | - GULP_DEBUG=1
17 | - PROD=1
18 | # ASANA_GITHUB_TOKEN=...
19 | - secure: "AkS+8f6jqmjcC7eIJavvpx05vAX9pljuvBIycFeUsFkHtyXCvDwiVMTkthf9dybl/xsx5jI5qxz6MsdmbO/jDIVW7+69krlJzBQiiCoZpwTFYpSk9+n2pnn5peR7rJL5RHyK8YEcmVRuG974tCFqYd2pKJu+6UEHutp/kn9VYVc="
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Deployed apps should consider commenting this line out:
24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
25 | node_modules
26 |
27 | # Documentation
28 | docs
29 |
30 | # Coverage
31 | coverage
32 |
33 | # Dist
34 | dist
35 |
36 | #IntelliJ
37 | .idea
38 |
--------------------------------------------------------------------------------
/src/templates/api_reference/partials/static_site_header.ejs:
--------------------------------------------------------------------------------
1 | ---
2 | ##### WARNING #####
3 | #
4 | # This file is auto generated upon deploy of https://github.com/Asana/asana-api-meta. To contribute please
5 | # familiarize yourself with the process as documented in the README.md found in that repository.
6 | #
7 | ##### WARNING #####
8 |
9 |
10 | parent-title: API Reference
11 | title: <%= thisType %>
12 | description: "Get documentation, references, and support for working with Asana <%= thisType %>"
13 | _template: dev-page
14 | _layout: default
15 |
16 |
17 | # Sidebar nav
18 | side_nav:
19 | <% _.forEach(resource.action_classes, function(action_class) {%><%= ' -'%>
20 | name: <%= action_class.name%>
21 | url_notrans: <%= action_class.url%>
22 | <%});%>
23 |
24 | # Header
25 | logo_text: Developers
26 |
27 | header_search: true;
28 | ---
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/resource.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | var yaml = require('js-yaml');
3 | var fs = require('fs');
4 |
5 | var resourceDir = './src/resources';
6 |
7 | function names() {
8 | var files = fs.readdirSync(resourceDir);
9 | return files.map(function(filename) {
10 | var match = /^(.*)\.yaml$/.exec(filename);
11 | return match ? match[1] : null;
12 | }).filter(function(name) {
13 | return name;
14 | }).sort();
15 | }
16 |
17 | function load(name) {
18 | var content = fs.readFileSync(resourceDir + '/' + name + '.yaml', 'utf8');
19 | content = content.replace(/\!include\s+(\S+)/g, function(match, filename) {
20 | var included = fs.readFileSync(resourceDir + '/' + filename, 'utf8');
21 | // Strip document header
22 | return included.replace(/^---+/m, '');
23 | });
24 | return yaml.load(content);
25 | }
26 |
27 | exports.names = names;
28 | exports.load = load;
29 |
30 |
--------------------------------------------------------------------------------
/src/templates/api_reference/partials/field_table_row.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | // Reminder: line breaks are very significant in Markdown tables.
3 | // The whole line with pipe characters needs to render on one line. Feel free to add newlines
4 | // for clarity, but just be aware that they should always be _inside_ a JS code block of < % \n\n % >
5 | // (minus the spaces to avoid that from being parsed) like here in this comment.
6 | %>| <%= property.name%> | <%
7 | _.forEach(property.example_values, function(value) {
8 | %><%= value%> <%
9 | });
10 | if (property.access) {
11 | %>**<%= property.access%>.** <%
12 | }
13 | %><%= removeLineBreaks(property.comment)%><%
14 | if (property.notes) {
15 | %>
**Note:** <% _.forEach(property.notes, function(note) {%><%= removeLineBreaks(note, "
")%><%})
16 | };%>|
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Asana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "asana-api-meta",
3 | "version": "0.11.2",
4 | "description": "Metadata for Asana API for generating client libraries and documentation",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "node_modules/.bin/gulp test"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/Asana/asana-api-meta.git"
12 | },
13 | "keywords": [
14 | "asana",
15 | "api"
16 | ],
17 | "author": "Greg Slovacek",
18 | "license": "MIT",
19 | "bugs": {
20 | "url": "https://github.com/Asana/asana-api-meta/issues"
21 | },
22 | "homepage": "https://github.com/Asana/asana-api-meta",
23 | "devDependencies": {
24 | "bluebird": "^2.9.34",
25 | "dateformat": "^1.0.11",
26 | "fs-extra": "^0.15.0",
27 | "github": "^0.2.4",
28 | "gulp": "^3.9.1",
29 | "gulp-bump": "^0.1.11",
30 | "gulp-git": "^2.7.0",
31 | "gulp-mocha": "^2.0.0",
32 | "gulp-rename": "^1.2.0",
33 | "gulp-tag-version": "^1.2.1",
34 | "gulp-template": "^2.0.0",
35 | "inflect": "^0.3.0",
36 | "js-yaml": "^3.13.1",
37 | "jsonschema": "^1.0.0",
38 | "lodash": "^4.17.13",
39 | "sync-to-github": "^0.1.2"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4 |
5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
6 |
7 | Examples of unacceptable behavior by participants include:
8 |
9 | * The use of sexualized language or imagery
10 | * Personal attacks
11 | * Trolling or insulting/derogatory comments
12 | * Public or private harassment
13 | * Publishing other's private information, such as physical or electronic addresses, without explicit permission
14 | * Other unethical or unprofessional conduct.
15 |
16 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
17 |
18 | This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
19 |
20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
21 |
22 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
23 |
--------------------------------------------------------------------------------
/test/resource_spec.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var fs = require('fs');
3 | var util = require('util');
4 |
5 | var Validator = require('jsonschema').Validator;
6 | var resource = require('../src/resource');
7 |
8 | describe('Resource', function() {
9 |
10 | describe('#names', function() {
11 | it('should return known resources in order', function() {
12 | assert.deepEqual(resource.names(), [
13 | 'attachment', 'custom_field_settings', 'custom_fields', 'event', 'job',
14 | 'organization_export', 'portfolio', 'portfolio_memberships', 'project',
15 | 'project_membership', 'project_status', 'section', 'story', 'tag', 'task',
16 | 'team', 'user', 'user_task_list', 'webhook', 'workspace', 'workspace_membership' ]);
17 | });
18 | });
19 |
20 | describe('#load', function() {
21 | var validator = new Validator();
22 | var schema = JSON.parse(fs.readFileSync('./test/resource_schema.json'));
23 | resource.names().forEach(function(name) {
24 | it('should load `' + name + '` conforming to resource schema', function() {
25 | var r = resource.load(name);
26 | var result = validator.validate(r, schema);
27 | var MAX_ERRORS = 10;
28 | if (result.errors.length > 0) {
29 | var lines = [
30 | "Schema validation failed with " + result.errors.length + " errors"
31 | ];
32 | result.errors.forEach(function(error, index) {
33 | if (index > MAX_ERRORS) {
34 | return;
35 | } else if (index === MAX_ERRORS) {
36 | lines.push("");
37 | lines.push("Too many errors, not showing them all.");
38 | return;
39 | }
40 | lines.push("");
41 | lines.push(
42 | "Error #" + (index + 1) + ": " + error.property + " " + error.message);
43 | lines.push(" on instance:");
44 | lines.push(util.inspect(error.instance));
45 | });
46 | // Massage output to be more readable in mocha reporter
47 | assert(false, lines.join("\n").replace(/\n/g, "\n "));
48 | }
49 | });
50 | });
51 | });
52 |
53 | });
54 |
55 |
--------------------------------------------------------------------------------
/src/templates/php/resource.ejs:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class <%= plural(classify(resource.name)) + "Base" %>
9 | {
10 | /**
11 | * @param Asana/Client client The client instance
12 | */
13 | public function __construct($client)
14 | {
15 | $this->client = $client;
16 | }<% _.forEach(resource.actions, function(action) {
17 | if (action.no_code) {
18 | return;
19 | }
20 |
21 | var name = snake(action.name);
22 | var method = action.method.toLowerCase();
23 | if (action.collection) {
24 | method += "Collection";
25 | }
26 |
27 | var extraParam = null;
28 | if (action.method === "GET") {
29 | extraParam = {
30 | name: 'params',
31 | type: 'Object',
32 | comment: 'Parameters for the request'
33 | };
34 | } else if (action.method !== 'DELETE') {
35 | extraParam = {
36 | name: 'data',
37 | type: 'Object',
38 | comment: 'Data for the request'
39 | };
40 | }
41 | var params = paramsForAction(action);
42 | var pathParamNames = params.pathParams.map(function(param) { return camel(snake(param.name), false); });
43 | var explicitParams = params.pathParams.concat(params.explicitNonPathParams).concat(extraParam ? [extraParam] : []);
44 | var documentedParams = explicitParams.concat(params.optionParams);
45 | var phpPathParamNames = pathParamNames.map(function(p) { return "$" + p; });
46 | %>
47 |
48 | /**
49 | <%= comment(action.comment, 4) %>
50 | *<% _.forEach(params.pathParams, function(param) { %>
51 | <%= comment('@param ' + param.name + ' ' + param.comment, 4) %><% }); %>
52 | * @return response
53 | */
54 | public function <%= action.name %>(<%= phpPathParamNames.concat([""]).join(", ") %>$params = array(), $options = array())
55 | {<% if (pathParamNames.length > 0) { %>
56 | $path = sprintf(<%= JSON.stringify(action.path) %>, <%= phpPathParamNames.join(", ") %>);
57 | return $this->client-><%= method %>($path, $params, $options);
58 | <% } else { %>
59 | return $this->client-><%= method %>(<%= JSON.stringify(action.path) %>, $params, $options);
60 | <% } %>}<% }); %>
61 | }
62 |
--------------------------------------------------------------------------------
/src/templates/python/resource.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | var thisType = classify(resource.name)
3 |
4 | function paramNameInComment(param) {
5 | return param.required ? param.name : ('[' + param.name + ']');
6 | }
7 | %>
8 | class _<%= thisType %>:
9 | <%= '"""' + prefix(resource.comment, " ", true)%>
10 | """
11 |
12 | def __init__(self, client=None):
13 | self.client = client
14 | <% _.forEach(resource.actions, function(action) {
15 | if (action.no_code) {
16 | return;
17 | }
18 |
19 | var name = snake(action.name);
20 | var method = action.method.toLowerCase();
21 | if (action.collection) {
22 | method += "_collection";
23 | }
24 |
25 | var extraParam = null;
26 | if (action.method === "GET") {
27 | extraParam = {
28 | name: 'params',
29 | type: 'Object',
30 | comment: 'Parameters for the request'
31 | };
32 | } else if (action.method !== 'DELETE') {
33 | extraParam = {
34 | name: 'data',
35 | type: 'Object',
36 | comment: 'Data for the request'
37 | };
38 | }
39 | var params = paramsForAction(action);
40 | var pathParamNames = params.pathParams.map(function(param) { return snake(param.name); });
41 | var explicitParams = params.pathParams.concat(params.explicitNonPathParams).concat(extraParam ? [extraParam] : []);
42 | //var documentedParams = explicitParams.concat(params.optionParams);
43 | %>
44 | def <%= name %>(self, <%= pathParamNames.concat([""]).join(", ") %>params={}, **options):
45 | """<%= prefix(action.comment, " ", true) %>
46 |
47 | Parameters
48 | ----------<% _.forEach(explicitParams, function(param) { %>
49 | <%= prefix(paramNameInComment(param) + ' : {' + typeName(param.type) + "} " +
50 | param.comment, " ", true) %><% }); %><%_.forEach(params.optionParams, function(param) {%>
51 | - <%= prefix(paramNameInComment(param) + ' : {' + typeName(param.type) + "} " +
52 | param.comment, " ", true) %><%});%>
53 | """<%
54 | if (pathParamNames.length > 0) { %>
55 | path = <%= JSON.stringify(action.path) %> % (<%= pathParamNames.join(", ") %>)
56 | return self.client.<%= method %>(path, params, **options)
57 | <% } else { %>
58 | return self.client.<%= method %>(<%= JSON.stringify(action.path) %>, params, **options)
59 | <% } %><%
60 | });
61 | %>
62 |
--------------------------------------------------------------------------------
/src/resources/job.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: job
4 | comment: |
5 | A _job_ represents a process that handles asynchronous work.
6 |
7 | Jobs are created when an endpoint requests an action that will be handled asynchronously.
8 | Such as project or task duplication.
9 |
10 | notes:
11 | - |
12 | Only the creator of the duplication process can access the duplication status
13 | of the new object.
14 | properties:
15 | - name: gid
16 | <<: *PropType.Gid
17 | comment: |
18 | Globally unique ID of the job.
19 |
20 | - name: resource_type
21 | <<: *PropType.ResourceType
22 | comment: |
23 | The resource type of this resource. The value for this resource is always `job`.
24 | example_values:
25 | - '"job"'
26 | values:
27 | - name: job
28 | comment: A job resource type.
29 |
30 | - name: resource_subtype
31 | <<: *PropType.ResourceSubtype
32 | access: Read-only
33 | comment: |
34 | The type of job.
35 | example_values:
36 | - '"duplicate_project"'
37 | - '"duplicate_task"'
38 | values:
39 | - name: duplicate_project
40 | comment: A job that duplicates a project.
41 | - name: duplicate_task
42 | comment: A job that duplicates a task.
43 |
44 | - name: status
45 | <<: *PropType.JobStatus
46 | comment:
47 | The status of the job.
48 | example_values:
49 | - '"not_started"'
50 | - '"in_progress"'
51 | - '"succeeded"'
52 | - '"failed"'
53 | values:
54 | - name: not_started
55 | comment: The job has not started.
56 | - name: in_progress
57 | comment: The job is in progress.
58 | - name: succeeded
59 | comment: The job has completed.
60 | - name: failed
61 | comment: The job has not started.
62 |
63 | - name: new_project
64 | <<: *PropType.Project
65 | access: Read-only
66 | comment: |
67 | Contains the new project if the job created a new project.
68 |
69 | - name: new_task
70 | <<: *PropType.Task
71 | access: Read-only
72 | comment: |
73 | Contains the new task if the job created a new task.
74 |
75 | action_classes:
76 | - name: Get a job
77 | url: get
78 |
79 | actions:
80 | - name: findById
81 | class: get
82 | method: GET
83 | path: "/jobs/%s"
84 | params:
85 | - name: job
86 | <<: *Param.JobGid
87 | required: true
88 | comment: The job to get.
89 | comment: |
90 | Returns the complete job record for a single job.
91 |
--------------------------------------------------------------------------------
/src/templates/java/resource.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | var hasItemRequest = false, hasCollectionRequest = false;
3 | resource.actions.forEach(function(action) {
4 | hasItemRequest = hasItemRequest || (!action.no_code && !action.collection);
5 | hasCollectionRequest = hasCollectionRequest || (!action.no_code && action.collection);
6 | });
7 | %>package com.asana.resources.gen;
8 |
9 | import com.asana.Client;
10 | import com.asana.resources.Resource;
11 | <% if (hasItemRequest || hasCollectionRequest) { %>import com.asana.models.<%= single(camel(resource.name)) %>;
12 | <% }
13 | %><% if (hasItemRequest) { %>import com.asana.requests.ItemRequest;
14 | <% }
15 | %><% if (hasCollectionRequest) { %>import com.asana.requests.CollectionRequest;
16 | <% }
17 | %>
18 | /**
19 | <%= comment(resource.comment) %>
20 | */
21 | public class <%= plural(classify(resource.name)) + "Base" %> extends Resource {
22 | /**
23 | * @param client Parent client instance
24 | */
25 | public <%= plural(classify(resource.name)) + "Base" %>(Client client) {
26 | super(client);
27 | }<% _.forEach(resource.actions, function(action) {
28 | if (action.no_code) {
29 | return;
30 | }
31 |
32 | var name = snake(action.name);
33 | var path = action.path;
34 | var method = action.method.toUpperCase();
35 | var modelClass = single(camel(resource.name));
36 | var returnClass;
37 | if (action.collection) {
38 | returnClass = "CollectionRequest<" + modelClass + ">";
39 | } else {
40 | returnClass = "ItemRequest<" + modelClass + ">";
41 | }
42 |
43 | var params = paramsForAction(action);
44 | var pathParamNames = params.pathParams.map(function(param) { return camel(snake(param.name), false); });
45 | var pathParamNamesAndTypes = params.pathParams.map(function(param) { return typeName(param.type) + " " + camel(snake(param.name), false); });
46 | %>
47 |
48 | /**
49 | <%= comment(action.comment, 4) %>
50 | *<% _.forEach(params.pathParams, function(param) { %>
51 | <%= comment('@param ' + camel(snake(param.name), false) + ' ' + param.comment, 4) %><% }); %>
52 | * @return Request object
53 | */
54 | public <%= returnClass %> <%= action.name %>(<%= pathParamNamesAndTypes.join(", ") %>) {
55 | <% if (pathParamNames.length > 0) { %>
56 | String path = String.format(<%= JSON.stringify(path) %>, <%= pathParamNames.join(", ") %>);
57 | return new <%= returnClass %>(this, <%= modelClass %>.class, path, <%= JSON.stringify(method) %>);
58 | <% } else { %>
59 | return new <%= returnClass %>(this, <%= modelClass %>.class, <%= JSON.stringify(action.path) %>, <%= JSON.stringify(method) %>);
60 | <% } %>}<% }); %>
61 | }
62 |
--------------------------------------------------------------------------------
/src/resources/project_membership.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: project_membership
5 | comment: |
6 | With the introduction of "comment-only" projects in Asana, a user's membership
7 | in a project comes with associated permissions. These permissions (whether a
8 | user has full access to the project or comment-only access) are accessible
9 | through the project memberships endpoints described here.
10 |
11 | properties:
12 |
13 | - name: id
14 | <<: *PropType.Id
15 | comment: |
16 | Globally unique ID of the project membership.
17 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
18 |
19 | - name: gid
20 | <<: *PropType.Gid
21 | comment: |
22 | Globally unique ID of the project membership.
23 |
24 | - name: resource_type
25 | <<: *PropType.ResourceType
26 | comment: |
27 | The resource type of this resource. The value for this resource is always `project_membership`.
28 | example_values:
29 | - '"project_membership"'
30 | values:
31 | - name: project_membership
32 | comment: A project membership resource type.
33 |
34 | - name: user
35 | <<: *PropType.User
36 | example_values:
37 | - '{ id: 12345, gid: "12345", name: "Tim Bizarro" }'
38 | access: Read-only
39 | comment: |
40 | The user in the membership.
41 |
42 | - name: project
43 | <<: *PropType.Project
44 | access: Read-only
45 | comment: |
46 | [Opt In](https://asana.com/developers/documentation/getting-started/input-output-options). The project the user is a member of.
47 |
48 | - name: write_access
49 | <<: *PropType.WriteAccess
50 | access: Read-only
51 | comment: |
52 | Whether the user has full access to the project or has comment-only access.
53 |
54 | action_classes:
55 | - name: Get all memberships for a project
56 | url: get-many
57 | - name: Get a project membership
58 | url: get-single
59 |
60 | actions:
61 |
62 | - name: findByProject
63 | class: get-many
64 | method: GET
65 | path: "/projects/%s/project_memberships"
66 | params:
67 | - name: project
68 | <<: *Param.ProjectGid
69 | required: true
70 | comment: The project for which to fetch memberships.
71 | - name: user
72 | <<: *Param.User
73 | comment: If present, the user to filter the memberships to.
74 | collection: true
75 | comment: |
76 | Returns the compact project membership records for the project.
77 |
78 |
79 | - name: findById
80 | class: get-single
81 | method: GET
82 | path: "/project_memberships/%s"
83 | params:
84 | - name: project_membership
85 | <<: *Param.ProjectMembershipGid
86 | required: true
87 | comment: |
88 | Returns the project membership record.
89 |
--------------------------------------------------------------------------------
/src/templates/api_reference/resource.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | /*
3 | Reminder: line breaks are significant in Markdown in certain contexts.
4 | Feel free to add newlines for clarity, but just be aware that they should always be _inside_
5 | a JS code block of < % //Commments, with\n\nwhitespace % > (minus the space between "< %", inserted to avoid parsing here)
6 | like here in this comment, which is between such a whitespace-safe code block.
7 | */
8 |
9 | // We use the type name like "Tasks" a lot, so do that first from the resource name
10 | %><%
11 | var thisType = title(plural(resource.name));
12 | %><%
13 | /*
14 | Render the static site header for page title, sidebar, etc...
15 | This is everything between the "---" blocks in the markdown file.
16 | */
17 | %><%=partial("static_site_header", {thisType: thisType, resource:resource})%>
18 |
19 | <%
20 | /*
21 | Render the stuff above the property table with the resource name and overview
22 | description of what the resource represents.
23 | */
24 | %><%=partial("resource_description", {thisType: thisType, resource: resource})%>
25 |
26 | <% //Render the table with the fields in it.
27 | %>#####<%= thisType%> have the following fields: {#fields}
28 |
29 | | Field | Description |
30 | |-------|-------------|
31 | <% _.forEach(resource.properties, function(property) {
32 | %><%=partial("field_table_row", {property: property})%>
33 | <%});%><% if (resource.extra_general_information)
34 | {%><%=resource.extra_general_information.text%><%}
35 | %>
36 | <%var examples = examplesForResource(thisType);
37 | _.forEach(resource.action_classes, function(action_class) {
38 | var matchingActions = resource.actions.filter(function(action) {
39 | return action.class == action_class.url;
40 | });
41 | %>
42 | <%= '**[' + action_class.name.toUpperCase() + '](#' + action_class.url + ')**'%><%if (action_class.comment) {%><%= '\n' + removeLineBreaks(action_class.comment)%>
43 | <%}%>
44 | <%var curl_for_keys = curlExamplesForKeys(action_class.example_keys, examples);%>
45 |
46 | <% _.forEach(matchingActions, function(action) {
47 | var params = paramsForAction(action);%>
48 |
49 | ~~~ .nohighlight
50 | <%= action.method + ' ' + genericPath(action, params.pathParams)%>
51 | ~~~
52 |
53 | <%= removeLineBreaks(action.comment)%>
54 | <%if (action.notes) {%><% _.forEach(action.notes, function(note) {%>
55 | <%= removeLineBreaks(note)%>
56 | <%})}
57 | %><%if (action.params) {%>
58 | | Parameter | Description |
59 | |---|---|<% _.forEach(action.params, function(param) {%>
60 | | <%if (_.find(params.pathParams, function(obj){return obj == param})){%><%= idIfyParamName(param.name) %><%} else {%><%= param.name %><%}%> | <%_.forEach(param.example_values, function(value) {
61 | %><%= value%> <%});%><%if (param.required) {
62 | %>**Required:** <%}%><%= removeLineBreaks(param.comment)%><%if (param.notes) {
63 | %>
**Note:** <% _.forEach(param.notes, function(note) {%><%= removeLineBreaks(note, "
")%><%})};%>|<%
64 | });}%>
65 | <%var curl = curlExamplesForAction(action, examples);%>
66 | <%= partial("curl_example", {curl: curl, indent: indent})%>
67 | <%if (action.footer) {%><%= removeLineBreaks(action.footer)%>
68 | <%}%><%});%><%= partial("curl_example", {curl: curl_for_keys, indent: indent})%>
69 | <%});%>
70 |
--------------------------------------------------------------------------------
/src/resources/workspace_membership.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: workspace_membership
5 | comment: |
6 | This object determines if a user is a member of a workspace.
7 |
8 | properties:
9 |
10 | - name: gid
11 | <<: *PropType.Gid
12 | comment: |
13 | Globally unique ID of the workspace membership.
14 |
15 | - name: resource_type
16 | <<: *PropType.ResourceType
17 | comment: |
18 | The resource type of this resource. The value for this resource is always `workspace_membership`.
19 | example_values:
20 | - '"workspace_membership"'
21 | values:
22 | - name: workspace_membership
23 | comment: A workspace membership resource type.
24 |
25 | - name: user
26 | <<: *PropType.User
27 | example_values:
28 | - '{ id: 12345, gid: "12345", name: "Tim Bizarro" }'
29 | access: Read-only
30 | comment: |
31 | The user in the membership.
32 |
33 | - name: workspace
34 | <<: *PropType.Workspace
35 | access: Read-only
36 | comment: |
37 | The workspace the user is a member of.
38 |
39 | - name: user_task_list
40 | <<: *PropType.UserTaskList
41 | access: Read-only
42 | comment: |
43 | The user's "My Tasks" in the workspace.
44 |
45 | - name: is_active
46 | <<: *PropType.Bool
47 | access: Read-only
48 | comment: |
49 | Reflects if this user still a member of the workspace.
50 |
51 | - name: is_admin
52 | <<: *PropType.Bool
53 | access: Read-only
54 | comment: |
55 | Reflects if this user is an admin of the workspace.
56 |
57 | - name: is_guest
58 | <<: *PropType.Bool
59 | access: Read-only
60 | comment: |
61 | Reflects if this user is a guest of the workspace.
62 |
63 | action_classes:
64 | - name: Get all memberships for a workspace
65 | url: get-many-workspace
66 | - name: Get all workspace memberships for a user
67 | url: get-many-user
68 | - name: Get a workspace membership
69 | url: get-single
70 |
71 | actions:
72 | - name: findByWorkspace
73 | class: get-many-workspace
74 | method: GET
75 | path: "/workspaces/%s/workspace_memberships"
76 | params:
77 | - name: workspace
78 | <<: *Param.WorkspaceGid
79 | required: true
80 | comment: The workspace for which to fetch memberships.
81 | - name: user
82 | <<: *Param.User
83 | comment: If present, the user to filter the memberships on.
84 | collection: true
85 | comment: |
86 | Returns the compact workspace membership records for the workspace.
87 |
88 | - name: findByUser
89 | class: get-many-user
90 | method: GET
91 | path: "/users/%s/workspace_memberships"
92 | params:
93 | - name: user
94 | <<: *Param.User
95 | required: true
96 | comment: If present, the user to filter the memberships on.
97 | collection: true
98 | comment: |
99 | Returns the compact workspace membership records for the user.
100 |
101 | - name: findById
102 | class: get-single
103 | method: GET
104 | path: "/workspace_memberships/%s"
105 | params:
106 | - name: workspace_membership
107 | <<: *Param.WorkspaceMembershipGid
108 | required: true
109 | comment: |
110 | Returns the workspace membership record.
111 |
--------------------------------------------------------------------------------
/src/resources/portfolio_memberships.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: portfolio_membership
5 | comment: |
6 | This object determines if a user is a member of a portfolio.
7 |
8 | properties:
9 |
10 | - name: id
11 | <<: *PropType.Id
12 | comment: |
13 | Globally unique ID of the portfolio membership.
14 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
15 |
16 | - name: gid
17 | <<: *PropType.Gid
18 | comment: |
19 | Globally unique ID of the portfolio membership.
20 |
21 | - name: resource_type
22 | <<: *PropType.ResourceType
23 | comment: |
24 | The resource type of this resource. The value for this resource is always `portfolio_membership`.
25 | example_values:
26 | - '"portfolio_membership"'
27 | values:
28 | - name: portfolio_membership
29 | comment: A portfolio membership resource type.
30 |
31 | - name: user
32 | <<: *PropType.User
33 | example_values:
34 | - '{ id: 12345, gid: "12345", name: "Tim Bizarro" }'
35 | access: Read-only
36 | comment: |
37 | The user in the membership.
38 |
39 | - name: portfolio
40 | <<: *PropType.Portfolio
41 | access: Read-only
42 | comment: |
43 | [Opt In](https://asana.com/developers/documentation/getting-started/input-output-options). The portfolio the user is a member of.
44 |
45 | action_classes:
46 | - name: Query for portfolio memberships
47 | url: query
48 | - name: Get all memberships for a portfolio
49 | url: get-many
50 | - name: Get a portfolio membership
51 | url: get-single
52 |
53 | actions:
54 |
55 | - name: findAll
56 | class: query
57 | method: GET
58 | path: "/portfolio_memberships"
59 | params:
60 | - name: portfolio
61 | <<: *Param.PortfolioGid
62 | comment: The portfolio for which to fetch memberships.
63 | - name: workspace
64 | <<: *Param.WorkspaceGid
65 | comment: The workspace for which to fetch memberships.
66 | - name: user
67 | <<: *Param.User
68 | comment: The user to filter the memberships on.
69 | collection: true
70 | comment: |
71 | Returns the compact portfolio membership records for the portfolio. You must
72 | specify `portfolio`, `portfolio` and `user`, or `workspace` and `user`.
73 |
74 | - name: findByPortfolio
75 | class: get-many
76 | method: GET
77 | path: "/portfolios/%s/portfolio_memberships"
78 | params:
79 | - name: portfolio
80 | <<: *Param.PortfolioGid
81 | required: true
82 | comment: The portfolio for which to fetch memberships.
83 | - name: user
84 | <<: *Param.User
85 | comment: If present, the user to filter the memberships on.
86 | collection: true
87 | comment: |
88 | Returns the compact portfolio membership records for the portfolio.
89 |
90 |
91 | - name: findById
92 | class: get-single
93 | method: GET
94 | path: "/portfolio_memberships/%s"
95 | params:
96 | - name: portfolio_membership
97 | <<: *Param.PortfolioMembershipGid
98 | required: true
99 | comment: |
100 | Returns the portfolio membership record.
101 |
--------------------------------------------------------------------------------
/src/resources/organization_export.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: organization_export
4 | comment: |
5 | An _organization_export_ object represents a request to export the complete data of an Organization
6 | in JSON format.
7 |
8 | To export an Organization using this API:
9 |
10 | * Create an `organization_export` [request](#create) and store the id that is returned.\
11 | * Request the `organization_export` every few minutes, until the `state` field contains 'finished'.\
12 | * Download the file located at the URL in the `download_url` field.
13 |
14 | Exports can take a long time, from several minutes to a few hours for large Organizations.
15 |
16 | **Note:** These endpoints are only available to [Service Accounts](/guide/help/premium/service-accounts)
17 | of an [Enterprise](/enterprise) Organization.
18 | properties:
19 |
20 | - name: id
21 | <<: *PropType.Id
22 | comment: |
23 | Globally unique ID of the Organization export.
24 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
25 |
26 | - name: gid
27 | <<: *PropType.Gid
28 | comment: |
29 | Globally unique ID of the Organization export.
30 |
31 | - name: created_at
32 | <<: *PropType.DateTime
33 | access: Read-only
34 | comment: |
35 | The time at which this export was requested.
36 |
37 | - name: download_url
38 | type: String
39 | example_values:
40 | - "'https://asana-export.s3.amazonaws.com/export-4632784536274-20170127-43246.json.gz?AWSAccessKeyId=xxxxxxxx'"
41 | - "null"
42 | access: Read-only
43 | comment: |
44 | Download this URL to retreive the full export of the organization in JSON format. It will be
45 | compressed in a gzip (.gz) container.
46 | notes:
47 | - |
48 | May be null if the export is still in progress or failed. If present, this URL
49 | may only be valid for 1 hour from the time of retrieval. You should avoid
50 | persisting this URL somewhere and rather refresh on demand to ensure you
51 | do not keep stale URLs.
52 |
53 | - name: state
54 | type: String
55 | example_values:
56 | - "'pending'"
57 | - "'started'"
58 | - "'finished'"
59 | - "'error'"
60 | access: Read-only
61 | comment: |
62 | The current state of the export.
63 |
64 | - name: organization
65 | <<: *PropType.Workspace
66 | comment: |
67 | The Organization that is being exported. This can only be specified at create time.
68 |
69 | action_classes:
70 | - name: Get an Organization export
71 | url: get
72 | - name: Create a request to export an Organization
73 | url: create
74 |
75 | actions:
76 |
77 | # Create, Retrieve
78 |
79 | - name: findById
80 | class: get
81 | method: GET
82 | path: "/organization_exports/%s"
83 | params:
84 | - name: organization_export
85 | <<: *Param.Gid
86 | required: true
87 | comment: |
88 | Globally unique identifier for the Organization export.
89 | comment: |
90 | Returns details of a previously-requested Organization export.
91 |
92 | - name: create
93 | class: create
94 | method: POST
95 | path: "/organization_exports"
96 | params:
97 | - name: organization
98 | <<: *Param.WorkspaceGid
99 | required: true
100 | comment: |
101 | This method creates a request to export an Organization. Asana will complete the export at some
102 | point after you create the request.
103 |
--------------------------------------------------------------------------------
/src/resources/custom_field_settings.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: custom_field_settings
5 | comment: |
6 |
7 | Custom fields are applied to a particular project or portfolio with the
8 | Custom Field Settings resource. This resource both represents the
9 | many-to-many join of the Custom Field and Project or Portfolio as well as
10 | stores information that is relevant to that particular pairing; for instance,
11 | the `is_important` property determines some possible application-specific
12 | handling of that custom field and parent.
13 |
14 | properties:
15 |
16 | - name: id
17 | <<: *PropType.Id
18 | comment: |
19 | Globally unique ID of the custom field settings object.
20 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
21 | - name: gid
22 | <<: *PropType.Gid
23 | comment: |
24 | Globally unique ID of the custom field settings object.
25 | - name: resource_type
26 | <<: *PropType.ResourceType
27 | comment: |
28 | The resource type of this resource. The value for this resource is always `custom_field_setting`
29 | example_values:
30 | - '"custom_field_setting"'
31 | values:
32 | - name: custom_field_setting
33 | comment: A custom_field_setting resource type.
34 | - name: created_at
35 | <<: *PropType.DateTime
36 | access: Read-only
37 | comment: |
38 | The time at which this custom field setting was created.
39 | - name: is_important
40 | <<: *PropType.Bool
41 | access: Read-only
42 | comment: |
43 | `is_important` is used in the Asana web application to determine if this
44 | custom field is displayed in the task list (left pane) of a project. A
45 | project can have a maximum of 5 custom field settings marked as
46 | `is_important`.
47 | - name: parent
48 | <<: *PropType.CustomFieldSettingsParent
49 | comment: |
50 | The parent to which the custom field is applied. This can be a project
51 | or portfolio and indicates that the tasks or projects that the parent
52 | contains may be given custom field values for this custom field.
53 | - name: project
54 | <<: *PropType.Project
55 | comment: |
56 | **Deprecated: new integrations should prefer the `parent` field.**
57 | The id of the project that this custom field settings refers to.
58 | - name: custom_field
59 | access: Read-only
60 | <<: *PropType.CustomField
61 | comment: |
62 | The custom field that is applied to the `parent`.
63 |
64 |
65 |
66 | action_classes:
67 | - name: Query for custom field settings on a project
68 | url: query-project-settings
69 | - name: Query for custom field settings on a portfolio
70 | url: query-portfolio-settings
71 |
72 | actions:
73 |
74 | # Create, Retrieve, Update, Delete
75 |
76 | - name: findByProject
77 | class: query-project-settings
78 | method: GET
79 | path: "/projects/%s/custom_field_settings"
80 | params:
81 | - name: project
82 | <<: *Param.ProjectGid
83 | required: true
84 | comment: The ID of the project for which to list custom field settings
85 | collection: true
86 | comment: |
87 | Returns a list of all of the custom fields settings on a project.
88 |
89 | - name: findByPortfolio
90 | class: query-portfolio-settings
91 | method: GET
92 | path: "/portfolios/%s/custom_field_settings"
93 | params:
94 | - name: portfolio
95 | <<: *Param.PortfolioGid
96 | required: true
97 | comment: The ID of the portfolio for which to list custom field settings
98 | collection: true
99 | comment: |
100 | Returns a list of all of the custom fields settings on a portfolio.
101 |
102 |
--------------------------------------------------------------------------------
/src/templates/js/resource.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | var thisType = classify(resource.name);
3 |
4 | function paramNameInComment(param) {
5 | return param.required ? param.name : ('[' + param.name + ']');
6 | }
7 | %>
8 | /**
9 | * This file is auto-generated by the `asana-api-meta` NodeJS package.
10 | * We try to keep the generated code pretty clean but there will be lint
11 | * errors that are just not worth fixing (like unused requires).
12 | * TODO: maybe we can just disable those specifically and keep this code
13 | * pretty lint-free too!
14 | */
15 | /* jshint ignore:start */
16 | var Resource = require('../resource');
17 | var util = require('util');
18 | var _ = require('lodash');
19 |
20 | /**
21 | <%= comment(resource.comment) %>
22 | * @class
23 | * @param {Dispatcher} dispatcher The API dispatcher
24 | */
25 | function <%= thisType %>(dispatcher) {
26 | Resource.call(this, dispatcher);
27 | }
28 | util.inherits(<%= thisType %>, Resource);
29 |
30 | <% _.forEach(resource.actions, function(action) {
31 | if (action.no_code) {
32 | return;
33 | }
34 | var isGet = action.method === 'GET';
35 | var optionParams = [];
36 | var requestOptionsParamName = null;
37 | var dispatchName = 'dispatch' + cap(action.method);
38 | if (isGet) {
39 | optionParams.push({
40 | name: 'params',
41 | type: 'Object',
42 | comment: 'Parameters for the request'
43 | });
44 | requestOptionsParamName = 'params';
45 | if (action.collection) {
46 | dispatchName = 'dispatchGetCollection';
47 | }
48 | } else if (action.method !== 'DELETE') {
49 | optionParams.push({
50 | name: 'data',
51 | type: 'Object',
52 | comment: 'Data for the request',
53 | required: true
54 | });
55 | requestOptionsParamName = 'data';
56 | }
57 |
58 | // Figure out how many params will be consumed by the path and put the
59 | // first N required params there - the rest go in options.
60 | var numPathParams = (action.path.match(/%/g) || []).length;
61 | var pathParams = [];
62 | var explicitNonPathParams = [];
63 | var optionChildParams = [];
64 | if (action.params) {
65 | action.params.forEach(function(param, index) {
66 | if (param.required && pathParams.length < numPathParams) {
67 | pathParams.push(param);
68 | } else if (param.explicit) {
69 | explicitNonPathParams.push(param);
70 | } else {
71 | optionChildParams.push(
72 | _.extend({}, param, {
73 | name: requestOptionsParamName + '.' + param.name
74 | }));
75 | }
76 | });
77 | }
78 |
79 | // This includes the params that go on the path plus the request data param
80 | // and the dispatchOptions param.
81 | var explicitParams = pathParams
82 | .concat(explicitNonPathParams)
83 | .concat(optionParams);
84 | var allOrderedParams = explicitParams
85 | .concat(optionChildParams);
86 |
87 | // Add a dispatchOptions as the last param to every method.
88 | var dispatchOptionsParam = {
89 | name: 'dispatchOptions',
90 | type: 'Object',
91 | comment: 'Options, if any, to pass the dispatcher for the request'
92 | };
93 | explicitParams.push(dispatchOptionsParam);
94 | allOrderedParams.push(dispatchOptionsParam);
95 |
96 | %>
97 | /**
98 | <%= comment(action.comment) %>
99 | <% _.forEach(allOrderedParams, function(param) { %><%= comment(
100 | '@param {' + typeName(param.type) + '} ' + paramNameInComment(param) + ' ' + param.comment, 2) %>
101 | <% }); %><%= comment(
102 | '@return {Promise} ' +
103 | ((isGet && !action.collection) ?
104 | 'The requested resource' :
105 | 'The response from the API'), 2) %>
106 | */
107 | <%= thisType + '.prototype.' + action.name %> = function(
108 | <% _.forEach(explicitParams, function(param, i) { %> <%= camel(snake(param.name), false) %><% if (i !== explicitParams.length - 1) { %>,
109 | <% } }); %>
110 | ) {
111 | var path = util.format('<%= action.path %>'<% _.forEach(pathParams, function(param) { %>, <%= camel(snake(param.name), false) %><% }); %>);
112 | <% if (explicitNonPathParams.length > 0) { %>
113 | <%= requestOptionsParamName %> = _.extend({}, <%= requestOptionsParamName %> || {}, {<% _.forEach(explicitNonPathParams, function(npp, npp_index) { %>
114 | <%= npp.name %>: <%= npp.name %><%= npp_index !== explicitNonPathParams.length - 1 ? "," : "" %><% }); %>
115 | });<% } %>
116 | return this.<%= dispatchName %>(path<%= requestOptionsParamName ? (', ' + requestOptionsParamName) : ''%>, dispatchOptions);
117 | };
118 | <% }); %>
119 |
120 | module.exports = <%= thisType %>;
121 | /* jshint ignore:end */
122 |
--------------------------------------------------------------------------------
/src/resources/user_task_list.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: user_task_list
4 | comment: |
5 | A _user task list_ represents the tasks assigned to a particular user. It provides API access to a user's "My Tasks" view in Asana.
6 |
7 | A user's "My Tasks" represent all of the tasks assigned to that user. It is
8 | visually divided into regions based on the task's
9 | [`assignee_status`](/developers/api-reference/tasks#field-assignee_status)
10 | for Asana users to triage their tasks based on when they can address them.
11 | When building an integration it's worth noting that tasks with due dates will
12 | automatically move through `assignee_status` states as their due dates
13 | approach; read up on [task
14 | auto-promotion](/guide/help/fundamentals/my-tasks#gl-auto-promote) for more
15 | infomation.
16 |
17 |
18 | properties:
19 |
20 | - name: id
21 | <<: *PropType.Id
22 | comment: |
23 | Globally unique ID of the user task list.
24 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
25 |
26 | - name: gid
27 | <<: *PropType.Gid
28 | comment: |
29 | Globally unique ID of the user task list.
30 |
31 | - name: resource_type
32 | <<: *PropType.ResourceType
33 | comment: |
34 | The resource type of this resource. The value for this resource is always `user_task_list`.
35 | example_values:
36 | - '"user_task_list"'
37 | values:
38 | - name: user_task_list
39 | comment: A user_task_list resource type.
40 |
41 | - name: name
42 | type: String
43 | example_values:
44 | - '"My Tasks"'
45 | comment: |
46 | The name of the user task list.
47 | access: Read-only
48 |
49 |
50 | - name: owner
51 | <<: *PropType.User
52 | comment: |
53 | The owner of the user task list, i.e. the person whose My Tasks is represented by this resource.
54 | access: Read-only
55 |
56 | - name: workspace
57 | <<: *PropType.Workspace
58 | comment: |
59 | The workspace in which the user task list is located.
60 | access: Read-only
61 |
62 | action_classes:
63 | - name: Get a user's user task list
64 | url: find-by-user
65 | - name: Get a user task list
66 | url: get
67 | - name: Get tasks in a user task list
68 | url: get-tasks
69 |
70 | actions:
71 | - name: findByUser
72 | class: find-by-user
73 | method: GET
74 | path: "/users/%s/user_task_list"
75 | params:
76 | - name: user
77 | <<: *Param.User
78 | required: true
79 | - name: workspace
80 | <<: *Param.WorkspaceGid
81 | required: true
82 | comment: |
83 | Returns the full record for the user task list for the given user
84 |
85 | - name: findById
86 | class: get
87 | method: GET
88 | path: "/user_task_lists/%s"
89 | params:
90 | - name: user_task_list
91 | <<: *Param.UserTaskListGid
92 | required: true
93 | comment: |
94 | Returns the full record for a user task list.
95 |
96 |
97 | - name: tasks
98 | class: get-tasks
99 | method: GET
100 | path: "/user_task_lists/%s/tasks"
101 | params:
102 | - name: user_task_list
103 | <<: *Param.UserTaskListGid
104 | required: true
105 | comment: The user task list in which to search for tasks.
106 | - name: completed_since
107 | <<: *Param.DateTime
108 | comment: |
109 | Only return tasks that are either incomplete or that have been
110 | completed since this time.
111 | collection: true
112 | comment: |
113 | Returns the compact list of tasks in a user's My Tasks list. The returned
114 | tasks will be in order within each assignee status group of `Inbox`,
115 | `Today`, and `Upcoming`.
116 |
117 | **Note:** tasks in `Later` have a different ordering in the Asana web app
118 | than the other assignee status groups; this endpoint will still return
119 | them in list order in `Later` (differently than they show up in Asana,
120 | but the same order as in Asana's mobile apps).
121 |
122 | **Note:** Access control is enforced for this endpoint as with all Asana
123 | API endpoints, meaning a user's private tasks will be filtered out if the
124 | API-authenticated user does not have access to them.
125 |
126 | **Note:** Both complete and incomplete tasks are returned by default
127 | unless they are filtered out (for example, setting `completed_since=now`
128 | will return only incomplete tasks, which is the default view for "My
129 | Tasks" in Asana.)
130 |
--------------------------------------------------------------------------------
/src/resources/team.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: team
4 | comment: |
5 | A _team_ is used to group related projects and people together within an
6 | organization. Each project in an organization is associated with a team.
7 | properties:
8 |
9 | - name: id
10 | <<: *PropType.Id
11 | comment: |
12 | Globally unique ID of the team.
13 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
14 |
15 | - name: gid
16 | <<: *PropType.Gid
17 | comment: |
18 | Globally unique ID of the team.
19 |
20 | - name: resource_type
21 | <<: *PropType.ResourceType
22 | comment: |
23 | The resource type of this resource. The value for this resource is always `team`.
24 | example_values:
25 | - '"team"'
26 | values:
27 | - name: team
28 | comment: A team resource type.
29 |
30 | - name: name
31 | type: String
32 | example_values:
33 | - "'Engineering'"
34 | comment: |
35 | The name of the team.
36 |
37 | - name: description
38 | type: String
39 | example_values:
40 | - "'All developers should be members of this team.'"
41 | comment: |
42 | [Opt In](/developers/documentation/getting-started/input-output-options). The description of the team.
43 |
44 | - name: html_description
45 | <<: *PropType.HtmlText
46 | example_values:
47 | - "'<body><em>All</em> developers should be members of this team.</body>'"
48 | comment: |
49 | [Opt In](/developers/documentation/getting-started/input-output-options). The description of the team with formatting as HTML.
50 |
51 | - name: organization
52 | <<: *PropType.Workspace
53 | comment: |
54 | The organization the team belongs to.
55 |
56 | action_classes:
57 | - name: Get teams in organization
58 | url: get
59 | - name: Get team members
60 | url: users
61 |
62 | actions:
63 |
64 | # Create, Retrieve, Update, Delete
65 |
66 | - name: findById
67 | class: get
68 | method: GET
69 | path: "/teams/%s"
70 | params:
71 | - name: team
72 | <<: *Param.TeamId
73 | required: true
74 | comment: |
75 | Returns the full record for a single team.
76 |
77 | - name: findByOrganization
78 | class: get
79 | method: GET
80 | path: "/organizations/%s/teams"
81 | collection: true
82 | params:
83 | - name: organization
84 | <<: *Param.WorkspaceId
85 | required: true
86 | comment: |
87 | Returns the compact records for all teams in the organization visible to
88 | the authorized user.
89 |
90 | - name: findByUser
91 | class: get
92 | method: GET
93 | path: "/users/%s/teams"
94 | collection: true
95 | params:
96 | - name: user
97 | <<: *Param.User
98 | required: true
99 | - name: organization
100 | <<: *Param.WorkspaceId
101 | comment: The workspace or organization to filter teams on.
102 | comment: |
103 | Returns the compact records for all teams to which user is assigned.
104 |
105 | # Users
106 |
107 | - name: users
108 | class: users
109 | method: GET
110 | path: "/teams/%s/users"
111 | collection: true
112 | collection_cannot_paginate: true
113 | params:
114 | - name: team
115 | <<: *Param.TeamId
116 | required: true
117 | comment: |
118 | Returns the compact records for all users that are members of the team.
119 |
120 | - name: addUser
121 | class: users
122 | method: POST
123 | path: "/teams/%s/addUser"
124 | params:
125 | - name: team
126 | <<: *Param.TeamId
127 | required: true
128 | - name: user
129 | <<: *Param.User
130 | required: true
131 | comment: |
132 | The user making this call must be a member of the team in order to add others.
133 | The user to add must exist in the same organization as the team in order to be added.
134 | The user to add can be referenced by their globally unique user ID or their email address.
135 | Returns the full user record for the added user.
136 |
137 | - name: removeUser
138 | class: users
139 | method: POST
140 | path: "/teams/%s/removeUser"
141 | params:
142 | - name: team
143 | <<: *Param.TeamId
144 | required: true
145 | - name: user
146 | <<: *Param.User
147 | required: true
148 | comment: |
149 | The user to remove can be referenced by their globally unique user ID or their email address.
150 | Removes the user from the specified team. Returns an empty data record.
151 |
--------------------------------------------------------------------------------
/test/resource_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Resource",
3 | "type": "object",
4 | "additionalProperties": false,
5 | "required": ["name", "comment", "properties", "actions"],
6 | "properties": {
7 | "templates": { "type": "array" },
8 | "name": { "type": "string" },
9 | "comment": { "type": "string" },
10 | "notes": {
11 | "type": "array",
12 | "items": { "type": "string" }
13 | },
14 | "properties": {
15 | "type": "array",
16 | "items": {
17 | "type": "object",
18 | "additionalProperties": false,
19 | "required": ["name", "type", "comment", "example_values"],
20 | "properties": {
21 | "name": { "type": "string" },
22 | "type": { "type": "string" },
23 | "comment": { "type": "string" },
24 | "notes": {
25 | "type": "array",
26 | "items": { "type": "string" }
27 | },
28 | "example_values": {
29 | "type": "array",
30 | "items": { "type": "string" },
31 | "length": { "min": 1 }
32 | },
33 | "access": { "type": "string" },
34 | "values": {
35 | "type": "array",
36 | "items": {
37 | "type": "object",
38 | "required": ["name", "comment"],
39 | "additionalProperties": false,
40 | "properties": {
41 | "name": { "type": "string" },
42 | "comment": { "type": "string" },
43 | "notes": {
44 | "type": "array",
45 | "items": { "type": "string" }
46 | }
47 | }
48 | }
49 | }
50 | }
51 | }
52 | },
53 | "extra_general_information": {
54 | "type": "object",
55 | "items": {
56 | "type": "object",
57 | "properties": {
58 | "text": { "type": "string" }
59 | }
60 | }
61 | },
62 | "action_classes": {
63 | "type": "array",
64 | "items": {
65 | "type": "object",
66 | "required": ["name", "url"],
67 | "additionalProperties": false,
68 | "properties": {
69 | "name": {"type": "string"},
70 | "url": {"type": "string"},
71 | "comment": {"type": "string"},
72 | "example_keys": {"type": "array"}
73 | }
74 | }
75 | },
76 | "actions": {
77 | "type": "array",
78 | "items": {
79 | "type": "object",
80 | "required": ["name", "method", "path", "comment"],
81 | "additionalProperties": false,
82 | "properties": {
83 | "name": { "type": "string" },
84 | "class": { "type": "string" },
85 | "method": { "type": "string" },
86 | "path": { "type": "string" },
87 | "comment": { "type": "string" },
88 | "notes": {
89 | "type": "array",
90 | "items": { "type": "string" }
91 | },
92 | "footer": { "type": "string"},
93 | "no_code": { "type": "boolean" },
94 | "collection": { "type": "boolean" },
95 | "collection_cannot_paginate": { "type": "boolean" },
96 | "params": {
97 | "type": "array",
98 | "items": {
99 | "type": "object",
100 | "required": ["name", "type", "comment"],
101 | "additionalProperties": false,
102 | "properties": {
103 | "name": { "type": "string" },
104 | "type": { "type": "string" },
105 | "required": { "type": "boolean" },
106 | "explicit": { "type": "boolean" },
107 | "comment": { "type": "string" },
108 | "notes": {
109 | "type": "array",
110 | "items": { "type": "string" }
111 | },
112 | "example_values": {
113 | "type": "array",
114 | "items": { "type": "string" },
115 | "length": { "min": 1 }
116 | },
117 | "values": {
118 | "type": "array",
119 | "items": {
120 | "type": "object",
121 | "required": ["name", "comment"],
122 | "additionalProperties": false,
123 | "properties": {
124 | "name": { "type": "string" },
125 | "comment": { "type": "string" },
126 | "notes": {
127 | "type": "array",
128 | "items": { "type": "string" }
129 | }
130 | }
131 | }
132 | }
133 | }
134 | }
135 | }
136 | }
137 | }
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/resources/event.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: event
4 | comment: |
5 | An _event_ is an object representing a change to a resource that was observed
6 | by an event subscription.
7 |
8 | In general, requesting events on a resource is faster and subject to higher
9 | rate limits than requesting the resource itself. Additionally, change events
10 | bubble up - listening to events on a project would include when stories are
11 | added to tasks in the project, even on subtasks.
12 |
13 | Establish an initial sync token by making a request with no sync token.
14 | The response will be a `412` error - the same as if the sync token had
15 | expired.
16 |
17 | Subsequent requests should always provide the sync token from the immediately
18 | preceding call.
19 |
20 | Sync tokens may not be valid if you attempt to go 'backward' in the history
21 | by requesting previous tokens, though re-requesting the current sync token
22 | is generally safe, and will always return the same results.
23 |
24 | When you receive a `412 Precondition Failed` error, it means that the
25 | sync token is either invalid or expired. If you are attempting to keep a set
26 | of data in sync, this signals you may need to re-crawl the data.
27 |
28 | Sync tokens always expire after 24 hours, but may expire sooner, depending on
29 | load on the service.
30 | properties:
31 |
32 | - name: user
33 | <<: *PropType.User
34 | access: Read-only
35 | comment: |
36 | The user who triggered the event.
37 | notes:
38 | - |
39 | The event may be triggered by a different user than the subscriber. For
40 | example, if user A subscribes to a task and user B modified it, the
41 | event’s user will be user B. Note: Some events are generated by the
42 | system, and will have `null` as the user. API consumers should make sure
43 | to handle this case.
44 |
45 | - name: resource
46 | <<: *PropType.Task
47 | comment: |
48 | The resource the event occurred on.
49 | notes:
50 | - |
51 | The resource that triggered the event may be different from the one that
52 | the events were requested for. For example, a subscription to a project
53 | will contain events for tasks contained within the project.
54 |
55 | - name: type
56 | <<: *PropType.Type
57 | values:
58 | - name: task
59 | comment: A task.
60 | - name: project
61 | comment: A project.
62 | - name: story
63 | comment: A story.
64 | access: Read-only
65 | comment: |
66 | **Deprecated: Refer to the resource_type of the resource.** The type of the resource that generated the event.
67 | notes:
68 | - |
69 | Currently, only tasks, projects and stories generate events.
70 |
71 | - name: action
72 | <<: *PropType.Action
73 | comment: |
74 | The type of action taken that triggered the event.
75 |
76 | - name: parent
77 | <<: *PropType.Project
78 | comment: |
79 | For added/removed events, the parent that resource was added to or
80 | removed from. `null` for other event types.
81 |
82 | - name: created_at
83 | <<: *PropType.DateTime
84 | access: Read-only
85 | comment: |
86 | The timestamp when the event occurred.
87 |
88 | action_classes:
89 | - name: Get events on resource
90 | url: get-all
91 | example_keys: ["get-event-on-project"]
92 |
93 | actions:
94 |
95 | - name: get
96 | class: get-all
97 | method: GET
98 | path: "/events"
99 | collection: true
100 | # Calling get collection isn't quite right since it will add limit=
101 | no_code: true
102 | params:
103 | - name: resource
104 | <<: *Param.Gid
105 | required: true
106 | explicit: true
107 | comment: |
108 | A resource ID to subscribe to. The resource can be a task or project.
109 | - name: sync
110 | type: String
111 | explicit: true
112 | example_values:
113 | - "'de4774f6915eae04714ca93bb2f5ee81'"
114 | comment: |
115 | A sync token received from the last request, or none on first sync.
116 | Events will be returned from the point in time that the sync token
117 | was generated.
118 | notes:
119 | - |
120 | On your first request, omit the sync token. The response will be the
121 | same as for an expired sync token, and will include a new valid
122 | sync token.
123 | - |
124 | If the sync token is too old (which may happen from time to time)
125 | the API will return a `412 Precondition Failed` error, and include
126 | a fresh `sync` token in the response.
127 | comment: |
128 | Returns the full record for all events that have occurred since the
129 | sync token was created.
130 | notes:
131 | - |
132 | A GET request to the endpoint `/[path_to_resource]/events` can be made
133 | in lieu of including the resource ID in the data for the request.
134 |
--------------------------------------------------------------------------------
/src/resources/user.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # Yaml file for the User resource.
3 | # Schema for all resources exists in `resource_schema.json` and is validated
4 | # as part of tests.
5 | # All resource yaml files must include the includes.
6 | # We've extended our yaml loader to do a quick-and-dirty parse of these
7 | # includes, so that our files can remain syntactically still yaml even if
8 | # semantically they only work with includes.
9 | !include ../includes.yaml
10 | name: user
11 | comment: |
12 | A _user_ object represents an account in Asana that can be given access to
13 | various workspaces, projects, and tasks.
14 |
15 | Like other objects in the system, users are referred to by numerical IDs.
16 | However, the special string identifier `me` can be used anywhere
17 | a user ID is accepted, to refer to the current authenticated user.
18 |
19 | properties:
20 |
21 | - name: id
22 | <<: *PropType.Id
23 | comment: |
24 | Globally unique ID of the user.
25 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
26 |
27 | - name: gid
28 | <<: *PropType.Gid
29 | comment: |
30 | Globally unique ID of the user.
31 |
32 | - name: resource_type
33 | <<: *PropType.ResourceType
34 | comment: |
35 | The resource type of this resource. The value for this resource is always `user`.
36 | example_values:
37 | - '"user"'
38 | values:
39 | - name: user
40 | comment: A user resource type.
41 |
42 | - name: name
43 | type: String
44 | example_values:
45 | - "'Greg Sanchez'"
46 | access: Read-only except when same user as requester
47 | comment: |
48 | The user's name.
49 |
50 | - name: email
51 | <<: *PropType.Email
52 | comment: |
53 | The user's email address.
54 |
55 | - name: photo
56 | <<: *PropType.Photo
57 | comment: |
58 | A map of the user's profile photo in various sizes, or `null` if no photo
59 | is set. Sizes provided are 21, 27, 36, 60, and 128. Images are in
60 | PNG format.
61 |
62 | # Action class refers to a general category the action falls under. These are
63 | # referenced in the actions themselves by their url because we may want to change
64 | # the exact wording of the class names in the future.
65 | action_classes:
66 | - name: Get a single user
67 | url: get-single
68 | - name: Get all users
69 | url: get-all
70 | - name: Get a user's favorites
71 | url: get-favorites
72 |
73 | actions:
74 |
75 | - name: me
76 | class: get-single
77 | method: GET
78 | path: "/users/me"
79 | comment: |
80 | Returns the full user record for the currently authenticated user.
81 |
82 | - name: findById
83 | class: get-single
84 | method: GET
85 | path: "/users/%s"
86 | params:
87 | - name: user
88 | <<: *Param.User
89 | required: true
90 | comment: |
91 | Returns the full user record for the single user with the provided ID.
92 |
93 | - name: getUserFavorites
94 | class: get-favorites
95 | method: GET
96 | path: "/users/%s/favorites"
97 | params:
98 | - name: user
99 | <<: *Param.User
100 | required: true
101 | - name: workspace
102 | <<: *Param.WorkspaceId
103 | required: true
104 | comment: The workspace in which to get favorites.
105 | - name: resource_type
106 | type: Enum
107 | example_values:
108 | - user
109 | values:
110 | - name: portfolio
111 | comment: A portfolio.
112 | - name: project
113 | comment: A project.
114 | - name: tag
115 | comment: A tag.
116 | - name: task
117 | comment: A task.
118 | - name: user
119 | comment: A user.
120 | required: true
121 | comment: The resource type of favorites to be returned.
122 | comment: |
123 | Returns all of a user's favorites in the given workspace, of the given type.
124 | Results are given in order (The same order as Asana's sidebar).
125 |
126 | - name: findByWorkspace
127 | class: get-all
128 | method: GET
129 | path: "/workspaces/%s/users"
130 | collection: true
131 | collection_cannot_paginate: true
132 | params:
133 | - name: workspace
134 | <<: *Param.WorkspaceId
135 | required: true
136 | comment: The workspace in which to get users.
137 | comment: |
138 | Returns the user records for all users in the specified workspace or
139 | organization.
140 | notes:
141 | - |
142 | Results are sorted alphabetically by user `name`s.
143 |
144 | - name: findAll
145 | class: get-all
146 | method: GET
147 | path: "/users"
148 | collection: true
149 | params:
150 | - name: workspace
151 | <<: *Param.WorkspaceId
152 | comment: The workspace or organization to filter users on.
153 | comment: |
154 | Returns the user records for all users in all workspaces and organizations
155 | accessible to the authenticated user. Accepts an optional workspace ID
156 | parameter.
157 | notes:
158 | - |
159 | Results are sorted by user ID.
160 |
--------------------------------------------------------------------------------
/src/resources/project_status.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: project_status
5 | comment: |
6 | A _project status_ is an update on the progress of a particular project, and is sent out to all project
7 | followers when created. These updates include both text describing the update and a color code intended to
8 | represent the overall state of the project: "green" for projects that are on track, "yellow" for projects
9 | at risk, and "red" for projects that are behind.
10 |
11 | Project statuses can be created and deleted, but not modified.
12 |
13 | properties:
14 |
15 | - name: id
16 | <<: *PropType.Id
17 | comment: |
18 | Globally unique ID of the project status update.
19 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
20 |
21 | - name: gid
22 | <<: *PropType.Gid
23 | comment: |
24 | Globally unique ID of the project status update.
25 |
26 | - name: resource_type
27 | <<: *PropType.ResourceType
28 | comment: |
29 | The resource type of this resource. The value for this resource is always `project_status`.
30 | example_values:
31 | - '"project_status"'
32 | values:
33 | - name: project_status
34 | comment: A project status resource type.
35 |
36 | - name: title
37 | type: String
38 | access: Read-only
39 | comment: |
40 | The title of the project status update.
41 | example_values:
42 | - "'Status Update - Jun 15'"
43 |
44 | - name: text
45 | type: String
46 | access: Read-only
47 | comment: |
48 | The text content of the status update.
49 | example_values:
50 | - "'The project is moving forward according to plan...'"
51 |
52 | - name: html_text
53 | <<: *PropType.HtmlText
54 | access: Read-only
55 | comment: |
56 | [Opt In](https://asana.com/developers/documentation/getting-started/input-output-options). The text content of the status update with formatting as HTML.
57 | example_values:
58 | - "'<body>The project <strong>is</strong> moving forward according to plan...</body>'"
59 |
60 | - name: color
61 | type: Enum
62 | access: Read-only
63 | comment: |
64 | The color associated with the status update.
65 | example_values:
66 | - "'green'"
67 | - "'yellow'"
68 | - "'red'"
69 |
70 | - name: created_by
71 | <<: *PropType.User
72 | access: Read-only
73 | comment: |
74 | The creator of the status update.
75 | example_values:
76 | - "{ id: 12345, name: 'Tim Bizarro' }"
77 |
78 | - name: created_at
79 | <<: *PropType.DateTime
80 | access: Read-only
81 | comment: |
82 | The time at which the status update was created.
83 |
84 | action_classes:
85 | - name: Create a status update
86 | url: create
87 | - name: Get status updates for a project
88 | url: query
89 | - name: Get a status update
90 | url: get-single
91 | - name: Delete a status update
92 | url: delete
93 |
94 | actions:
95 |
96 | # Create, Retrieve, Update, Delete
97 |
98 | - name: createInProject
99 | class: create
100 | method: POST
101 | path: "/projects/%s/project_statuses"
102 | params:
103 | - name: project
104 | <<: *Param.ProjectGid
105 | required: true
106 | comment: The project on which to create a status update.
107 | - name: text
108 | type: String
109 | required: true
110 | comment: |
111 | The text of the project status update.
112 | example_values:
113 | - "The project is on track to ship next month!"
114 | - name: color
115 | <<: *Param.ProjectStatusColor
116 | required: true
117 | comment: |
118 | The color to associate with the status update. Must be one of `"red"`, `"yellow"`, or `"green"`.
119 | comment: |
120 | Creates a new status update on the project.
121 |
122 | Returns the full record of the newly created project status update.
123 |
124 | - name: findByProject
125 | class: query
126 | method: GET
127 | path: "/projects/%s/project_statuses"
128 | params:
129 | - name: project
130 | <<: *Param.ProjectGid
131 | required: true
132 | comment: The project to find status updates for.
133 | collection: true
134 | comment: |
135 | Returns the compact project status update records for all updates on the project.
136 |
137 | - name: findById
138 | class: get-single
139 | method: GET
140 | path: "/project_statuses/%s"
141 | params:
142 | - name: project-status
143 | <<: *Param.ProjectStatusUpdateGid
144 | required: true
145 | comment: The project status update to get.
146 | comment: |
147 | Returns the complete record for a single status update.
148 |
149 | - name: delete
150 | class: delete
151 | method: DELETE
152 | path: "/project_statuses/%s"
153 | params:
154 | - name: project-status
155 | <<: *Param.ProjectStatusUpdateGid
156 | required: true
157 | comment: The project status update to delete.
158 | comment: |
159 | Deletes a specific, existing project status update.
160 |
161 | Returns an empty data record.
162 |
--------------------------------------------------------------------------------
/src/resources/attachment.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: attachment
4 | comment: |
5 | An _attachment_ object represents any file attached to a task in Asana,
6 | whether it's an uploaded file or one associated via a third-party service
7 | such as Dropbox or Google Drive.
8 | properties:
9 |
10 | - name: id
11 | <<: *PropType.Id
12 | comment: |
13 | Globally unique ID of the attachment.
14 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
15 |
16 | - name: gid
17 | <<: *PropType.Gid
18 | comment: |
19 | Globally unique ID of the attachment.
20 |
21 | - name: resource_type
22 | <<: *PropType.ResourceType
23 | comment: |
24 | The resource type of this resource. The value for this resource is always `attachment`.
25 | example_values:
26 | - '"attachment"'
27 | values:
28 | - name: attachment
29 | comment: An attachment resource type.
30 |
31 | - name: created_at
32 | <<: *PropType.DateTime
33 | access: Read-only
34 | comment: |
35 | The time at which this attachment was uploaded.
36 |
37 | - name: download_url
38 | type: String
39 | example_values:
40 | - "'https://www.dropbox.com/s/123/Screenshot.png?dl=1'"
41 | - "null"
42 | access: Read-only
43 | comment: |
44 | The URL containing the content of the attachment.
45 | notes:
46 | - |
47 | May be `null` if the attachment is hosted by box. If present, this URL
48 | may only be valid for 1 hour from the time of retrieval. You should avoid
49 | persisting this URL somewhere and just refresh it on demand to ensure you
50 | do not keep stale URLs.
51 |
52 | - name: host
53 | type: String
54 | example_values:
55 | - "'dropbox'"
56 | access: Read-only
57 | comment: |
58 | The service hosting the attachment. Valid values are `asana`, `dropbox`,
59 | `gdrive` and `box`.
60 |
61 | - name: name
62 | type: String
63 | example_values:
64 | - "'Screenshot.png'"
65 | access: Read-only
66 | comment: |
67 | The name of the file.
68 |
69 | - name: parent
70 | <<: *PropType.Task
71 | comment: |
72 | The task this attachment is attached to.
73 |
74 | - name: view_url
75 | type: String
76 | example_values:
77 | - "'https://www.dropbox.com/s/123/Screenshot.png'"
78 | - "null"
79 | access: Read-only
80 | comment: |
81 | The URL where the attachment can be viewed, which may be friendlier to
82 | users in a browser than just directing them to a raw file.
83 |
84 | action_classes:
85 | - name: Get single attachment
86 | url: get-single
87 | - name: Get all attachments
88 | url: get-all-task
89 | - name: Upload an attachment
90 | url: upload
91 |
92 | actions:
93 |
94 | # Create, Retrieve, Update, Delete
95 |
96 | - name: findById
97 | class: get-single
98 | method: GET
99 | path: "/attachments/%s"
100 | params:
101 | - name: attachment
102 | <<: *Param.AttachmentGid
103 | required: true
104 | comment: |
105 | Returns the full record for a single attachment.
106 |
107 | - name: findByTask
108 | class: get-all-task
109 | method: GET
110 | path: "/tasks/%s/attachments"
111 | collection: true
112 | params:
113 | - name: task
114 | <<: *Param.TaskGid
115 | required: true
116 | comment: |
117 | Returns the compact records for all attachments on the task.
118 |
119 | - name: createOnTask
120 | class: upload
121 | method: POST
122 | path: "/tasks/%s/attachments"
123 | no_code: true # Uploading attachments must be hand-coded
124 | params:
125 | - name: task
126 | <<: *Param.TaskGid
127 | required: true
128 | - name: file
129 | type: File
130 | example_values:
131 | - file.txt
132 | required: true
133 | comment: The file you want to upload.
134 | notes:
135 | - |
136 | **When using curl:**
137 |
138 | Be sure to add an '@' before the file path, and use the --form
139 | option instead of the -d option.
140 |
141 | When uploading PDFs with curl, force the content-type to be pdf by
142 | appending the content type to the file path: --form
143 | "file=@file.pdf;type=application/pdf".
144 | comment: |
145 | This method uploads an attachment to a task and returns the compact
146 | record for the created attachment object. It is not possible to attach
147 | files from third party services such as Dropbox, Box & Google Drive via
148 | the API. You must download the file content first and then upload it as any
149 | other attachment.
150 |
151 | The 100MB size limit on attachments in Asana is enforced on this endpoint.
152 | notes:
153 | - |
154 | This endpoint expects a multipart/form-data encoded request containing
155 | the full contents of the file to be uploaded.
156 |
157 | Below is an example of what a well formed multipart/form-data encoded
158 | request might look like.
159 |
160 | Authorization: Bearer \
161 | Content-Type: multipart/form-data; boundary=\
162 | User-Agent: Java/1.7.0_76\
163 | Host: localhost\
164 | Accept: */*\
165 | Connection: keep-alive\
166 | Content-Length: 141
167 |
168 | --\
169 | Content-Disposition: form-data; name="file"; filename="example.txt"\
170 | Content-Type: text/plain
171 |
172 |
173 |
174 | ----
175 |
176 | Requests made should follow the HTTP/1.1 specification that line terminators are of the form `CRLF` or `\r\n`
177 | outlined [here](http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01#Basic-Rules) in order for the server
178 | to reliably and properly handle the request.
179 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Asana API Metadata [![Build Status][travis-image]][travis-url]
2 | Metadata for Asana API for generating client libraries and documentation
3 |
4 | This repository contains descriptions of the various resources in the API and their endpoints. The metadata is rich in structural information and comments, such that we can use it to build both documentation and functioning client library objects.
5 |
6 | It is currently used to build the following client libraries:
7 |
8 | * [`java-asana`](https://github.com/Asana/java-asana)
9 | * [`node-asana`](https://github.com/Asana/node-asana)
10 | * [`php-asana`](https://github.com/Asana/php-asana)
11 | * [`python-asana`](https://github.com/Asana/python-asana)
12 | * [`ruby-asana`](https://github.com/Asana/ruby-asana)
13 |
14 | It is also used to build the [Asana API Reference](https://asana.com/developers/api-reference) in the developer documentation.
15 |
16 | ## Contributing
17 |
18 | 1. Clone the repo:
19 | `git clone git@github.com:Asana/asana-api-meta.git`
20 | 2. Make a topic branch:
21 | `git checkout -b my-topic-branch`
22 | 3. Make changes on the topic branch.
23 | 4. Run `gulp build` to build the output for all languages. You can inspect the final output in `dist/`.
24 | 5. When satisfied, make a pull request.
25 |
26 | ## How It Works
27 |
28 | ### Language Configuration
29 |
30 | Each language has its own configuration that determines how the output files are built. These configurations are specified in `gulpfile.js` as `var languages = ...`. Each record has the following schema:
31 |
32 | * `repo`: Name of the target repository where built files will be pushed to.
33 | * `branch`: Name of the branch in the repository to push to.
34 | * `outputPath`: Path, relative to the root of `repo`, where template output will go.
35 | * `preserveRepoFiles`: Set to true if when the files are built and pushed to the target, any existing files are preserved. If false, it will clear out `outputPath` each time it pushes.
36 | * `skip`: An array of resource names to avoid generating output files for.
37 |
38 | ### Resource Definitions
39 |
40 | Resources are defined in individual YAML files under `src/resources`. The one-resource-per-file convention keeps the files manageable.
41 |
42 | A schema is provided for resources, and the resources are validated against it during testing. It can be found in `test/resource_schema.json`, and uses the [JSON Schema](http://json-schema.org/) syntax, with the [`jsonschema`](http://json-schema.org/) Node package for validation.
43 |
44 | The schemas make heavy use of the "anchor" and "alias" features of YAML, so the files can be more normalized and avoid excessive repetition. These are mostly used to define commonly-used components like types, properties, and parameters that appear in multiple (sometimes many, many) places.
45 |
46 | Definitions for value types (like `Email`, `ProjectColor`, `TeamId` etc.) that may appear as a parameter or resource property in more than one place should go in the `src/includes.yaml` file.
47 |
48 | ### Templates
49 |
50 | This module uses templates for generating library source files from the resources.
51 |
52 | The build system will, for each language `LANG` it is building (e.g. `LANG='js'`):
53 | 1. For each resource:
54 | 2. Read in the resource definition file, `src/resources/NAME.yaml`.
55 | 3. Read in the template definition file, `src/templates/LANG/index.js`.
56 | 4. Find the `resource` key.
57 | 5. Read in the `template` to find the input template, and the `filename` function to generate the output filename.
58 | 4. Execute the template against the resource definition.
59 | 5. Output the result into the file `dist/LANG/OUTPUTFILE`.
60 |
61 | All templates use the [`lodash`](https://www.npmjs.com/package/lodash) library for generation. `gulpfile.js` has the build rules that execute the templates. It provides various helpers to the template that are configurable on a per-library basis, by scoping the file `helpers.js` into the template namespace. These include utilities for common code-generation patterns.
62 |
63 | Authors modifying the templates should ensure that they *generate pretty code*, at the expense of the prettiness of the template. Trivial issues like bad indents in the output should be fixed.
64 |
65 | ### Helpers
66 |
67 | Rather than pour a bunch of logic into the template, it's better style to put them into helpers. Currently there is only a single `helpers` file, but we should break this into a set of language-specific files (that might each call some useful common helpers where appropriate).
68 |
69 | Examples of places where extracting helpers is useful are `paramsForAction` or `wrapComment`.
70 |
71 | ## Owner Workflow
72 |
73 | ### Testing Proposed Changes
74 |
75 | 1. Get a personal access token for GitHub and assign it to the environment variable `ASANA_GITHUB_TOKEN`:
76 | `export ASANA_GITHUB_TOKEN=...`
77 | 2. Run a test deployment for a single language, e.g. `gulp deploy-js`. This will create a new branch in the target repo and deploy the generated files to that branch. The branch will be named for your GitHub username and a date-timestamp, for example `asanabot-20150531-012345`.
78 | 3. Inspect the diffs on GitHub. If you need to make changes you can re-run the deploy and it will create a new branch.
79 | 4. You can do a test deploy to all languages at once by running just `gulp deploy`.
80 |
81 | ### Committing
82 |
83 | 1. Push changes to origin `git push origin my-topic-branch`.
84 | 2. Make a pull request in GitHub. This will automatically create a task in Asana.
85 | 3. Once your request is reviewed, it can be merged.
86 |
87 | ### Deploying
88 |
89 | 1. Clone the repo, work on master.
90 | 2. Bump the package version to indicate the [semantic version](http://semver.org/) change, using one of: `gulp bump-patch`, `gulp bump-feature`, or `gulp bump-release`
91 | 3. Push changes to origin, including tags:
92 | `git push origin master --tags`
93 |
94 | ### Propagating Changes to Client Libraries
95 |
96 | 1. Travis will automatically build and deploy new code to the `api-meta-incoming` branch of all the repos, creating pull requests for each.
97 | 2. Review and merge the pull requests as appropriate.
98 | 3. Update package versions according to [semantic versioning](http://semver.org/), and push.
99 |
100 |
101 | [travis-url]: http://travis-ci.org/Asana/asana-api-meta
102 | [travis-image]: https://api.travis-ci.org/Asana/asana-api-meta.svg?style=flat-square&branch=master
103 |
--------------------------------------------------------------------------------
/src/resources/tag.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: tag
5 | comment: |
6 | A _tag_ is a label that can be attached to any task in Asana. It exists in a
7 | single workspace or organization.
8 |
9 | Tags have some metadata associated with them, but it is possible that we will
10 | simplify them in the future so it is not encouraged to rely too heavily on it.
11 | Unlike projects, tags do not provide any ordering on the tasks they
12 | are associated with.
13 |
14 | properties:
15 | # We borrow a bunch of the project templates here because they're literally
16 | # exactly the same
17 |
18 | - name: id
19 | <<: *PropType.Id
20 | comment: |
21 | Globally unique ID of the tag.
22 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
23 |
24 | - name: gid
25 | <<: *PropType.Gid
26 | comment: |
27 | Globally unique ID of the tag.
28 |
29 | - name: resource_type
30 | <<: *PropType.ResourceType
31 | comment: |
32 | The resource type of this resource. The value for this resource is always `tag`.
33 | example_values:
34 | - '"tag"'
35 | values:
36 | - name: tag
37 | comment: A tag resource type.
38 |
39 | - name: created_at
40 | <<: *PropType.DateTime
41 | access: Read-only
42 | comment: |
43 | The time at which this tag was created.
44 |
45 | - name: followers
46 | <<: *PropType.UserArray
47 | access: Read-only
48 | comment: |
49 | Array of users following this tag.
50 |
51 | - name: name
52 | <<: *PropType.PotName
53 | comment: |
54 | Name of the tag. This is generally a short sentence fragment that fits
55 | on a line in the UI for maximum readability. However, it can be longer.
56 |
57 | - name: color
58 | <<: *PropType.PotColor
59 | comment: |
60 | Color of the tag. Must be either `null` or one of: `dark-pink`,
61 | `dark-green`, `dark-blue`, `dark-red`, `dark-teal`, `dark-brown`,
62 | `dark-orange`, `dark-purple`, `dark-warm-gray`, `light-pink`, `light-green`,
63 | `light-blue`, `light-red`, `light-teal`, `light-yellow`, `light-orange`,
64 | `light-purple`, `light-warm-gray`.
65 |
66 | - name: workspace
67 | <<: *PropType.Workspace
68 | comment: |
69 | The workspace or organization this tag is associated with. Once created,
70 | tags cannot be moved to a different workspace. This attribute can only
71 | be specified at creation time.
72 |
73 | action_classes:
74 | - name: Create a tag
75 | url: create
76 | - name: Get a single tag
77 | url: get-single
78 | - name: Update a tag
79 | url: update
80 | - name: Query for tags
81 | url: query
82 |
83 | actions:
84 |
85 | # Create, Retrieve, Update, Delete
86 |
87 | - name: create
88 | class: create
89 | method: POST
90 | path: "/tags"
91 | params:
92 | - name: workspace
93 | <<: *Param.WorkspaceGid
94 | required: true
95 | comment: The workspace or organization to create the tag in.
96 | comment: |
97 | Creates a new tag in a workspace or organization.
98 |
99 | Every tag is required to be created in a specific workspace or
100 | organization, and this cannot be changed once set. Note that you can use
101 | the `workspace` parameter regardless of whether or not it is an
102 | organization.
103 |
104 | Returns the full record of the newly created tag.
105 |
106 | - name: createInWorkspace
107 | class: create
108 | method: POST
109 | path: "/workspaces/%s/tags"
110 | params:
111 | - name: workspace
112 | <<: *Param.WorkspaceGid
113 | required: true
114 | comment: The workspace or organization to create the tag in.
115 | comment: |
116 | Creates a new tag in a workspace or organization.
117 |
118 | Every tag is required to be created in a specific workspace or
119 | organization, and this cannot be changed once set. Note that you can use
120 | the `workspace` parameter regardless of whether or not it is an
121 | organization.
122 |
123 | Returns the full record of the newly created tag.
124 |
125 | - name: findById
126 | class: get-single
127 | method: GET
128 | path: "/tags/%s"
129 | params:
130 | - name: tag
131 | <<: *Param.TagGid
132 | required: true
133 | comment: The tag to get.
134 | comment: |
135 | Returns the complete tag record for a single tag.
136 |
137 | - name: update
138 | class: update
139 | method: PUT
140 | path: "/tags/%s"
141 | params:
142 | - name: tag
143 | <<: *Param.TagGid
144 | required: true
145 | comment: The tag to update.
146 | comment: |
147 | Updates the properties of a tag. Only the fields provided in the `data`
148 | block will be updated; any unspecified fields will remain unchanged.
149 |
150 | When using this method, it is best to specify only those fields you wish
151 | to change, or else you may overwrite changes made by another user since
152 | you last retrieved the task.
153 |
154 | Returns the complete updated tag record.
155 |
156 | - name: delete
157 | class: delete
158 | method: DELETE
159 | path: "/tags/%s"
160 | params:
161 | - name: tag
162 | <<: *Param.TagGid
163 | required: true
164 | comment: The tag to delete.
165 | comment: |
166 | A specific, existing tag can be deleted by making a DELETE request
167 | on the URL for that tag.
168 |
169 | Returns an empty data record.
170 |
171 | - name: findAll
172 | class: query
173 | method: GET
174 | path: "/tags"
175 | collection: true
176 | comment: |
177 | Returns the compact tag records for some filtered set of tags.
178 | Use one or more of the parameters provided to filter the tags returned.
179 | params:
180 | - name: workspace
181 | <<: *Param.WorkspaceGid
182 | comment: The workspace or organization to filter tags on.
183 | - name: team
184 | <<: *Param.TeamGid
185 | comment: The team to filter tags on.
186 | - name: archived
187 | <<: *Param.Bool
188 | comment: |
189 | Only return tags whose `archived` field takes on the value of
190 | this parameter.
191 |
192 | - name: findByWorkspace
193 | class: query
194 | method: GET
195 | path: "/workspaces/%s/tags"
196 | params:
197 | - name: workspace
198 | <<: *Param.WorkspaceGid
199 | required: true
200 | comment: The workspace or organization to find tags in.
201 | collection: true
202 | comment: |
203 | Returns the compact tag records for all tags in the workspace.
204 |
--------------------------------------------------------------------------------
/src/resources/story.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: story
4 | comment: |
5 | A _story_ represents an activity associated with an object in the Asana
6 | system. Stories are generated by the system whenever users take actions such
7 | as creating or assigning tasks, or moving tasks between projects. _Comments_
8 | are also a form of user-generated story.
9 |
10 | properties:
11 |
12 | - name: id
13 | <<: *PropType.Id
14 | comment: |
15 | Globally unique ID of the story.
16 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
17 |
18 | - name: gid
19 | <<: *PropType.Gid
20 | comment: |
21 | Globally unique ID of the story.
22 |
23 | - name: resource_type
24 | <<: *PropType.ResourceType
25 | comment: |
26 | The resource type of this resource. The value for this resource is always `story`.
27 | example_values:
28 | - '"story"'
29 | values:
30 | - name: story
31 | comment: A story resource type.
32 |
33 | - name: resource_subtype
34 | <<: *PropType.ResourceSubtype
35 | access: Read-only
36 | comment: |
37 | The type of story. This provides fine-grained information about what triggered the story's creation. There are many story subtypes, so inspect the data returned from Asana's API to find the value for your use case.
38 | example_values:
39 | - '"comment_added"'
40 | - '"description_changed"'
41 | - '"liked"'
42 | - '...'
43 |
44 | - name: created_at
45 | <<: *PropType.DateTime
46 | comment: |
47 | The time at which this story was created.
48 |
49 | - name: created_by
50 | <<: *PropType.User
51 | comment: |
52 | The user who created the story.
53 |
54 | - name: liked
55 | <<: *PropType.Bool
56 | comment: |
57 | True if the story is liked by the authorized user, false if not.
58 | notes:
59 | - |
60 | This property only exists for stories that provide likes.
61 |
62 | - name: likes
63 | <<: *PropType.UserArray
64 | access: Read-only
65 | comment: |
66 | Array of users who have liked this story.
67 | notes:
68 | - |
69 | This property only exists for stories that provide likes.
70 |
71 | - name: num_likes
72 | <<: *PropType.Count
73 | access: Read-only
74 | comment: |
75 | The number of users who have liked this story.
76 | notes:
77 | - |
78 | This property only exists for stories that provide likes.
79 |
80 | - name: text
81 | type: String
82 | example_values:
83 | - "'marked today'"
84 | access: Create-only
85 | comment: |
86 | Human-readable text for the story or comment. This will not include the
87 | name of the creator.
88 | notes:
89 | - |
90 | This is not guaranteed to be stable for a given type of story. For
91 | example, text for a reassignment may **not** always say “assigned to …”
92 | as the text for a story can both be edited and change based on the language settings
93 | of the user making the request.
94 | Use the `resource_subtype` property to discover the action that created the story.
95 |
96 | - name: html_text
97 | <<: *PropType.HtmlText
98 | comment: |
99 | [Opt In](https://asana.com/developers/documentation/getting-started/input-output-options). HTML formatted text for a comment.
100 |
101 | - name: target
102 | <<: *PropType.Task
103 | comment: |
104 | The object this story is associated with. Currently may only be a task.
105 |
106 | - name: is_pinned
107 | <<: *PropType.Bool
108 | comment: |
109 | Whether the story is pinned on the target.
110 | notes:
111 | - This field is only present on comment and attachment stories.
112 |
113 | - name: is_edited
114 | <<: *PropType.Bool
115 | comment: |
116 | Whether the text of the story has been edited after creation.
117 | notes:
118 | - This field is only present on comment stories.
119 |
120 | - name: source
121 | <<: *PropType.StorySource
122 | comment: |
123 | The component of the Asana product the user used to create the story.
124 |
125 | - name: type
126 | <<: *PropType.StoryType
127 | comment: |
128 | **Deprecated: new integrations should prefer the `resource_subtype` field.**
129 | The type of this story. For more fine-grained inspection of story types, see the [`resource_subtype`](#field-resource_subtype) property.
130 |
131 | action_classes:
132 | - name: Get stories on object
133 | url: get-all
134 | - name: Get a single story
135 | url: get-single
136 | - name: Commenting on an object
137 | url: post-comment
138 | - name: Update a story
139 | url: update
140 | - name: Delete a story
141 | url: delete
142 |
143 | actions:
144 |
145 | # Create, Retrieve, Update, Delete
146 |
147 | - name: findByTask
148 | class: get-all
149 | method: GET
150 | path: "/tasks/%s/stories"
151 | collection: true
152 | params:
153 | - name: task
154 | <<: *Param.TaskGid
155 | required: true
156 | comment: |
157 | Returns the compact records for all stories on the task.
158 |
159 | - name: findById
160 | class: get-single
161 | method: GET
162 | path: "/stories/%s"
163 | params:
164 | - name: story
165 | <<: *Param.StoryGid
166 | required: true
167 | comment: |
168 | Returns the full record for a single story.
169 |
170 | - name: createOnTask
171 | class: post-comment
172 | <<: *Action.CommentOnTask
173 |
174 | - name: update
175 | class: update
176 | method: PUT
177 | path: "/stories/%s"
178 | params:
179 | - name: story
180 | <<: *Param.StoryGid
181 | required: true
182 | - name: text
183 | type: String
184 | example_values:
185 | - "'This is a comment.'"
186 | comment: |
187 | The plain text with which to update the comment.
188 | - name: html_text
189 | type: String
190 | example_values:
191 | - "'Get whatever <a href='https://app.asana.com/0/1123/1234'>Sashimi</a> has.'"
192 | comment: The rich text with which to update the comment.
193 | - name: is_pinned
194 | <<: *Param.Bool
195 | comment: Whether the story should be pinned on the resource.
196 | comment: |
197 | Updates the story and returns the full record for the updated story.
198 | Only comment stories can have their text updated, and only comment stories and
199 | attachment stories can be pinned. Only one of `text` and `html_text` can be specified.
200 |
201 | - name: delete
202 | class: delete
203 | method: DELETE
204 | path: "/stories/%s"
205 | params:
206 | - name: story
207 | <<: *Param.StoryGid
208 | required: true
209 | comment: |
210 | Deletes a story. A user can only delete stories they have created. Returns an empty data record.
211 |
--------------------------------------------------------------------------------
/src/resources/section.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: section
5 | comment: |
6 | A _section_ is a subdivision of a project that groups tasks together. It can
7 | either be a header above a list of tasks in a list view or a column in a
8 | board view of a project.
9 |
10 | notes:
11 | - |
12 | Sections are largely a shared idiom in Asana's API for both list and board
13 | views of a project regardless of the project's layout.
14 |
15 | The 'memberships' property when [getting a
16 | task](/developers/api-reference/tasks#get) will return the information for
17 | the section or the column under 'section' in the response.
18 |
19 | properties:
20 |
21 | - name: id
22 | <<: *PropType.Id
23 | comment: |
24 | Globally unique ID of the section.
25 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
26 | - name: gid
27 | <<: *PropType.Gid
28 | comment: |
29 | Globally unique ID of the section.
30 | - name: resource_type
31 | <<: *PropType.ResourceType
32 | comment: |
33 | The resource type of this resource. The value for this resource is always `section`.
34 | example_values:
35 | - '"section"'
36 | values:
37 | - name: section
38 | comment: A section resource type.
39 | - name: name
40 | <<: *PropType.SectionName
41 | comment: |
42 | The name of the section (i.e. the text displayed as the section header).
43 |
44 | - name: project
45 | <<: *PropType.Project
46 | access: Read-only
47 | comment: |
48 | The project which contains the section.
49 |
50 | - name: created_at
51 | <<: *PropType.DateTime
52 | access: Read-only
53 | comment: |
54 | The time at which the section was created.
55 |
56 |
57 | action_classes:
58 | - name: Create a section
59 | url: create
60 | - name: Get sections in a project
61 | url: find-project
62 | - name: Get a single section
63 | url: get-single
64 | - name: Update a section
65 | url: update
66 | - name: Delete a section
67 | url: delete
68 | - name: Move a section in a project
69 | url: reorder
70 |
71 | actions:
72 |
73 | # Create, Retrieve, Update, Delete
74 |
75 | - name: createInProject
76 | class: create
77 | method: POST
78 | path: "/projects/%s/sections"
79 | params:
80 | - name: project
81 | <<: *Param.ProjectGid
82 | required: true
83 | comment: The project to create the section in
84 | - name: name
85 | <<: *Param.SectionName
86 | comment: The text to be displayed as the section name. This cannot be an empty string.
87 | required: true
88 | comment: |
89 | Creates a new section in a project.
90 |
91 | Returns the full record of the newly created section.
92 |
93 | - name: findByProject
94 | class: find-project
95 | method: GET
96 | path: "/projects/%s/sections"
97 | params:
98 | - name: project
99 | <<: *Param.ProjectGid
100 | required: true
101 | comment: The project to get sections from.
102 | collection: true
103 | comment: |
104 | Returns the compact records for all sections in the specified project.
105 |
106 | - name: findById
107 | class: get-single
108 | method: GET
109 | path: "/sections/%s"
110 | params:
111 | - name: section
112 | <<: *Param.SectionGid
113 | required: true
114 | comment: The section to get.
115 | comment: |
116 | Returns the complete record for a single section.
117 |
118 | - name: update
119 | class: update
120 | method: PUT
121 | path: "/sections/%s"
122 | params:
123 | - name: section
124 | <<: *Param.SectionGid
125 | required: true
126 | comment: The section to update.
127 | comment: |
128 | A specific, existing section can be updated by making a PUT request on
129 | the URL for that project. Only the fields provided in the `data` block
130 | will be updated; any unspecified fields will remain unchanged. (note that
131 | at this time, the only field that can be updated is the `name` field.)
132 |
133 | When using this method, it is best to specify only those fields you wish
134 | to change, or else you may overwrite changes made by another user since
135 | you last retrieved the task.
136 |
137 | Returns the complete updated section record.
138 |
139 | - name: delete
140 | class: delete
141 | method: DELETE
142 | path: "/sections/%s"
143 | params:
144 | - name: section
145 | <<: *Param.SectionGid
146 | required: true
147 | comment: The section to delete.
148 | comment: |
149 | A specific, existing section can be deleted by making a DELETE request
150 | on the URL for that section.
151 |
152 | Note that sections must be empty to be deleted.
153 |
154 | The last remaining section in a board view cannot be deleted.
155 |
156 | Returns an empty data block.
157 |
158 | - name: addTask
159 | class: reorder
160 | method: POST
161 | path: "/sections/%s/addTask"
162 | params:
163 | - name: section
164 | <<: *Param.SectionGid
165 | required: true
166 | comment: The section in which to add the task
167 | - name: task
168 | <<: *Param.TaskGid
169 | required: true
170 | comment: The task to add to this section
171 | - name: insert_before
172 | <<: *Param.Gid
173 | comment: Insert the given task immediately before the task specified by this parameter. Cannot be provided together with `insert_after`.
174 | - name: insert_after
175 | <<: *Param.Gid
176 | comment: Insert the given task immediately after the task specified by this parameter. Cannot be provided together with `insert_before`.
177 | comment: |
178 | Add a task to a specific, existing section. This will remove the task from other sections of the project.
179 |
180 | The task will be inserted at the top of a section unless an `insert_before` or `insert_after` parameter is declared.
181 |
182 | This does not work for separators (tasks with the `resource_subtype` of section).
183 |
184 | - name: insertInProject
185 | class: reorder
186 | method: POST
187 | path: "/projects/%s/sections/insert"
188 | params:
189 | - name: project
190 | <<: *Param.ProjectGid
191 | required: true
192 | comment: The project in which to reorder the given section
193 | - name: section
194 | <<: *Param.SectionGid
195 | required: true
196 | comment: The section to reorder
197 | - name: before_section
198 | <<: *Param.SectionGid
199 | required: false
200 | example_values:
201 | - "86420"
202 | comment: Insert the given section immediately before the section specified by this parameter.
203 | - name: after_section
204 | <<: *Param.SectionGid
205 | required: false
206 | example_values:
207 | - "86420"
208 | comment: Insert the given section immediately after the section specified by this parameter.
209 | comment: |
210 | Move sections relative to each other in a board view. One of
211 | `before_section` or `after_section` is required.
212 |
213 | Sections cannot be moved between projects.
214 |
215 | At this point in time, moving sections is not supported in list views, only board views.
216 |
217 | Returns an empty data block.
218 |
--------------------------------------------------------------------------------
/src/resources/workspace.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: workspace
4 | comment: |
5 | A _workspace_ is the highest-level organizational unit in Asana. All projects
6 | and tasks have an associated workspace.
7 |
8 | An _organization_ is a special kind of workspace that represents a company.
9 | In an organization, you can group your projects into teams. You can read
10 | more about how organizations work on the Asana Guide.
11 | To tell if your workspace is an organization or not, check its
12 | `is_organization` property.
13 |
14 | Over time, we intend to migrate most workspaces into organizations and to
15 | release more organization-specific functionality. We may eventually deprecate
16 | using workspace-based APIs for organizations. Currently, and until after
17 | some reasonable grace period following any further announcements, you can
18 | still reference organizations in any `workspace` parameter.
19 |
20 | properties:
21 |
22 | - name: id
23 | <<: *PropType.Id
24 | comment: |
25 | Globally unique ID of the workspace.
26 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
27 |
28 | - name: gid
29 | <<: *PropType.Gid
30 | comment: |
31 | Globally unique ID of the workspace.
32 |
33 | - name: resource_type
34 | <<: *PropType.ResourceType
35 | comment: |
36 | The resource type of this resource. The value for this resource is always `workspace`.
37 | example_values:
38 | - '"workspace"'
39 | values:
40 | - name: workspace
41 | comment: A workspace resource type.
42 |
43 | - name: name
44 | type: String
45 | example_values:
46 | - "'My Favorite Workspace'"
47 | comment: |
48 | The name of the workspace.
49 |
50 | - name: is_organization
51 | <<: *PropType.Bool
52 | comment: |
53 | Whether the workspace is an _organization_.
54 |
55 | action_classes:
56 | - name: Get available workspaces
57 | url: get
58 | - name: Update a workspace
59 | url: update
60 | - name: Typeahead search
61 | url: typeahead
62 | - name: User Management
63 | url: user-mgmt
64 | - name: Get workspaces for user
65 | url: get-all-user
66 |
67 | actions:
68 |
69 | # Create, Retrieve, Update, Delete
70 |
71 | - name: findById
72 | class: get
73 | method: GET
74 | path: "/workspaces/%s"
75 | params:
76 | - name: workspace
77 | <<: *Param.WorkspaceId
78 | required: true
79 | comment: |
80 | Returns the full workspace record for a single workspace.
81 |
82 | - name: findAll
83 | class: get
84 | method: GET
85 | path: "/workspaces"
86 | collection: true
87 | comment: |
88 | Returns the compact records for all workspaces visible to the authorized user.
89 |
90 | - name: update
91 | class: update
92 | method: PUT
93 | path: "/workspaces/%s"
94 | params:
95 | - name: workspace
96 | <<: *Param.WorkspaceId
97 | required: true
98 | comment: The workspace to update.
99 | comment: |
100 | A specific, existing workspace can be updated by making a PUT request on
101 | the URL for that workspace. Only the fields provided in the data block
102 | will be updated; any unspecified fields will remain unchanged.
103 |
104 | Currently the only field that can be modified for a workspace is its `name`.
105 |
106 | Returns the complete, updated workspace record.
107 |
108 | - name: typeahead
109 | class: typeahead
110 | method: GET
111 | path: "/workspaces/%s/typeahead"
112 | params:
113 | - name: workspace
114 | <<: *Param.WorkspaceId
115 | required: true
116 | comment: The workspace to fetch objects from.
117 | - name: resource_type
118 | type: Enum
119 | example_values:
120 | - user
121 | values:
122 | - name: custom_field
123 | comment: A custom field.
124 | - name: portfolio
125 | comment: A portfolio.
126 | - name: project
127 | comment: A project.
128 | - name: tag
129 | comment: A tag.
130 | - name: task
131 | comment: A task.
132 | - name: user
133 | comment: A user.
134 | required: true
135 | comment: |
136 | The type of values the typeahead should return. You can choose from
137 | one of the following: custom_field, project, tag, task, and user.
138 | Note that unlike in the names of endpoints, the types listed here are
139 | in singular form (e.g. `task`). Using multiple types is not yet supported.
140 | - name: type
141 | type: Enum
142 | example_values:
143 | - user
144 | values:
145 | - name: custom_field
146 | comment: A custom field.
147 | - name: portfolio
148 | comment: A portfolio.
149 | - name: project
150 | comment: A project.
151 | - name: tag
152 | comment: A tag.
153 | - name: task
154 | comment: A task.
155 | - name: user
156 | comment: A user.
157 | required: false
158 | comment: |
159 | **Deprecated: new integrations should prefer the resource_type field.**
160 | - name: query
161 | type: String
162 | example_values:
163 | - Greg
164 | comment: |
165 | The string that will be used to search for relevant objects. If an
166 | empty string is passed in, the API will currently return an empty
167 | result set.
168 | - name: count
169 | type: Number
170 | example_values:
171 | - "10"
172 | comment: |
173 | The number of results to return. The default is `20` if this
174 | parameter is omitted, with a minimum of `1` and a maximum of `100`.
175 | If there are fewer results found than requested, all will be returned.
176 | collection: true
177 | collection_cannot_paginate: true
178 | comment: |
179 | Retrieves objects in the workspace based on an auto-completion/typeahead
180 | search algorithm. This feature is meant to provide results quickly, so do
181 | not rely on this API to provide extremely accurate search results. The
182 | result set is limited to a single page of results with a maximum size,
183 | so you won't be able to fetch large numbers of results.
184 |
185 | - name: addUser
186 | class: user-mgmt
187 | method: POST
188 | path: "/workspaces/%s/addUser"
189 | params:
190 | - name: workspace
191 | <<: *Param.WorkspaceId
192 | required: true
193 | comment: The workspace or organization to invite the user to.
194 | - name: user
195 | <<: *Param.User
196 | required: true
197 | comment: |
198 | The user can be referenced by their globally unique user ID or their email address.
199 | Returns the full user record for the invited user.
200 |
201 | - name: removeUser
202 | class: user-mgmt
203 | method: POST
204 | path: "/workspaces/%s/removeUser"
205 | params:
206 | - name: workspace
207 | <<: *Param.WorkspaceId
208 | required: true
209 | comment: The workspace or organization to invite the user to.
210 | - name: user
211 | <<: *Param.User
212 | required: true
213 | comment: |
214 | The user making this call must be an admin in the workspace.
215 | Returns an empty data record.
216 |
217 | - name: findByUser
218 | class: get-all-user
219 | method: GET
220 | path: "/users/%s/workspaces"
221 | collection: true
222 | collection_cannot_paginate: true
223 | params:
224 | - name: user
225 | <<: *Param.User
226 | required: true
227 | comment: The user for which to get workspaces.
228 | comment: |
229 | Returns the workspaces for the given user.
230 |
--------------------------------------------------------------------------------
/src/templates/ruby/resource.ejs:
--------------------------------------------------------------------------------
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.required || p.type == "Id" || p.type == "Gid" })
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(/%s/) &&
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 ? "gid" : id.name
85 | return acc.replace("\%s", "#{" + 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 <%= single(classify(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
226 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var bump = require('gulp-bump');
2 | var exec = require('child_process').exec;
3 | var fs = require('fs-extra');
4 | var git = require('gulp-git');
5 | var syncToGitHub = require('sync-to-github');
6 | var gulp = require('gulp');
7 | var mocha = require('gulp-mocha');
8 | var rename = require('gulp-rename');
9 | var tagVersion = require('gulp-tag-version');
10 | var template = require('gulp-template');
11 | var util = require('util');
12 | var yaml = require('js-yaml');
13 | var resource = require('./src/resource');
14 | var helpers = require('./src/helpers');
15 | var _ = require('lodash');
16 | var Bluebird = require('bluebird');
17 | var GitHubApi = require('github');
18 | var dateFormat = require('dateformat');
19 |
20 | /**
21 | * Paths
22 | */
23 | var test = 'test/**/*';
24 |
25 | /**
26 | * Environment
27 | */
28 | var languages = {
29 | js: {
30 | repo: 'Asana/node-asana',
31 | outputPath: 'lib/resources/gen',
32 | preserveRepoFiles: false
33 | },
34 | php: {
35 | repo: 'Asana/php-asana',
36 | outputPath: 'src/Asana/Resources/Gen',
37 | preserveRepoFiles: false
38 | },
39 | java: {
40 | repo: 'Asana/java-asana',
41 | outputPath: 'src/main/java/com/asana/resources/gen',
42 | preserveRepoFiles: false
43 | },
44 | python: {
45 | repo: 'Asana/python-asana',
46 | outputPath: 'asana/resources/gen',
47 | // Keep the __init__.py file there
48 | preserveRepoFiles: true
49 | },
50 | ruby: {
51 | repo: 'Asana/ruby-asana',
52 | outputPath: 'lib/asana/resources',
53 | preserveRepoFiles: false,
54 | skip: ['event']
55 | },
56 | api_explorer: {
57 | repo: 'Asana/api-explorer',
58 | outputPath: 'src/resources/gen',
59 | preserveRepoFiles: false
60 | },
61 | api_reference: {
62 | repo: 'Asana/asanastatic',
63 | outputPath: '_content/developers/02-api-reference',
64 | // Keep the other markdown pages and metadata there
65 | preserveRepoFiles: true
66 | }
67 | };
68 |
69 | var paths = {
70 | dist: function(lang) {
71 | return 'dist/' + lang;
72 | },
73 | repoOutputRelative: function(lang) {
74 | return languages[lang].outputPath;
75 | }
76 | };
77 |
78 | function readPackage(filename) {
79 | return fs.readJSONSync(filename || 'package.json');
80 | }
81 |
82 | function writePackage(filename, data) {
83 | return fs.writeJSONSync(filename || 'package.json', data);
84 | }
85 |
86 | /**
87 | * High-level tasks
88 | */
89 |
90 | // Build all languages
91 | gulp.task('build', ['test'].concat(Object.keys(languages).map(function(lang) {
92 | return 'build-' + lang;
93 | })));
94 |
95 | // Deploy languages
96 | gulp.task('deploy', ['ensure-git-clean', 'build'].concat(Object.keys(languages).map(function(lang) {
97 | return 'deploy-' + lang;
98 | })));
99 |
100 |
101 | // Store a single timestamp representing right now.
102 | var nowTimestamp = dateFormat('yyyymmdd-HHMMss');
103 |
104 |
105 | /**
106 | * Generate build and deploy rules for each language
107 | */
108 | var resourceNames = resource.names();
109 | Object.keys(languages).forEach(function(lang) {
110 | var config = languages[lang];
111 | var token = process.env.ASANA_GITHUB_TOKEN || null;
112 | var isProd = (process.env.PROD == '1');
113 |
114 | function echoAndExec(command, options) {
115 | if (process.env.GULP_DEBUG) {
116 | console.log('+ ' + arguments[0]);
117 | }
118 |
119 | return new Bluebird(function(resolve, reject) {
120 | return exec(command, options, function(err, stdout, stderr) {
121 | if (err) {
122 | reject(err);
123 | } else {
124 | resolve(stdout);
125 | }
126 | });
127 | });
128 | }
129 |
130 | function resTaskName(resourceName) {
131 | return 'build-' + lang + '-' + resourceName;
132 | }
133 |
134 | /**
135 | * Clean work area for target repo
136 | */
137 | Object.keys(languages).forEach(function(lang) {
138 | gulp.task('clean-' + lang, function() {
139 | fs.removeSync(paths.dist(lang));
140 | });
141 | });
142 |
143 | /**
144 | * Build rules, per resource.
145 | */
146 | var resourcesToSkip = config.skip || [];
147 | var resourcesToBuild = _.difference(resourceNames, resourcesToSkip);
148 | resourcesToBuild.forEach(function(resourceName) {
149 | gulp.task(resTaskName(resourceName), function() {
150 | // Support only local templates
151 | var templatePath = 'src/templates/' + lang;
152 | var resourceTemplateInfo = require('./' + templatePath).resource;
153 |
154 | // Load the resource yaml into a variable
155 | var resourceInstance = resource.load(resourceName);
156 | var templateHelpers = helpers(lang);
157 | templateHelpers.resources = resourceNames;
158 | // Load the resource file
159 | return gulp.src(templatePath + '/' + resourceTemplateInfo.template)
160 | .pipe( //Pipe it through a templating path with the language-specific helpers
161 | template(resourceInstance, {
162 | imports: templateHelpers,
163 | variable: 'resource'
164 | }))
165 | .pipe( //Pipe it through a file renaming step, i.e. resource.ejs becomes project.rb
166 | rename(resourceTemplateInfo.filename(resourceInstance, templateHelpers)))
167 | .pipe( //Pipe it to the output destination
168 | gulp.dest(paths.dist(lang)));
169 | });
170 | });
171 | gulp.task(
172 | 'build-' + lang,
173 | resourcesToBuild.map(resTaskName));
174 |
175 | /**
176 | * Deploy
177 | */
178 |
179 | /**
180 | * @returns {Promise} The commit message to provide for a deployment.
181 | */
182 | function createCommitMessage(user) {
183 | var version = readPackage().version;
184 | var revParse = Bluebird.promisify(git.revParse, git);
185 |
186 | var githubUserName = user.login;
187 | return revParse({args: '--abbrev-ref HEAD'}).then(function(branchName) {
188 | return revParse({args: '--short HEAD'}).then(function(commitHash) {
189 | var commitDesc = branchName.trim() ?
190 | util.format("%s/%s", commitHash, branchName.trim()) :
191 | commitHash;
192 | return util.format(
193 | "Deploy from asana-api-meta v%s (%s) by %s",
194 | version, commitDesc, githubUserName);
195 | });
196 | });
197 | }
198 |
199 | function getGitHubUser() {
200 | var github = githubClient(token);
201 | var getUser = Bluebird.promisify(github.user.get, github.user);
202 | return getUser({});
203 | }
204 |
205 | function deployWithGithubApi() {
206 | return getGitHubUser().then(function(user) {
207 | var branchName = isProd ?
208 | 'api-meta-incoming' :
209 | (user.login + '-' + nowTimestamp);
210 | return createCommitMessage(user).then(function(commitMessage) {
211 | var repoParts = config.repo.split('/');
212 | return syncToGitHub({
213 | oauthToken: token,
214 | user: repoParts[0],
215 | repo: repoParts[1],
216 | localPath: paths.dist(lang),
217 | repoPath: paths.repoOutputRelative(lang),
218 | branch: branchName,
219 | baseBranch: 'master',
220 | createBranch: true,
221 | message: commitMessage,
222 | preserveRepoFiles: !!config.preserveRepoFiles,
223 | createPullRequest: isProd,
224 | debug: !!process.env.GULP_DEBUG
225 | });
226 | });
227 | });
228 | }
229 |
230 | function githubClient(token) {
231 | var github = new GitHubApi({
232 | version: '3.0.0',
233 | protocol: 'https',
234 | host: 'api.github.com'
235 | });
236 | github.authenticate({
237 | type: 'oauth',
238 | token: token
239 | });
240 | return github;
241 | }
242 |
243 | gulp.task(
244 | 'deploy-' + lang,
245 | ['ensure-git-clean', 'build-' + lang],
246 | deployWithGithubApi);
247 | });
248 |
249 |
250 | /**
251 | * Bumping version number and tagging the repository with it.
252 | * Please read http://semver.org/
253 | *
254 | * You can use the commands
255 | *
256 | * gulp bump-patch # makes v0.1.0 → v0.1.1
257 | * gulp bump-feature # makes v0.1.1 → v0.2.0
258 | * gulp bump-release # makes v0.2.1 → v1.0.0
259 | *
260 | * To bump the version numbers accordingly after you did a patch,
261 | * introduced a feature or made a backwards-incompatible release.
262 | */
263 | function bumpVersion(importance) {
264 | return gulp.src(['./package.json'])
265 | .pipe(bump({type: importance}))
266 | .pipe(gulp.dest('./'))
267 | .pipe(git.commit('bump package version'))
268 | .pipe(tagVersion());
269 | }
270 | gulp.task('bump-patch', ['ensure-git-clean'], function() {
271 | return bumpVersion('patch');
272 | });
273 | gulp.task('bump-feature', ['ensure-git-clean'], function() {
274 | return bumpVersion('minor');
275 | });
276 | gulp.task('bump-release', ['ensure-git-clean'], function() {
277 | return bumpVersion('major');
278 | });
279 |
280 | /**
281 | * Ensure that the git working directory is clean.
282 | */
283 | gulp.task('ensure-git-clean', function() {
284 | git.status(function(err, out) {
285 | if (err) { throw err; }
286 | if (!/working tree clean/.exec(out)) {
287 | // Working directory must be clean for some operations.
288 | // For bumping the version, this prevents accidental commits of
289 | // unintended or partial changes.
290 | // For deployment, this ensures that the deploy is tagged with a commit
291 | // and that can be used to reference the exact state of the repo (if we
292 | // allowed changes then the commit would not tell us what the code
293 | // actually looked like).
294 | throw new Error('Git working directory not clean, will not proceed.');
295 | }
296 | });
297 | });
298 |
299 | /**
300 | * Tests the code with mocha.
301 | */
302 | gulp.task('test', function(callback) {
303 | gulp.src(test)
304 | .pipe(mocha({
305 | reporter: process.env.TRAVIS ? 'spec' : 'nyan'
306 | }));
307 | });
308 |
309 |
310 | /**
311 | * Copy the documents for api-reference to a sibling static site installation.
312 | * TODO: it appears that the convention might be to do all of those repos in
313 | * subdirs (as opposed to sibling dirs), based on paths.repoOutputRelative
314 | */
315 |
316 | gulp.task('local-copy-api-reference', ['build-api_reference'], function(callback) {
317 | fs.copySync(paths.dist('api_reference'), "../../vagrant-php7/asanastatic/" + paths.repoOutputRelative('api_reference'));
318 | callback()
319 | });
320 |
321 | /**
322 | * Setup gulp to watch the metadata and depoly to a working copy
323 | * of the static site. The only assumptions are that asana-api-meta
324 | * and the static site repo are in the same directory.
325 | */
326 | gulp.task('watch-documents', function(callback) {
327 | gulp.watch("src/**/*.{js,yaml,ejs}", ['local-copy-api-reference']);
328 | });
329 |
330 | gulp.task('default', ['build', 'local-copy-api-reference']);
331 |
--------------------------------------------------------------------------------
/src/resources/webhook.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | !include ../includes.yaml
3 | name: webhook
4 | comment: |
5 | Webhooks allow an application to be notified of changes. This is in addition
6 | to the ability to fetch those changes directly as
7 | [Events](/developers/api-reference/events) - in fact, Webhooks are just a way
8 | to receive Events via HTTP POST at the time they occur instead of polling for
9 | them. For services accessible via HTTP this is often vastly more convenient,
10 | and if events are not too frequent can be significantly more efficient.
11 |
12 | In both cases, however, changes are represented as Event objects - refer to
13 | the [Events documentation](/developers/api-reference/events) for more
14 | information on what data these events contain.
15 |
16 | **NOTE:** While Webhooks send arrays of Event objects to their target, the
17 | Event objects themselves contain *only IDs*, rather than the actual resource
18 | they are referencing. So while a normal event you receive via GET /events
19 | would look like this:
20 |
21 | {\
22 | "resource": {\
23 | "id": 1337,\
24 | "resource_type": "task",\
25 | "name": "My Task"\
26 | },\
27 | "parent": null,\
28 | "created_at": "2013-08-21T18:20:37.972Z",\
29 | "user": {\
30 | "id": 1123,\
31 | "resource_type": "user",\
32 | "name": "Tom Bizarro"\
33 | },\
34 | "action": "changed",\
35 | "type": "task"\
36 | }
37 |
38 | In a Webhook payload you would instead receive this:
39 |
40 | {\
41 | "resource": 1337,\
42 | "parent": null,\
43 | "created_at": "2013-08-21T18:20:37.972Z",\
44 | "user": 1123,\
45 | "action": "changed",\
46 | "type": "task"\
47 | }
48 |
49 | Webhooks themselves contain only the information necessary to deliver the
50 | events to the desired target as they are generated.
51 | properties:
52 |
53 | - name: id
54 | <<: *PropType.Id
55 | comment: |
56 | Globally unique ID of the webhook.
57 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
58 |
59 | - name: gid
60 | <<: *PropType.Gid
61 | comment: |
62 | Globally unique ID of the webhook.
63 |
64 | - name: resource_type
65 | <<: *PropType.ResourceType
66 | comment: |
67 | The resource type of this resource. The value for this resource is always `webhook`.
68 | example_values:
69 | - '"webhook"'
70 | values:
71 | - name: webhook
72 | comment: A webhook resource type.
73 |
74 | - name: resource
75 | <<: *PropType.Task
76 | access: Read-only
77 | comment: |
78 | The resource the webhook is subscribed to.
79 |
80 | - name: target
81 | <<: *PropType.Target
82 | access: Read-only
83 | comment: |
84 | The URL to receive the HTTP POST.
85 |
86 | - name: active
87 | <<: *PropType.Bool
88 | comment: |
89 | If true, the webhook will send events - if false it is considered
90 | inactive and will not generate events.
91 |
92 | - name: created_at
93 | <<: *PropType.DateTime
94 | access: Read-only
95 | comment: |
96 | The timestamp when the webhook was created.
97 |
98 | - name: last_success_at
99 | <<: *PropType.DateTime
100 | access: Read-only
101 | comment: |
102 | The timestamp when the webhook last successfully sent an event to the
103 | target.
104 |
105 | - name: last_failure_at
106 | <<: *PropType.DateTime
107 | access: Read-only
108 | comment: |
109 | The timestamp when the webhook last received an error when sending an
110 | event to the target.
111 |
112 | - name: last_failure_content
113 | <<: *PropType.FailureContent
114 | access: Read-only
115 | comment: |
116 | The contents of the last error response sent to the webhook when
117 | attempting to deliver events to the target.
118 |
119 | action_classes:
120 | - name: Create a webhook
121 | url: create
122 | - name: Get webhooks
123 | url: get
124 | - name: Get webhook
125 | url: get-single
126 | - name: Receiving webhook events
127 | url: receive
128 | comment: |
129 | Because multiple events often happen in short succession, a webhook
130 | payload is designed to be able to transmit multiple events at once. The
131 | exact model of events is described in the
132 | [Events documentation](/developers/api-reference/events).
133 |
134 | The HTTP POST that the target receives contains:
135 |
136 | * An `X-Hook-Signature` header, which allows verifying that the payload
137 | is genuine. The signature is a SHA256 HMAC using the shared secret
138 | (transmitted during the handshake) of the request body. Verification is
139 | **strongly recommended**, as it would otherwise be possible for an
140 | attacker to POST a malicious payload to the same endpoint. If the target
141 | endpoint can be kept secret this risk is mitigated somewhat, of course.\
142 | * A JSON body with a single key, `events`, containing an array of the
143 | events that have occurred since the last webhook delivery. Note that this
144 | list may be empty, as periodically we may send a "heartbeat" webhook to
145 | verify that the endpoint is available.
146 |
147 | Note that events are "skinny" - we expect consumers who desire syncing
148 | data to make additional calls to the API to retrieve the latest state.
149 | Because the data may have already changed by the time we send the event,
150 | it would be misleading to send a snapshot of the data along with the
151 | event.
152 |
153 | **Example**
154 |
155 | # Request to your server\
156 | POST /receive-webhook/7654\
157 | X-Hook-Signature: 1d6207f8818f063890758a32d3833914754ba788cb8993e644701bac7257f59e
158 |
159 | {\
160 | "events": [\
161 | {\
162 | "action": "changed",\
163 | "created_at": "2013-08-21T18:20:37.972Z",\
164 | "parent": null,\
165 | "resource": 1337,\
166 | "type": "task",\
167 | "user": 1123\
168 | },\
169 | {\
170 | "action": "changed",\
171 | "created_at": "2013-08-21T18:22:45.421Z",\
172 | "parent": null,\
173 | "resource": 1338,\
174 | "type": "task",\
175 | "user": 1428\
176 | }\
177 | ]\
178 | }
179 |
180 | - name: Error handling and retry
181 | url: retry
182 | comment: |
183 | If we attempt to send a webhook payload and we receive an error status
184 | code, or the request times out, we will retry delivery with exponential
185 | backoff. In general, if your servers are not available for an hour, you
186 | can expect it to take no longer than approximately an hour after they
187 | come back before the paused delivery resumes. However, if we are unable
188 | to deliver a message for 24 hours the webhook will be deactivated.
189 | - name: Delete a webhook
190 | url: delete
191 | actions:
192 |
193 | - name: create
194 | class: create
195 | method: POST
196 | path: "/webhooks"
197 | params:
198 | - name: resource
199 | <<: *Param.Id
200 | required: true
201 | explicit: true
202 | comment: |
203 | A resource ID to subscribe to. The resource can be a task or project.
204 | - name: target
205 | <<: *Param.Target
206 | required: true
207 | explicit: true
208 | comment: |
209 | The URL to receive the HTTP POST.
210 | comment: |
211 | Establishing a webhook is a two-part process. First, a simple HTTP POST
212 | similar to any other resource creation. Since you could have multiple
213 | webhooks we recommend specifying a unique local id for each target.
214 |
215 | Next comes the confirmation handshake. When a webhook is created, we will
216 | send a test POST to the `target` with an `X-Hook-Secret` header as
217 | described in the
218 | [Resthooks Security documentation](http://resthooks.org/docs/security/).
219 | The target must respond with a `200 OK` and a matching `X-Hook-Secret`
220 | header to confirm that this webhook subscription is indeed expected.
221 |
222 | If you do not acknowledge the webhook's confirmation handshake it will
223 | fail to setup, and you will receive an error in response to your attempt
224 | to create it. This means you need to be able to receive and complete the
225 | webhook *while* the POST request is in-flight.
226 | footer: |
227 | **Example**
228 |
229 | # Request\
230 | curl -H "Authorization: Bearer " \\
231 | -X POST https://app.asana.com/api/1.0/webhooks \\
232 | -d "resource=8675309" \\
233 | -d "target=https://example.com/receive-webhook/7654"
234 |
235 | # Handshake sent to https://example.com/\
236 | POST /receive-webhook/7654\
237 | X-Hook-Secret: b537207f20cbfa02357cf448134da559e8bd39d61597dcd5631b8012eae53e81
238 |
239 | # Handshake response sent by example.com\
240 | HTTP/1.1 200\
241 | X-Hook-Secret: b537207f20cbfa02357cf448134da559e8bd39d61597dcd5631b8012eae53e81
242 |
243 | # Response\
244 | HTTP/1.1 201\
245 | {\
246 | "data": {\
247 | "id": 43214,\
248 | "resource_type": "webhook",\
249 | "resource": {\
250 | "id": 8675309,\
251 | "gid": "8675309",\
252 | "resource_type": "project",\
253 | "name": "Bugs"\
254 | },\
255 | "target": "https://example.com/receive-webhook/7654",\
256 | "active": true,\
257 | "created_at": "2018-10-12T19:06:29.993Z",\
258 | "last_success_at": null,\
259 | "last_failure_at": null,\
260 | "last_failure_content": null\
261 | }\
262 | }
263 | - name: getAll
264 | class: get
265 | method: GET
266 | path: "/webhooks"
267 | collection: true
268 | params:
269 | - name: workspace
270 | <<: *Param.WorkspaceId
271 | required: true
272 | explicit: true
273 | comment: |
274 | The workspace to query for webhooks in.
275 | - name: resource
276 | <<: *Param.Id
277 | comment: |
278 | Only return webhooks for the given resource.
279 | comment: |
280 | Returns the compact representation of all webhooks your app has
281 | registered for the authenticated user in the given workspace.
282 | - name: getById
283 | class: get-single
284 | method: GET
285 | path: "/webhooks/%s"
286 | params:
287 | - name: webhook
288 | <<: *Param.Id
289 | required: true
290 | explicit: true
291 | comment: |
292 | The webhook to get.
293 | comment: |
294 | Returns the full record for the given webhook.
295 | - name: deleteById
296 | class: delete
297 | method: DELETE
298 | path: "/webhooks/%s"
299 | params:
300 | - name: webhook
301 | <<: *Param.Id
302 | required: true
303 | explicit: true
304 | comment: |
305 | The webhook to delete.
306 | comment: |
307 | This method permanently removes a webhook. Note that it may be possible
308 | to receive a request that was already in flight after deleting the
309 | webhook, but no further requests will be issued.
310 |
--------------------------------------------------------------------------------
/src/resources/portfolio.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: portfolio
5 | comment: |
6 | A _portfolio_ gives a high-level overview of the status of multiple
7 | initiatives in Asana. Portfolios provide a dashboard overview of the state
8 | of multiple items, including a progress report and the most recent
9 | [project status](/developers/api-reference/project_statuses) update.
10 |
11 | Portfolios have some restrictions on size. Each portfolio has a maximum of 250
12 | items and, like projects, a maximum of 20 custom fields.
13 |
14 | notes:
15 | - |
16 |
17 | properties:
18 |
19 | - name: id
20 | <<: *PropType.Id
21 | comment: |
22 | Globally unique ID of the portfolio.
23 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
24 |
25 | - name: gid
26 | <<: *PropType.Gid
27 | comment: |
28 | Globally unique ID of the portfolio.
29 |
30 | - name: resource_type
31 | <<: *PropType.ResourceType
32 | comment: |
33 | The resource type of this resource. The value for this resource is always `portfolio`.
34 | example_values:
35 | - '"portfolio"'
36 | values:
37 | - name: portfolio
38 | comment: A portfolio resource type.
39 |
40 | - name: name
41 | <<: *PropType.PortfolioName
42 | comment: |
43 | Name of the portfolio.
44 |
45 | - name: owner
46 | <<: *PropType.PortfolioOwner
47 | access: Read-only
48 | comment: |
49 | The current owner of the portfolio. Cannot be null.
50 |
51 | - name: created_at
52 | <<: *PropType.DateTime
53 | access: Read-only
54 | comment: |
55 | The time at which this portfolio was created.
56 |
57 | - name: created_by
58 | <<: *PropType.User
59 | access: Read-only
60 | comment: |
61 | The user that created the portfolio.
62 |
63 | - name: custom_field_settings
64 | <<: *PropType.PortfolioCustomFieldSettingsCompactArray
65 | access: Read-only
66 | comment: |
67 | Array of custom field settings applied to the portfolio.
68 |
69 | - name: color
70 | <<: *PropType.PortfolioColor
71 | comment: |
72 | Must be either `none` or one of: `dark-pink`, `dark-green`, `dark-blue`,
73 | `dark-red`, `dark-teal`, `dark-brown`, `dark-orange`, `dark-purple`,
74 | `dark-warm-gray`, `light-pink`, `light-green`, `light-blue`, `light-red`,
75 | `light-teal`, `light-yellow`, `light-orange`, `light-purple`,
76 | `light-warm-gray`.
77 |
78 | - name: workspace
79 | <<: *PropType.Workspace
80 | comment: |
81 | The workspace or organization that the portfolio belongs to.
82 |
83 | - name: members
84 | <<: *PropType.UserArray
85 | comment: |
86 | Members of the portfolio.
87 |
88 |
89 | action_classes:
90 | - name: Create a portfolio
91 | url: create
92 | - name: Get a portfolio
93 | url: get-single
94 | - name: Update a portfolio
95 | url: update
96 | - name: Delete a portfolio
97 | url: delete
98 | - name: Query for portfolios
99 | url: query
100 | - name: Items in a portfolio
101 | url: items
102 | - name: Work with portfolio memberships
103 | url: members
104 | - name: Custom fields on a portfolio
105 | url: custom-field-settings
106 |
107 | actions:
108 |
109 | # Create, Retrieve, Update, Delete
110 |
111 | - name: create
112 | class: create
113 | method: POST
114 | path: "/portfolios"
115 | params:
116 | - name: workspace
117 | <<: *Param.WorkspaceGid
118 | required: true
119 | comment: The workspace or organization in which to create the portfolio.
120 | - name: name
121 | required: true
122 | <<: *Param.PortfolioName
123 | comment: The name of the newly-created portfolio
124 | - name: color
125 | required: false
126 | <<: *Param.PortfolioColor
127 | comment: An optional color for the portfolio
128 | comment: |
129 | Creates a new portfolio in the given workspace with the supplied name.
130 |
131 | Note that portfolios created in the Asana UI may have some state
132 | (like the "Priority" custom field) which is automatically added to the
133 | portfolio when it is created. Portfolios created via our API will **not**
134 | be created with the same initial state to allow integrations to create
135 | their own starting state on a portfolio.
136 |
137 | - name: findById
138 | class: get-single
139 | method: GET
140 | path: "/portfolios/%s"
141 | params:
142 | - name: portfolio
143 | <<: *Param.PortfolioGid
144 | required: true
145 | comment: The portfolio to get.
146 | comment: |
147 | Returns the complete record for a single portfolio.
148 |
149 | - name: update
150 | class: update
151 | method: PUT
152 | path: "/portfolios/%s"
153 | params:
154 | - name: portfolio
155 | <<: *Param.PortfolioGid
156 | required: true
157 | comment: The portfolio to update.
158 | comment: |
159 | An existing portfolio can be updated by making a PUT request on the
160 | URL for that portfolio. Only the fields provided in the `data` block will be
161 | updated; any unspecified fields will remain unchanged.
162 |
163 | Returns the complete updated portfolio record.
164 |
165 | - name: delete
166 | class: delete
167 | method: DELETE
168 | path: "/portfolios/%s"
169 | params:
170 | - name: portfolio
171 | <<: *Param.PortfolioGid
172 | required: true
173 | comment: The portfolio to delete.
174 | comment: |
175 | An existing portfolio can be deleted by making a DELETE request
176 | on the URL for that portfolio.
177 |
178 | Returns an empty data record.
179 |
180 | - name: findAll
181 | class: query
182 | method: GET
183 | path: "/portfolios"
184 | collection: true
185 | comment: |
186 | Returns a list of the portfolios in compact representation that are owned
187 | by the current API user.
188 | params:
189 | - name: workspace
190 | <<: *Param.WorkspaceGid
191 | required: true
192 | comment: The workspace or organization to filter portfolios on.
193 | - name: owner
194 | <<: *Param.User
195 | required: true
196 | comment: |
197 | The user who owns the portfolio. Currently, API users can only get a
198 | list of portfolios that they themselves own.
199 |
200 | - name: getItems
201 | class: items
202 | method: GET
203 | path: "/portfolios/%s/items"
204 | collection: true
205 | comment: |
206 | Get a list of the items in compact form in a portfolio.
207 | params:
208 | - name: portfolio
209 | <<: *Param.PortfolioGid
210 | required: true
211 | comment: The portfolio from which to get the list of items.
212 |
213 | - name: addItem
214 | class: items
215 | method: POST
216 | path: "/portfolios/%s/addItem"
217 | comment: |
218 | Add an item to a portfolio.
219 |
220 | Returns an empty data block.
221 |
222 | params:
223 | - name: portfolio
224 | <<: *Param.PortfolioGid
225 | required: true
226 | comment: The portfolio to which to add an item.
227 | - name: item
228 | <<: *Param.ProjectGid
229 | required: true
230 | comment: The item to add to the portfolio.
231 | - name: insert_before
232 | <<: *Param.ProjectGid
233 | comment: |
234 | An id of an item in this portfolio. The new item will be added before the one specified here.
235 | `insert_before` and `insert_after` parameters cannot both be specified.
236 | - name: insert_after
237 | <<: *Param.ProjectGid
238 | comment: |
239 | An id of an item in this portfolio. The new item will be added after the one specified here.
240 | `insert_before` and `insert_after` parameters cannot both be specified.
241 |
242 | - name: removeItem
243 | class: items
244 | method: POST
245 | path: "/portfolios/%s/removeItem"
246 | comment: |
247 | Remove an item to a portfolio.
248 |
249 | Returns an empty data block.
250 |
251 | params:
252 | - name: portfolio
253 | <<: *Param.PortfolioGid
254 | required: true
255 | comment: The portfolio from which to remove the item.
256 | - name: item
257 | <<: *Param.ProjectGid
258 | required: true
259 | comment: The item to remove from the portfolio.
260 |
261 | - name: addMembers
262 | class: members
263 | method: POST
264 | path: "/portfolios/%s/addMembers"
265 | params:
266 | - name: portfolio
267 | <<: *Param.ProjectGid
268 | required: true
269 | comment: The portfolio to add members to.
270 | - name: members
271 | <<: *Param.GidArray
272 | required: true
273 | comment: An array of user ids.
274 | comment: |
275 | Adds the specified list of users as members of the portfolio. Returns the updated portfolio record.
276 |
277 | - name: removeMembers
278 | class: members
279 | method: POST
280 | path: "/portfolios/%s/removeMembers"
281 | params:
282 | - name: portfolio
283 | <<: *Param.PortfolioGid
284 | required: true
285 | comment: The portfolio to remove members from.
286 | - name: members
287 | <<: *Param.GidArray
288 | required: true
289 | comment: An array of user ids.
290 | comment: |
291 | Removes the specified list of members from the portfolio. Returns the updated portfolio record.
292 |
293 | - name: customFieldSettings
294 | class: custom-field-settings
295 | method: GET
296 | path: "/portfolios/%s/custom_field_settings"
297 | collection: true
298 | comment: |
299 | Get the custom field settings on a portfolio.
300 | params:
301 | - name: portfolio
302 | <<: *Param.PortfolioGid
303 | required: true
304 | comment: The portfolio from which to get the custom field settings.
305 |
306 | - name: addCustomFieldSetting
307 | class: custom-field-settings
308 | method: POST
309 | path: "/portfolios/%s/addCustomFieldSetting"
310 | comment: |
311 | Create a new custom field setting on the portfolio. Returns the full
312 | record for the new custom field setting.
313 | params:
314 | - name: portfolio
315 | <<: *Param.PortfolioGid
316 | required: true
317 | comment: The portfolio onto which to add the custom field.
318 | - name: custom_field
319 | <<: *Param.CustomFieldGid
320 | required: true
321 | comment: The id of the custom field to add to the portfolio.
322 | - name: is_important
323 | <<: *Param.Bool
324 | comment: |
325 | Whether this field should be considered important to this portfolio (for instance, to display in the list view of items in the portfolio).
326 | - name: insert_before
327 | <<: *Param.CustomFieldSettingsGid
328 | comment: |
329 | An id of a custom field setting on this portfolio. The new custom field setting will be added before this one.
330 | `insert_before` and `insert_after` parameters cannot both be specified.
331 | - name: insert_after
332 | <<: *Param.CustomFieldSettingsGid
333 | comment: |
334 | An id of a custom field setting on this portfolio. The new custom field setting will be added after this one.
335 | `insert_before` and `insert_after` parameters cannot both be specified.
336 |
337 | - name: removeCustomFieldSetting
338 | class: custom-field-settings
339 | method: POST
340 | path: "/portfolios/%s/removeCustomFieldSetting"
341 | comment: |
342 | Remove a custom field setting on the portfolio. Returns an empty data
343 | block.
344 | params:
345 | - name: portfolio
346 | <<: *Param.PortfolioGid
347 | required: true
348 | comment: The portfolio from which to remove the custom field.
349 | - name: custom_field
350 | required: true
351 | <<: *Param.CustomFieldGid
352 | comment: The id of the custom field to remove from this portfolio.
353 |
--------------------------------------------------------------------------------
/src/helpers.js:
--------------------------------------------------------------------------------
1 | var inflect = require('inflect');
2 | var util = require('util');
3 | var path = require('path')
4 | var _ = require('lodash');
5 | var fs = require('fs')
6 | var yaml = require('js-yaml');
7 |
8 | /**
9 | * Helpers for code-generation templates
10 | */
11 |
12 | function _repeat(char, times) {
13 | var s = '';
14 | for (var i = 0; i < times; i++) {
15 | s += char;
16 | }
17 | return s;
18 | }
19 |
20 | /**
21 | * Wrap the interior of a multi-line comment at the 80-column boundary.
22 | *
23 | * @param {String} text
24 | * @param {String?} prefix
25 | * @param {Number?} maxChars
26 | * @returns {*}
27 | */
28 | function wrapComment(text, prefix, maxChars) {
29 | // TODO: actually wrap :)
30 | prefix = prefix || "";
31 | maxChars = maxChars || 78;
32 | return prefixLines(text, prefix);
33 | }
34 |
35 | function prefixLines(text, prefix, skipFirst) {
36 | return (skipFirst ? "" : prefix) + text.trim().replace(/\n/g, "\n" + prefix);
37 | }
38 |
39 | function wrapStarComment(text, indent) {
40 | return wrapComment(text, _repeat(' ', indent || 0) + ' * ');
41 | }
42 |
43 | function wrapHashComment(text, indent) {
44 | return wrapComment(text, _repeat(' ', indent || 0) + '# ');
45 | }
46 |
47 | function typeNameTranslator(lang) {
48 | return ({
49 | js: function(name) {
50 | return ({
51 | Id: 'String',
52 | Enum: 'String'
53 | })[name] || name;
54 | },
55 | java: function(name) {
56 | return ({
57 | Id: 'String',
58 | Enum: 'String'
59 | })[name] || name;
60 | }
61 | })[lang] || function(x) { return x; };
62 | }
63 |
64 | function paramsForAction(action) {
65 | // Figure out how many params will be consumed by the path and put the
66 | // first N required params there - the rest go in options.
67 | var numPathParams = (action.path.match(/%/g) || []).length;
68 | var results = {
69 | pathParams: [],
70 | explicitNonPathParams: [],
71 | optionParams: []
72 | };
73 | if (action.params) {
74 | action.params.forEach(function(param, index) {
75 | if (param.required && results.pathParams.length < numPathParams) {
76 | results.pathParams.push(param);
77 | } else if (param.explicit) {
78 | results.explicitNonPathParams.push(param);
79 | } else {
80 | results.optionParams.push(param);
81 | }
82 | });
83 | }
84 | return results;
85 | }
86 |
87 | // Removes line breaks but preserves paragraphs (double newlines).
88 | // Also reserves line breaks denoted with an optional delimiter.
89 | // TODO: make this un-hacky
90 | function removeLineBreaks(text, opt_paragraph_delim) {
91 | var paragraph_delim = opt_paragraph_delim || "\n\n";
92 | text = text.replace(/\\\n/gm, "XX");
93 | text = text.replace(/\n\n/gm, "CC");
94 | text = text.replace(/\r\n|\n|\r/gm, " ");
95 | text = text.replace(/XX/g, "\n");
96 | return text.replace(/CC/g, paragraph_delim).trim();
97 | }
98 |
99 | // Strip a string of leading/trailing whitespace
100 | // unlike removeLineBreaks, this doesn't affect anything inside the text.
101 | function stripWhitespace(text) {
102 | return text.replace(/^\s+/, '').replace(/\s+$/, '')
103 | }
104 |
105 | function idIfyParamName(name) {
106 | return name + "_gid";
107 | }
108 |
109 | function genericPath(action, pathParams) {
110 | var path = action.path;
111 | _.forEach(pathParams, function(pathParam) {
112 | path = path.replace(/%./, "{" + idIfyParamName(pathParam.name) + "}");
113 | });
114 | return path;
115 | }
116 |
117 | function examplesForResource(resource) {
118 | var yamlFile = fs.readFileSync(path.join(__dirname, './templates/examples.yaml'), 'utf8');
119 | var examples = yaml.load(yamlFile);
120 | return examples[resource];
121 | }
122 |
123 | // This is for "action class" as in "action_classes" which is, in this context,
124 | // "class" as in css class. Basically, we can have a section of only text that
125 | // falls under a blue header that can be linked to.
126 | function curlExamplesForKeys(keys, resource_examples) {
127 | var key_examples = _.filter(resource_examples, function(example) {
128 | if (! example.key) return false;
129 | var index = _.findIndex(keys, function(key){
130 | return key === example.key
131 | })
132 | if(index != -1) {
133 | return true;
134 | }
135 | return false
136 | });
137 | return buildCurlExamples(key_examples);
138 | }
139 |
140 | // Note: this is "action" as in "endpoint description"; `GET /tasks` for example.
141 | function curlExamplesForAction(action, resource_examples) {
142 | var action_examples = _.filter(resource_examples, function(example) {
143 | //TODO: this is a hack, simply to exclude selection-by-key vs selection-by-action/endpoint
144 | if (example.key) return false;
145 | var regex = "^" + action.path.replace(/%s/g, "[^\/]+").replace(/\//g, "\\/") + "[^\\/]*$";
146 | match = (example.method === action.method.toLowerCase() && example.endpoint.match(regex));
147 | return match
148 |
149 | });
150 | return buildCurlExamples(action_examples);
151 | }
152 |
153 | function buildCurlExamples(examples) {
154 | var curlExamples = [];
155 | _.forEach(examples, function(example) {
156 | var request = 'curl';
157 | if (example.method === 'put') {
158 | request += ' --request PUT';
159 | } else if (example.method === 'delete') {
160 | request += ' --request DELETE';
161 | }
162 | request += ' -H "Authorization: Bearer "';
163 | var url = 'https://app.asana.com/api/1.0' + example.endpoint;
164 | var data = [];
165 | if (example.request_data) {
166 | _.forEach(example.request_data, function(value, param_name) {
167 | var line;
168 | if (Array.isArray(value)) { // exception for array types because of curl weirdness
169 | line = '--data-urlencode "' + param_name + '[0]=' + value[0] + '"';
170 | } else if (param_name === 'file') { // exception for files
171 | line = '--form "' + param_name + "=" + value + '"';
172 | } else {
173 | line = '--data-urlencode "' + param_name + "=" + value + '"';
174 | }
175 | data.push(line);
176 | })
177 | }
178 | var response_status = "";
179 | var response = {};
180 | _.forEach(example.response, function(value, field_name) {
181 | if (field_name === 'status') {
182 | response_status = "HTTP/1.1 " + example.response.status;
183 | } else {
184 | response[field_name] = value;
185 | }
186 | });
187 | var ex = {
188 | description: examples.length > 1 ? example.description : null,
189 | request: request,
190 | url: url,
191 | dataForRequest: data,
192 | responseStatus: response_status,
193 | response: JSON.stringify(response, null, ' ')
194 | };
195 | curlExamples.push(ex);
196 | });
197 | return curlExamples;
198 | }
199 |
200 | /**
201 | * Modeled after Ruby's classify inflection. It turns, for example, 'custom field settings'
202 | * to 'CustomFieldSettings'
203 | */
204 | function classify(str) {
205 | return inflect.titleize(inflect.pluralize(str)).replace(/\s/gi, '');
206 | }
207 |
208 |
209 | /**
210 | * Construct a partial name based on a series of path parameters
211 | * The last argument (as in Ruby partials) will have a suffix appended.
212 | * (Unlike Ruby, the partial name need not start with an underscore)
213 | * Example:, the path [resource.name, "pre_description"] resolves to
214 | * "{repo_loc}/asana-api-meta/src/templates/api_reference/partials/{task, for example}/pre_description.ejs"
215 | */
216 | function partialFilename() {
217 | var partial_path_arry = _.flatten(Array.prototype.slice.call(arguments));//Ideally: Array.from(arguments)); that's only implemented in newer JS impls
218 | var partial_path = partial_path_arry.join('/') + ".ejs";
219 | // path.join takes variable length arguments, so we pre-calculate a standard prefix and suffix
220 | var filename = path.join(__dirname, '/templates/api_reference/partials', partial_path);
221 | return filename;
222 | }
223 |
224 | /**
225 | * Test if we can stat a partial, given the path parameters (as in partialFilename)
226 | */
227 | function partialExists() {
228 | try {
229 | var partial_path_arry = _.flatten(Array.prototype.slice.call(arguments));//Ideally: Array.from(arguments)); that's only implemented in newer JS impls
230 | fs.lstatSync(partialFilename(partial_path_arry));
231 | return true;
232 | } catch (e) {
233 | return false;
234 | }
235 | }
236 |
237 | /** Evaluate a partial, given the path parameters (as in partialFilename)
238 | * @param [partial_path_arry] {vararg(String, Array)}: [path, [...]] variable length path segments
239 | * @param [partial_context] {Object} : context argument for partial's evaluation environment
240 | *
241 | * Let's break that function signature down:
242 | * This function takes a variable number of strings or arrays of strings, followed by an optional
243 | * context object. The context object, if present, sets the context for the partial, i.e. sets
244 | * the variables in scope for the partial.
245 | * The path is processed as in partialFilename(), that is, is resolved to the location that contains
246 | * partials based on the arguments passed in partial_path_arry. More info on how these are processed
247 | * can be found in partialFilename().
248 | */
249 | function partial() {
250 | var partial_path_arry = _.flatten(Array.prototype.slice.call(arguments));//Ideally: Array.from(arguments)); that's only implemented in newer JS impls
251 | var partial_context = {};
252 | // If the last element is not a string, we interpret it as a context for the partial.
253 | // This context is used to evaluate variables in that context.
254 | if (typeof arguments[arguments.length - 1] !== 'string')
255 | {
256 | partial_context = partial_path_arry.pop();
257 | }
258 | // Generally, partial_path_array will be a single element like ["partialname"]. It has the flexibility to look for nested directories, though,
259 | // by specifying directories in the array - so ["directory", "subdirectory", "partialname"] elements becomes directory/subdirectory/partialname.ejs
260 | if (partialExists(partial_path_arry)) {
261 | var template_content = fs.readFileSync(partialFilename(partial_path_arry), 'utf8');
262 | return stripWhitespace(_.template(template_content)(_.merge(partial_context, langs.api_reference))); // Mixing in langs.api_reference includes the api_reference specific functions in this file.
263 | } else {
264 | return '';
265 | }
266 | }
267 |
268 | var common = {
269 | prefix: prefixLines,
270 | plural: inflect.pluralize,
271 | single: inflect.singularize,
272 | camel: inflect.camelize,
273 | cap: inflect.capitalize,
274 | decap: inflect.decapitalize,
275 | snake: inflect.underscore,
276 | dash: inflect.dasherize,
277 | param: inflect.parameterize,
278 | human: inflect.humanize,
279 | title: inflect.titleize,
280 | classify: classify,
281 | paramsForAction: paramsForAction,
282 | examplesForResource: examplesForResource
283 | };
284 |
285 | var langs = {
286 | "java": _.merge({}, common, {
287 | typeName: typeNameTranslator("java"),
288 | comment: wrapStarComment
289 | }),
290 | "js": _.merge({}, common, {
291 | typeName: typeNameTranslator("js"),
292 | comment: wrapStarComment
293 | }),
294 | "php": _.merge({}, common, {
295 | typeName: typeNameTranslator("php"),
296 | comment: wrapStarComment
297 | }),
298 | "python": _.merge({}, common, {
299 | typeName: typeNameTranslator("python"),
300 | comment: wrapHashComment
301 | }),
302 | ruby: _.merge({}, common, {
303 | typeName: typeNameTranslator("ruby"),
304 | comment: wrapHashComment
305 | }),
306 | api_explorer: _.merge({}, common, {
307 | typeName: typeNameTranslator("js"),
308 | comment: wrapStarComment
309 | }),
310 | api_reference: _.merge({}, common, {
311 | typeName: typeNameTranslator("md"),
312 | indent: prefixLines,
313 | removeLineBreaks: removeLineBreaks,
314 | stripWhitespace: stripWhitespace,
315 | partialExists: partialExists,
316 | partial: partial,
317 | partialFilename: partialFilename,
318 | idIfyParamName: idIfyParamName,
319 | genericPath: genericPath,
320 | curlExamplesForAction: curlExamplesForAction,
321 | curlExamplesForKeys: curlExamplesForKeys
322 | })
323 | };
324 |
325 | module.exports = function(lang) {
326 | return langs[lang];
327 | };
328 |
--------------------------------------------------------------------------------
/src/resources/custom_fields.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: custom_fields
5 | comment: |
6 |
7 | Custom Fields store the metadata that is used in order to add user-specified
8 | information to tasks in Asana. Be sure to reference the [Custom
9 | Fields](/developers/documentation/getting-started/custom-fields) developer
10 | documentation for more information about how custom fields relate to various
11 | resources in Asana.
12 |
13 | Users in Asana can [lock custom
14 | fields](/guide/help/premium/custom-fields#gl-lock-fields), which will make
15 | them read-only when accessed by other users. Attempting to edit a locked
16 | custom field will return HTTP error code `403 Forbidden`.
17 |
18 |
19 | properties:
20 | # We borrow a bunch of the project templates here because they're literally
21 | # exactly the same
22 |
23 | - name: id
24 | <<: *PropType.Id
25 | comment: |
26 | Globally unique ID of the custom field.
27 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
28 |
29 | - name: gid
30 | <<: *PropType.Gid
31 | comment: |
32 | Globally unique ID of the custom field.
33 |
34 | - name: resource_type
35 | <<: *PropType.ResourceType
36 | comment: |
37 | The resource type of this resource. The value for this resource is always `custom_field`.
38 | example_values:
39 | - '"custom_field"'
40 | values:
41 | - name: custom_field
42 | comment: A custom field resource type.
43 |
44 | - name: resource_subtype
45 | <<: *PropType.ResourceSubtype
46 | access: Create-only
47 | comment: |
48 | The type of custom field. Must be one of the given values.
49 | example_values:
50 | - '"text"'
51 | - '"number"'
52 | - '"enum"'
53 | values:
54 | - name: text
55 | comment: A custom field of subtype `text`. Text custom fields store strings of text in Asana and have very few restrictions on content.
56 | - name: number
57 | comment: A custom field of subtype `number`. Number custom fields must contain only valid numbers and are constrained to a predefined precision.
58 | - name: enum
59 | comment: A custom field of subtype `enum`. Enum custom fields are constrained to one of a set of predefined values.
60 |
61 | - name: name
62 | <<: *PropType.CustomFieldName
63 | comment: |
64 | The name of the custom field.
65 |
66 | - name: description
67 | <<: *PropType.CustomFieldDescription
68 | comment: |
69 | [Opt In](/developers/documentation/getting-started/input-output-options). The description of the custom field.
70 |
71 | - name: type
72 | <<: *PropType.CustomFieldType
73 | access: Create-only
74 | comment: |
75 | **Deprecated: new integrations should prefer the `resource_subtype` field.**
76 | The type of the custom field. Must be one of the given values.
77 |
78 | - name: enum_options
79 | <<: *PropType.CustomFieldEnumOptions
80 | access: Read-only
81 | comment: |
82 | Only relevant for custom fields of type 'enum'. This array specifies the possible values which an `enum` custom field can adopt. To modify the enum options, refer to [working with enum options](#enum-options).
83 |
84 | - name: precision
85 | <<: *PropType.CustomFieldPrecision
86 | comment: |
87 | Only relevant for custom fields of type 'Number'. This field dictates the number of places after the decimal to round to, i.e. 0 is integer values, 1 rounds to the nearest tenth, and so on. Must be between 0 and 6, inclusive.
88 |
89 | action_classes:
90 | - name: Type-specific custom field information
91 | url: type-specific
92 | comment: |
93 | Since custom fields can be defined for one of a number of types, and these types have different data and behaviors, there are fields that are relevant to a particular type. For instance, as noted above, `enum_options` is only relevant for the `enum` type and defines the set of choices that the enum could represent. The examples below show some of these type-specific custom field definitions.
94 | example_keys: ["get-enum-custom-field-data", "get-text-custom-field-data", "get-number-custom-field-data"]
95 | - name: Get a custom field
96 | url: get-single
97 | - name: Query for custom fields
98 | url: query-metadata
99 | - name: Create a custom field
100 | url: create
101 | example_keys: ["create-enum-custom-field", "create-text-custom-field", "create-number-custom-field"]
102 | - name: Update a custom field
103 | url: update
104 | - name: Delete a custom field
105 | url: delete
106 | - name: Working with custom field enum options
107 | url: enum-options
108 | comment: |
109 | Enum options are the possible values which an enum custom field can adopt. An enum custom field must contain at least 1 enum option but no more than 50.
110 |
111 | You can add enum options to a custom field by using the `POST /custom_fields/custom-field-id/enum_options` endpoint.
112 |
113 | **It is not possible to remove or delete an enum option.** Instead, enum options can be disabled by updating the `enabled` field to `false ` with the `PUT /enum_options/enum_option-id` endpoint. Other attributes can be updated similarly.
114 |
115 | On creation of an enum option, `enabled` is always set to `true`, meaning the enum option is a selectable value for the custom field. Setting `enabled=false` is equivalent to "trashing" the enum option in the Asana web app within the "Edit Fields" dialog. The enum option will no longer be selectable but, if the enum option value was previously set within a task, the task will retain the value.
116 |
117 | Enum options are an ordered list and by default new enum options are inserted at the end. Ordering in relation to existing enum options can be specified on creation by using `insert_before` or `insert_after` to reference an existing enum option. Only one of `insert_before` and `insert_after` can be provided when creating a new enum option.
118 |
119 | An enum options list can be reordered with the `POST /custom_fields/custom-field-id/enum_options/insert` endpoint.
120 | - name: Add a new enum option
121 | url: add-option
122 | - name: Update an existing enum option
123 | url: update-option
124 | - name: Reorder enum options
125 | url: reorder-options
126 |
127 |
128 | actions:
129 |
130 | # Create, Retrieve, Update, Delete
131 |
132 | - name: create
133 | class: create
134 | method: POST
135 | path: "/custom_fields"
136 | params:
137 | - name: workspace
138 | <<: *Param.WorkspaceGid
139 | required: true
140 | comment: The workspace to create a custom field in.
141 | - name: resource_subtype
142 | <<: *Param.CustomFieldType
143 | required: true
144 | - name: type
145 | <<: *Param.CustomFieldType
146 | comment: "**Deprecated: New integrations should prefer the `resource_subtype` parameter.**"
147 | - name: name
148 | <<: *Param.CustomFieldName
149 | required: true
150 | - name: description
151 | <<: *Param.CustomFieldDescription
152 | required: false
153 | - name: precision
154 | <<: *Param.CustomFieldPrecision
155 | - name: enum_options
156 | <<: *Param.CustomFieldEnumOptions
157 |
158 | comment: |
159 | Creates a new custom field in a workspace. Every custom field is required to be created in a specific workspace, and this workspace cannot be changed once set.
160 |
161 | A custom field's `name` must be unique within a workspace and not conflict with names of existing task properties such as 'Due Date' or 'Assignee'. A custom field's `type` must be one of 'text', 'enum', or 'number'.
162 |
163 | Returns the full record of the newly created custom field.
164 |
165 | - name: findById
166 | class: get-single
167 | method: GET
168 | path: "/custom_fields/%s"
169 | params:
170 | - name: custom_field
171 | <<: *Param.CustomFieldGid
172 | required: true
173 | comment: |
174 | Returns the complete definition of a custom field's metadata.
175 |
176 | - name: findByWorkspace
177 | class: query-metadata
178 | method: GET
179 | path: "/workspaces/%s/custom_fields"
180 | params:
181 | - name: workspace
182 | <<: *Param.WorkspaceGid
183 | required: true
184 | comment: The workspace or organization to find custom field definitions in.
185 | collection: true
186 | comment: |
187 | Returns a list of the compact representation of all of the custom fields in a workspace.
188 |
189 | - name: update
190 | class: update
191 | method: PUT
192 | path: "/custom_fields/%s"
193 | params:
194 | - name: custom_field
195 | <<: *Param.CustomFieldGid
196 | required: true
197 | comment: |
198 | A specific, existing custom field can be updated by making a PUT request on the URL for that custom field. Only the fields provided in the `data` block will be updated; any unspecified fields will remain unchanged
199 |
200 | When using this method, it is best to specify only those fields you wish to change, or else you may overwrite changes made by another user since you last retrieved the custom field.
201 |
202 | An enum custom field's `enum_options` cannot be updated with this endpoint. Instead see "Work With Enum Options" for information on how to update `enum_options`.
203 |
204 | Locked custom fields can only be updated by the user who locked the field.
205 |
206 | Returns the complete updated custom field record.
207 |
208 | - name: delete
209 | class: delete
210 | method: DELETE
211 | path: "/custom_fields/%s"
212 | params:
213 | - name: custom_field
214 | <<: *Param.CustomFieldGid
215 | required: true
216 | comment: |
217 | A specific, existing custom field can be deleted by making a DELETE request on the URL for that custom field.
218 |
219 | Locked custom fields can only be deleted by the user who locked the field.
220 |
221 | Returns an empty data record.
222 |
223 | - name: createEnumOption
224 | class: add-option
225 | method: POST
226 | path: "/custom_fields/%s/enum_options"
227 | params:
228 | - name: custom_field
229 | <<: *Param.CustomFieldGid
230 | required: true
231 | - name: name
232 | <<: *Param.CustomFieldEnumOptionName
233 | required: true
234 | - name: color
235 | <<: *Param.CustomFieldEnumOptionColor
236 | - name: insert_before
237 | <<: *Param.Gid
238 | comment: An existing enum option within this custom field before which the new enum option should be inserted. Cannot be provided together with after_enum_option.
239 | - name: insert_after
240 | <<: *Param.Gid
241 | comment: An existing enum option within this custom field after which the new enum option should be inserted. Cannot be provided together with before_enum_option.
242 |
243 | comment: |
244 | Creates an enum option and adds it to this custom field's list of enum options. A custom field can have at most 50 enum options (including disabled options). By default new enum options are inserted at the end of a custom field's list.
245 |
246 | Locked custom fields can only have enum options added by the user who locked the field.
247 |
248 | Returns the full record of the newly created enum option.
249 |
250 | - name: updateEnumOption
251 | class: update-option
252 | method: PUT
253 | path: "/enum_options/%s"
254 | params:
255 | - name: enum_option
256 | <<: *Param.CustomFieldEnumOptionGid
257 | required: true
258 | - name: name
259 | <<: *Param.CustomFieldEnumOptionName
260 | required: true
261 | - name: color
262 | <<: *Param.CustomFieldEnumOptionColor
263 | - name: enabled
264 | <<: *PropType.Bool
265 | comment: Whether or not the enum option is a selectable value for the custom field.
266 |
267 | comment: |
268 | Updates an existing enum option. Enum custom fields require at least one enabled enum option.
269 |
270 | Locked custom fields can only be updated by the user who locked the field.
271 |
272 | Returns the full record of the updated enum option.
273 |
274 | - name: insertEnumOption
275 | class: reorder-options
276 | method: POST
277 | path: "/custom_fields/%s/enum_options/insert"
278 | params:
279 | - name: custom_field
280 | <<: *Param.CustomFieldGid
281 | required: true
282 | - name: enum_option
283 | <<: *Param.CustomFieldEnumOptionGid
284 | required: true
285 | comment: The ID of the enum option to relocate.
286 | - name: name
287 | <<: *Param.CustomFieldEnumOptionName
288 | required: true
289 | - name: color
290 | <<: *Param.CustomFieldEnumOptionColor
291 | - name: before_enum_option
292 | <<: *Param.Gid
293 | comment: An existing enum option within this custom field before which the new enum option should be inserted. Cannot be provided together with after_enum_option.
294 | - name: after_enum_option
295 | <<: *Param.Gid
296 | comment: An existing enum option within this custom field after which the new enum option should be inserted. Cannot be provided together with before_enum_option.
297 |
298 | comment: |
299 | Moves a particular enum option to be either before or after another specified enum option in the custom field.
300 |
301 | Locked custom fields can only be reordered by the user who locked the field.
302 |
303 |
--------------------------------------------------------------------------------
/src/resources/project.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # See `user.yaml` for more docs on these yaml files.
3 | !include ../includes.yaml
4 | name: project
5 | comment: |
6 | A _project_ represents a prioritized list of tasks in Asana or a board with
7 | columns of tasks represented as cards. It exists in a single workspace or
8 | organization and is accessible to a subset of users in that workspace or
9 | organization, depending on its permissions.
10 |
11 | Projects in organizations are shared with a single team. You cannot currently
12 | change the team of a project via the API. Non-organization workspaces do not
13 | have teams and so you should not specify the team of project in a regular
14 | workspace.
15 |
16 | notes:
17 | - |
18 | Followers of a project are a subset of the members of that project.
19 | Followers of a project will receive all updates including tasks created,
20 | added and removed from that project. Members of the project have access to
21 | and will receive status updates of the project. Adding followers to a
22 | project will add them as members if they are not already, removing
23 | followers from a project will not affect membership.
24 |
25 | properties:
26 |
27 |
28 |
29 | - name: id
30 | <<: *PropType.Id
31 | comment: |
32 | Globally unique ID of the project.
33 | **Note: This field is under active migration to the [`gid` field](#field-gid)--please see our [blog post](/developers/documentation/getting-started/deprecations) for more information.**
34 |
35 | - name: gid
36 | <<: *PropType.Gid
37 | comment: |
38 | Globally unique ID of the project.
39 |
40 | - name: resource_type
41 | <<: *PropType.ResourceType
42 | comment: |
43 | The resource type of this resource. The value for this resource is always `project`. To distinguish between board and list projects see the `layout` property.
44 | example_values:
45 | - '"project"'
46 | values:
47 | - name: project
48 | comment: A project resource type.
49 |
50 | - name: name
51 | <<: *PropType.PotName
52 | comment: |
53 | Name of the project. This is generally a short sentence fragment that fits
54 | on a line in the UI for maximum readability. However, it can be longer.
55 |
56 | - name: owner
57 | <<: *PropType.User
58 | comment: |
59 | The current owner of the project, may be null.
60 |
61 | - name: current_status
62 | <<: *PropType.Status
63 | access: Read-only
64 | comment: |
65 | The most recently created status update for the project, or `null` if no update exists. See also the
66 | documentation for [project status updates](/developers/api-reference/project_statuses).
67 |
68 | - name: due_date
69 | <<: *PropType.Date
70 | comment: |
71 | **Deprecated: new integrations should prefer the due_on field.**
72 | The day on which this project is due. This takes a date with format YYYY-MM-DD.
73 |
74 | - name: due_on
75 | <<: *PropType.Date
76 | comment: |
77 | The day on which this project is due. This takes a date with format YYYY-MM-DD.
78 |
79 | - name: start_on
80 | <<: *PropType.Date
81 | comment: |
82 | The day on which this project starts. This takes a date with format YYYY-MM-DD.
83 |
84 | - name: created_at
85 | <<: *PropType.DateTime
86 | access: Read-only
87 | comment: |
88 | The time at which this project was created.
89 |
90 | - name: modified_at
91 | <<: *PropType.DateTime
92 | access: Read-only
93 | comment: |
94 | The time at which this project was last modified.
95 | notes:
96 | - |
97 | This does not currently reflect any changes in associations such as tasks
98 | or comments that may have been added or removed from the project.
99 |
100 | - name: archived
101 | <<: *PropType.Bool
102 | comment: |
103 | True if the project is archived, false if not. Archived projects do not
104 | show in the UI by default and may be treated differently for queries.
105 |
106 | - name: public
107 | <<: *PropType.Bool
108 | comment: |
109 | True if the project is public to the organization. If false, do not share this project with other users in
110 | this organization without explicitly checking to see if they have access.
111 |
112 | - name: members
113 | <<: *PropType.UserArray
114 | access: Read-only
115 | comment: |
116 | Array of users who are members of this project.
117 |
118 | - name: followers
119 | <<: *PropType.UserArray
120 | access: Read-only
121 | comment: |
122 | Array of users following this project. Followers are a subset of members who receive all notifications for a
123 | project, the default notification setting when adding members to a project in-product.
124 |
125 | - name: custom_fields
126 | <<: *PropType.CustomFieldValuesArray
127 | comment: |
128 | Array of custom field values set on the project for a custom field applied to a parent portfolio. Take care to avoid confusing these custom field values with the custom field settings in the [custom_field_settings](#field-custom_field_settings) property. Please note that the `gid` returned on each custom field value is identical to the `gid` of the custom field, which allows referencing the custom field through the [/custom_fields/{custom_field_gid}](/developers/api-reference/custom_fields#get-single) endpoint.
129 |
130 | - name: custom_field_settings
131 | <<: *PropType.CustomFieldSettingsCompactArray
132 | access: Read-only
133 | comment: |
134 | Array of custom field settings in compact form. These represent the association of custom fields with this project. Take care to avoid confusing these custom field settings with the custom field values in the [custom_fields](#field-custom_fields) property.
135 |
136 | - name: color
137 | <<: *PropType.PotColor
138 | comment: |
139 | Color of the project. Must be either `null` or one of: `dark-pink`,
140 | `dark-green`, `dark-blue`, `dark-red`, `dark-teal`, `dark-brown`,
141 | `dark-orange`, `dark-purple`, `dark-warm-gray`, `light-pink`, `light-green`,
142 | `light-blue`, `light-red`, `light-teal`, `light-yellow`, `light-orange`,
143 | `light-purple`, `light-warm-gray`.
144 |
145 | - name: notes
146 | <<: *PropType.PotNotes
147 | comment: |
148 | More detailed, free-form textual information associated with the project.
149 |
150 | - name: html_notes
151 | <<: *PropType.HtmlText
152 | comment: |
153 | [Opt In](https://asana.com/developers/documentation/getting-started/input-output-options). The notes of the project with formatting as HTML.
154 |
155 | - name: workspace
156 | <<: *PropType.Workspace
157 | comment: |
158 | The workspace or organization this project is associated with. Once created,
159 | projects cannot be moved to a different workspace. This attribute can only
160 | be specified at creation time.
161 |
162 | - name: team
163 | <<: *PropType.Team
164 | comment: |
165 | The team that this project is shared with. This field only exists for
166 | projects in organizations.
167 |
168 | - name: layout
169 | <<: *PropType.Layout
170 | comment: |
171 | The layout (board or list view) of the project.
172 |
173 | action_classes:
174 | - name: Create a project
175 | url: create
176 | - name: Get single project
177 | url: get-single
178 | - name: Update a project
179 | url: update
180 | - name: Delete a project
181 | url: delete
182 | - name: Duplicate a project
183 | url: duplicate
184 | - name: Query for projects
185 | url: query
186 | - name: Get project tasks
187 | url: get-tasks
188 | - name: Work with project sections
189 | url: sections
190 | comment: |
191 | Sections are collections of tasks within a project. The `memberships` property
192 | of a task contains the project/section pairs to which a task belongs when applicable.
193 |
194 | **Deprecation warning**: At this time, sections in a list-layout project
195 | are manipulated as if they were tasks, i.e. reordering a section involves
196 | moving the section (and all of its tasks if they are to remain in that
197 | section) to a new location in a project. (see [Task, Project, and
198 | Section Associations](/developers/api-reference/tasks#projects) for more
199 | information). This method of manipulating sections as if they are tasks will
200 | soon be deprecated in favor of the methods described in the [Sections
201 | resource](/developers/api-reference/sections).
202 | - name: Work with project memberships
203 | url: members
204 | - name: Modify custom field settings
205 | url: custom-field-settings
206 | - name: Get project's task counts
207 | url: get-task-counts
208 |
209 | actions:
210 |
211 | # Create, Retrieve, Update, Delete
212 |
213 | - name: create
214 | class: create
215 | method: POST
216 | path: "/projects"
217 | params:
218 | - name: workspace
219 | <<: *Param.WorkspaceGid
220 | required: true
221 | comment: The workspace or organization to create the project in.
222 | - name: team
223 | <<: *Param.TeamGid
224 | comment: |
225 | If creating in an organization, the specific team to create the
226 | project in.
227 | comment: |
228 | Creates a new project in a workspace or team.
229 |
230 | Every project is required to be created in a specific workspace or
231 | organization, and this cannot be changed once set. Note that you can use
232 | the `workspace` parameter regardless of whether or not it is an
233 | organization.
234 |
235 | If the workspace for your project _is_ an organization, you must also
236 | supply a `team` to share the project with.
237 |
238 | Returns the full record of the newly created project.
239 |
240 | - name: createInWorkspace
241 | class: create
242 | method: POST
243 | path: "/workspaces/%s/projects"
244 | params:
245 | - name: workspace
246 | <<: *Param.WorkspaceGid
247 | required: true
248 | comment: The workspace or organization to create the project in.
249 | comment: |
250 | If the workspace for your project _is_ an organization, you must also
251 | supply a `team` to share the project with.
252 |
253 | Returns the full record of the newly created project.
254 |
255 | - name: createInTeam
256 | class: create
257 | method: POST
258 | path: "/teams/%s/projects"
259 | params:
260 | - name: team
261 | <<: *Param.TeamGid
262 | required: true
263 | comment: The team to create the project in.
264 | comment: |
265 | Creates a project shared with the given team.
266 |
267 | Returns the full record of the newly created project.
268 |
269 | - name: findById
270 | class: get-single
271 | method: GET
272 | path: "/projects/%s"
273 | params:
274 | - name: project
275 | <<: *Param.ProjectGid
276 | required: true
277 | comment: The project to get.
278 | comment: |
279 | Returns the complete project record for a single project.
280 |
281 | - name: update
282 | class: update
283 | method: PUT
284 | path: "/projects/%s"
285 | params:
286 | - name: project
287 | <<: *Param.ProjectGid
288 | required: true
289 | comment: The project to update.
290 | comment: |
291 | A specific, existing project can be updated by making a PUT request on the
292 | URL for that project. Only the fields provided in the `data` block will be
293 | updated; any unspecified fields will remain unchanged.
294 |
295 | When using this method, it is best to specify only those fields you wish
296 | to change, or else you may overwrite changes made by another user since
297 | you last retrieved the task.
298 |
299 | Returns the complete updated project record.
300 |
301 | - name: delete
302 | class: delete
303 | method: DELETE
304 | path: "/projects/%s"
305 | params:
306 | - name: project
307 | <<: *Param.ProjectGid
308 | required: true
309 | comment: The project to delete.
310 | comment: |
311 | A specific, existing project can be deleted by making a DELETE request
312 | on the URL for that project.
313 |
314 | Returns an empty data record.
315 |
316 | - name: duplicateProject
317 | class: duplicate
318 | method: POST
319 | path: "/projects/%s/duplicate"
320 | params:
321 | - name: project
322 | <<: *Param.ProjectGid
323 | required: true
324 | comment: The project to duplicate.
325 | - name: name
326 | type: String
327 | example_values:
328 | - "Things to Buy"
329 | required: true
330 | comment: The name of the new project.
331 | - name: team
332 | <<: *Param.TeamGid
333 | required: false
334 | comment: |
335 | Sets the team of the new project. If team is not defined, the new project
336 | will be in the same team as the the original project.
337 | - name: include
338 | <<: *Param.ProjectIncludes
339 | required: false
340 | comment: |
341 | The elements that will be duplicated to the new project.
342 | Tasks are always included.
343 | - name: schedule_dates
344 | <<: *Param.DuplicateScheduleDates
345 | required: false
346 | comment: |
347 | A dictionary of options to auto-shift dates.
348 | `task_dates` must be included to use this option.
349 | Requires either `start_on` or `due_on`, but not both.
350 | `start_on` will set the first start date of the new
351 | project to the given date, while `due_on` will set the last due date
352 | to the given date. Both will offset the remaining dates by the same amount
353 | of the original project.
354 | comment: |
355 | Creates and returns a job that will asynchronously handle the duplication.
356 |
357 | - name: findAll
358 | class: query
359 | method: GET
360 | path: "/projects"
361 | collection: true
362 | comment: |
363 | Returns the compact project records for some filtered set of projects.
364 | Use one or more of the parameters provided to filter the projects returned.
365 | params:
366 | - name: workspace
367 | <<: *Param.WorkspaceGid
368 | comment: The workspace or organization to filter projects on.
369 | - name: team
370 | <<: *Param.TeamGid
371 | comment: The team to filter projects on.
372 | - name: is_template
373 | <<: *Param.Bool
374 | comment: |
375 | **Note: This parameter can only be included if a team is also defined, or the workspace is not an organization**
376 | Filters results to include only template projects.
377 | - name: archived
378 | <<: *Param.Bool
379 | comment: |
380 | Only return projects whose `archived` field takes on the value of
381 | this parameter.
382 |
383 | - name: findByWorkspace
384 | class: query
385 | method: GET
386 | path: "/workspaces/%s/projects"
387 | params:
388 | - name: workspace
389 | <<: *Param.WorkspaceGid
390 | required: true
391 | comment: The workspace or organization to find projects in.
392 | - name: is_template
393 | <<: *Param.Bool
394 | comment: |
395 | **Note: This parameter can only be included if a team is also defined, or the workspace is not an organization**
396 | Filters results to include only template projects.
397 | - name: archived
398 | <<: *Param.Bool
399 | comment: |
400 | Only return projects whose `archived` field takes on the value of
401 | this parameter.
402 | collection: true
403 | comment: |
404 | Returns the compact project records for all projects in the workspace.
405 |
406 | - name: findByTeam
407 | class: query
408 | method: GET
409 | path: "/teams/%s/projects"
410 | params:
411 | - name: team
412 | <<: *Param.TeamGid
413 | required: true
414 | comment: The team to find projects in.
415 | - name: is_template
416 | <<: *Param.Bool
417 | comment: |
418 | Filters results to include only template projects.
419 | - name: archived
420 | <<: *Param.Bool
421 | comment: |
422 | Only return projects whose `archived` field takes on the value of
423 | this parameter.
424 | collection: true
425 | comment: |
426 | Returns the compact project records for all projects in the team.
427 |
428 | - name: tasks
429 | class: get-tasks
430 | method: GET
431 | path: "/projects/%s/tasks"
432 | params:
433 | - name: project
434 | <<: *Param.ProjectGid
435 | required: true
436 | comment: The project in which to search for tasks.
437 | collection: true
438 | comment: |
439 | Returns the compact task records for all tasks within the given project,
440 | ordered by their priority within the project. Tasks can exist in more than one project at a time.
441 |
442 | - name: addFollowers
443 | class: followers
444 | method: POST
445 | path: "/projects/%s/addFollowers"
446 | params:
447 | - name: project
448 | <<: *Param.ProjectGid
449 | required: true
450 | comment: The project to add followers to.
451 | - name: followers
452 | <<: *Param.GidArray
453 | required: true
454 | comment: An array of followers to add to the project.
455 | comment: |
456 | Adds the specified list of users as followers to the project. Followers are a subset of members, therefore if
457 | the users are not already members of the project they will also become members as a result of this operation.
458 | Returns the updated project record.
459 |
460 | - name: removeFollowers
461 | class: followers
462 | method: POST
463 | path: "/projects/%s/removeFollowers"
464 | params:
465 | - name: project
466 | <<: *Param.ProjectGid
467 | required: true
468 | comment: The project to remove followers from.
469 | - name: followers
470 | <<: *Param.GidArray
471 | required: true
472 | comment: An array of followers to remove from the project.
473 | comment: |
474 | Removes the specified list of users from following the project, this will not affect project membership status.
475 | Returns the updated project record.
476 |
477 | - name: addMembers
478 | class: members
479 | method: POST
480 | path: "/projects/%s/addMembers"
481 | params:
482 | - name: project
483 | <<: *Param.ProjectGid
484 | required: true
485 | comment: The project to add members to.
486 | - name: members
487 | <<: *Param.GidArray
488 | required: true
489 | comment: An array of user ids.
490 | comment: |
491 | Adds the specified list of users as members of the project. Returns the updated project record.
492 |
493 | - name: removeMembers
494 | class: members
495 | method: POST
496 | path: "/projects/%s/removeMembers"
497 | params:
498 | - name: project
499 | <<: *Param.ProjectGid
500 | required: true
501 | comment: The project to remove members from.
502 | - name: members
503 | <<: *Param.GidArray
504 | required: true
505 | comment: An array of user ids.
506 | comment: |
507 | Removes the specified list of members from the project. Returns the updated project record.
508 |
509 | - name: addCustomFieldSetting
510 | class: custom-field-settings
511 | method: POST
512 | path: "/projects/%s/addCustomFieldSetting"
513 | comment: |
514 | Create a new custom field setting on the project.
515 | params:
516 | - name: project
517 | <<: *Param.ProjectGid
518 | required: true
519 | comment: The project to associate the custom field with
520 | - name: custom_field
521 | <<: *Param.CustomFieldGid
522 | required: true
523 | comment: The id of the custom field to associate with this project.
524 | - name: is_important
525 | <<: *Param.Bool
526 | comment: |
527 | Whether this field should be considered important to this project.
528 | - name: insert_before
529 | <<: *Param.CustomFieldSettingsGid
530 | comment: |
531 | An id of a Custom Field Settings on this project, before which the new Custom Field Settings will be added.
532 | `insert_before` and `insert_after` parameters cannot both be specified.
533 | - name: insert_after
534 | <<: *Param.CustomFieldSettingsGid
535 | comment: |
536 | An id of a Custom Field Settings on this project, after which the new Custom Field Settings will be added.
537 | `insert_before` and `insert_after` parameters cannot both be specified.
538 |
539 | - name: removeCustomFieldSetting
540 | class: custom-field-settings
541 | method: POST
542 | path: "/projects/%s/removeCustomFieldSetting"
543 | comment: |
544 | Remove a custom field setting on the project.
545 | params:
546 | - name: project
547 | <<: *Param.ProjectGid
548 | required: true
549 | comment: The project to associate the custom field with
550 | - name: custom_field
551 | <<: *Param.CustomFieldGid
552 | comment: The id of the custom field to remove from this project.
553 |
554 | - name: getTaskCounts
555 | class: get-task-counts
556 | method: GET
557 | path: "/projects/%s/task_counts"
558 | comment: |
559 | Get an object that holds task count fields. **All fields are excluded by default**. You must [opt in](https://asana.com/developers/documentation/getting-started/input-output-options)
560 | using `opt_fields` to get any information from this endpoint.
561 |
562 | This endpoint has an additional [rate limit](/developers/documentation/getting-started/rate-limits#standard)
563 | and each field counts especially high against our [cost limits](/developers/documentation/getting-started/rate-limits#cost).
564 |
565 | Milestones are just tasks, so they are included in the `num_tasks`, `num_incomplete_tasks`, and `num_completed_tasks` counts.
566 | params:
567 | - name: project
568 | <<: *Param.ProjectGid
569 | required: true
570 | comment: The project to get task counts for
571 |
--------------------------------------------------------------------------------