asana-api-cli 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. asana_api_cli-1.2.0/LICENSE +190 -0
  2. asana_api_cli-1.2.0/PKG-INFO +105 -0
  3. asana_api_cli-1.2.0/README.md +87 -0
  4. asana_api_cli-1.2.0/pyproject.toml +82 -0
  5. asana_api_cli-1.2.0/setup.cfg +4 -0
  6. asana_api_cli-1.2.0/src/asana_api_cli/__init__.py +3 -0
  7. asana_api_cli-1.2.0/src/asana_api_cli/cli/__init__.py +140 -0
  8. asana_api_cli-1.2.0/src/asana_api_cli/cli/access_requests.py +66 -0
  9. asana_api_cli-1.2.0/src/asana_api_cli/cli/allocations.py +101 -0
  10. asana_api_cli-1.2.0/src/asana_api_cli/cli/attachments.py +92 -0
  11. asana_api_cli-1.2.0/src/asana_api_cli/cli/audit_log_api.py +52 -0
  12. asana_api_cli-1.2.0/src/asana_api_cli/cli/batch_api.py +30 -0
  13. asana_api_cli-1.2.0/src/asana_api_cli/cli/budgets.py +79 -0
  14. asana_api_cli-1.2.0/src/asana_api_cli/cli/custom_field_settings.py +92 -0
  15. asana_api_cli-1.2.0/src/asana_api_cli/cli/custom_fields.py +133 -0
  16. asana_api_cli-1.2.0/src/asana_api_cli/cli/custom_types.py +50 -0
  17. asana_api_cli-1.2.0/src/asana_api_cli/cli/events.py +32 -0
  18. asana_api_cli-1.2.0/src/asana_api_cli/cli/exports.py +39 -0
  19. asana_api_cli-1.2.0/src/asana_api_cli/cli/goal_relationships.py +98 -0
  20. asana_api_cli-1.2.0/src/asana_api_cli/cli/goals.py +217 -0
  21. asana_api_cli-1.2.0/src/asana_api_cli/cli/jobs.py +29 -0
  22. asana_api_cli-1.2.0/src/asana_api_cli/cli/memberships.py +89 -0
  23. asana_api_cli-1.2.0/src/asana_api_cli/cli/organization_exports.py +44 -0
  24. asana_api_cli-1.2.0/src/asana_api_cli/cli/portfolio_memberships.py +83 -0
  25. asana_api_cli-1.2.0/src/asana_api_cli/cli/portfolios.py +215 -0
  26. asana_api_cli-1.2.0/src/asana_api_cli/cli/project_briefs.py +72 -0
  27. asana_api_cli-1.2.0/src/asana_api_cli/cli/project_memberships.py +53 -0
  28. asana_api_cli-1.2.0/src/asana_api_cli/cli/project_portfolio_settings.py +87 -0
  29. asana_api_cli-1.2.0/src/asana_api_cli/cli/project_statuses.py +77 -0
  30. asana_api_cli-1.2.0/src/asana_api_cli/cli/project_templates.py +102 -0
  31. asana_api_cli-1.2.0/src/asana_api_cli/cli/projects.py +380 -0
  32. asana_api_cli-1.2.0/src/asana_api_cli/cli/rates.py +97 -0
  33. asana_api_cli-1.2.0/src/asana_api_cli/cli/reactions.py +34 -0
  34. asana_api_cli-1.2.0/src/asana_api_cli/cli/roles.py +98 -0
  35. asana_api_cli-1.2.0/src/asana_api_cli/cli/rules.py +28 -0
  36. asana_api_cli-1.2.0/src/asana_api_cli/cli/sections.py +111 -0
  37. asana_api_cli-1.2.0/src/asana_api_cli/cli/status_updates.py +86 -0
  38. asana_api_cli-1.2.0/src/asana_api_cli/cli/stories.py +130 -0
  39. asana_api_cli-1.2.0/src/asana_api_cli/cli/tags.py +155 -0
  40. asana_api_cli-1.2.0/src/asana_api_cli/cli/task_templates.py +77 -0
  41. asana_api_cli-1.2.0/src/asana_api_cli/cli/tasks.py +520 -0
  42. asana_api_cli-1.2.0/src/asana_api_cli/cli/team_memberships.py +103 -0
  43. asana_api_cli-1.2.0/src/asana_api_cli/cli/teams.py +133 -0
  44. asana_api_cli-1.2.0/src/asana_api_cli/cli/time_periods.py +57 -0
  45. asana_api_cli-1.2.0/src/asana_api_cli/cli/time_tracking_categories.py +123 -0
  46. asana_api_cli-1.2.0/src/asana_api_cli/cli/time_tracking_entries.py +138 -0
  47. asana_api_cli-1.2.0/src/asana_api_cli/cli/timesheet_approval_statuses.py +94 -0
  48. asana_api_cli-1.2.0/src/asana_api_cli/cli/typeahead.py +40 -0
  49. asana_api_cli-1.2.0/src/asana_api_cli/cli/user_task_lists.py +45 -0
  50. asana_api_cli-1.2.0/src/asana_api_cli/cli/users.py +173 -0
  51. asana_api_cli-1.2.0/src/asana_api_cli/cli/webhooks.py +96 -0
  52. asana_api_cli-1.2.0/src/asana_api_cli/cli/workspace_memberships.py +75 -0
  53. asana_api_cli-1.2.0/src/asana_api_cli/cli/workspaces.py +113 -0
  54. asana_api_cli-1.2.0/src/asana_api_cli/formatter.py +161 -0
  55. asana_api_cli-1.2.0/src/asana_api_cli/session.py +173 -0
  56. asana_api_cli-1.2.0/src/asana_api_cli/version.py +11 -0
  57. asana_api_cli-1.2.0/src/asana_api_cli.egg-info/PKG-INFO +105 -0
  58. asana_api_cli-1.2.0/src/asana_api_cli.egg-info/SOURCES.txt +64 -0
  59. asana_api_cli-1.2.0/src/asana_api_cli.egg-info/dependency_links.txt +1 -0
  60. asana_api_cli-1.2.0/src/asana_api_cli.egg-info/entry_points.txt +2 -0
  61. asana_api_cli-1.2.0/src/asana_api_cli.egg-info/requires.txt +4 -0
  62. asana_api_cli-1.2.0/src/asana_api_cli.egg-info/top_level.txt +1 -0
  63. asana_api_cli-1.2.0/tests/test_codegen.py +245 -0
  64. asana_api_cli-1.2.0/tests/test_formatter.py +382 -0
  65. asana_api_cli-1.2.0/tests/test_session.py +103 -0
  66. asana_api_cli-1.2.0/tests/test_version.py +22 -0
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to the Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by the Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding any notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2025 Masanao Izumo
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: asana-api-cli
3
+ Version: 1.2.0
4
+ Summary: Command-line wrapper around the official Asana Python SDK
5
+ Author-email: Masanao Izumo <asana@masanao.site>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/izumo-m/asana-api-cli
8
+ Project-URL: Repository, https://github.com/izumo-m/asana-api-cli
9
+ Project-URL: Issues, https://github.com/izumo-m/asana-api-cli/issues
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: click<9,>=8.1
14
+ Requires-Dist: jq<2,>=1.8
15
+ Requires-Dist: tabulate<1,>=0.9
16
+ Requires-Dist: asana<6,>=5.2.4
17
+ Dynamic: license-file
18
+
19
+ # asana-api-cli
20
+
21
+ A CLI tool for the Asana API. It thinly wraps the official
22
+ [python-asana](https://github.com/Asana/python-asana) SDK with click, exposing
23
+ every API endpoint from the command line via `asana-api <group> <command>`.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install asana-api-cli
29
+
30
+ # or, to install as an isolated CLI tool
31
+ pipx install asana-api-cli
32
+ ```
33
+
34
+ ## Environment variables
35
+
36
+ | Name | Required | Description |
37
+ |------|----------|-------------|
38
+ | `ASANA_ACCESS_TOKEN` | Yes (at runtime only) | Asana Personal Access Token |
39
+ | `ASANA_DEFAULT_WORKSPACE` | No | Default workspace GID for endpoints that require it |
40
+
41
+ The token can be issued from the
42
+ [Asana Developer Console](https://app.asana.com/0/developer-console).
43
+ No token is needed for `--help` or argument-error output.
44
+
45
+ ```bash
46
+ export ASANA_ACCESS_TOKEN="1/12345..."
47
+ export ASANA_DEFAULT_WORKSPACE="12345678" # optional
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ```bash
53
+ # Show version
54
+ asana-api --version
55
+
56
+ # List commands
57
+ asana-api --help
58
+ asana-api tasks --help
59
+ asana-api tasks get-tasks --help
60
+
61
+ # List workspaces
62
+ asana-api workspaces get-workspaces
63
+
64
+ # List projects (workspace resolved from ASANA_DEFAULT_WORKSPACE)
65
+ asana-api projects get-projects-for-workspace
66
+ asana-api projects get-projects --workspace <WORKSPACE_GID>
67
+
68
+ # List tasks (first page)
69
+ asana-api tasks get-tasks --project <PROJECT_GID>
70
+
71
+ # Auto-fetch all pages
72
+ asana-api tasks get-tasks --project <PROJECT_GID> --paginate
73
+
74
+ # Single task (--task instead of positional argument)
75
+ asana-api tasks get-task --task <TASK_GID>
76
+
77
+ # Create a task (body is a JSON string)
78
+ asana-api tasks create-task --body '{"data":{"name":"new task","projects":["<PID>"]}}'
79
+
80
+ # Output formats
81
+ asana-api tasks get-tasks --project <PID> --output table
82
+ asana-api tasks get-tasks --project <PID> --query '.data' --output csv
83
+ ```
84
+
85
+ ### Workspace resolution
86
+
87
+ Many API endpoints require a workspace. For those endpoints (e.g.
88
+ `get-projects-for-workspace`), the CLI resolves it in this order:
89
+
90
+ 1. `--workspace <GID>` on the command
91
+ 2. `ASANA_DEFAULT_WORKSPACE` environment variable
92
+
93
+ For endpoints where workspace is optional (e.g. `get-tasks`), the env-var
94
+ fallback is **not** used — pass `--workspace` explicitly if needed. This
95
+ prevents conflicts with other scope parameters like `--project` that are
96
+ mutually exclusive with workspace in the Asana API.
97
+
98
+ ## Development
99
+
100
+ See [docs/development.md](https://github.com/izumo-m/asana-api-cli/blob/main/docs/development.md)
101
+ for building from source, project layout, and library usage.
102
+
103
+ ## License
104
+
105
+ [Apache License 2.0](https://github.com/izumo-m/asana-api-cli/blob/main/LICENSE)
@@ -0,0 +1,87 @@
1
+ # asana-api-cli
2
+
3
+ A CLI tool for the Asana API. It thinly wraps the official
4
+ [python-asana](https://github.com/Asana/python-asana) SDK with click, exposing
5
+ every API endpoint from the command line via `asana-api <group> <command>`.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install asana-api-cli
11
+
12
+ # or, to install as an isolated CLI tool
13
+ pipx install asana-api-cli
14
+ ```
15
+
16
+ ## Environment variables
17
+
18
+ | Name | Required | Description |
19
+ |------|----------|-------------|
20
+ | `ASANA_ACCESS_TOKEN` | Yes (at runtime only) | Asana Personal Access Token |
21
+ | `ASANA_DEFAULT_WORKSPACE` | No | Default workspace GID for endpoints that require it |
22
+
23
+ The token can be issued from the
24
+ [Asana Developer Console](https://app.asana.com/0/developer-console).
25
+ No token is needed for `--help` or argument-error output.
26
+
27
+ ```bash
28
+ export ASANA_ACCESS_TOKEN="1/12345..."
29
+ export ASANA_DEFAULT_WORKSPACE="12345678" # optional
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```bash
35
+ # Show version
36
+ asana-api --version
37
+
38
+ # List commands
39
+ asana-api --help
40
+ asana-api tasks --help
41
+ asana-api tasks get-tasks --help
42
+
43
+ # List workspaces
44
+ asana-api workspaces get-workspaces
45
+
46
+ # List projects (workspace resolved from ASANA_DEFAULT_WORKSPACE)
47
+ asana-api projects get-projects-for-workspace
48
+ asana-api projects get-projects --workspace <WORKSPACE_GID>
49
+
50
+ # List tasks (first page)
51
+ asana-api tasks get-tasks --project <PROJECT_GID>
52
+
53
+ # Auto-fetch all pages
54
+ asana-api tasks get-tasks --project <PROJECT_GID> --paginate
55
+
56
+ # Single task (--task instead of positional argument)
57
+ asana-api tasks get-task --task <TASK_GID>
58
+
59
+ # Create a task (body is a JSON string)
60
+ asana-api tasks create-task --body '{"data":{"name":"new task","projects":["<PID>"]}}'
61
+
62
+ # Output formats
63
+ asana-api tasks get-tasks --project <PID> --output table
64
+ asana-api tasks get-tasks --project <PID> --query '.data' --output csv
65
+ ```
66
+
67
+ ### Workspace resolution
68
+
69
+ Many API endpoints require a workspace. For those endpoints (e.g.
70
+ `get-projects-for-workspace`), the CLI resolves it in this order:
71
+
72
+ 1. `--workspace <GID>` on the command
73
+ 2. `ASANA_DEFAULT_WORKSPACE` environment variable
74
+
75
+ For endpoints where workspace is optional (e.g. `get-tasks`), the env-var
76
+ fallback is **not** used — pass `--workspace` explicitly if needed. This
77
+ prevents conflicts with other scope parameters like `--project` that are
78
+ mutually exclusive with workspace in the Asana API.
79
+
80
+ ## Development
81
+
82
+ See [docs/development.md](https://github.com/izumo-m/asana-api-cli/blob/main/docs/development.md)
83
+ for building from source, project layout, and library usage.
84
+
85
+ ## License
86
+
87
+ [Apache License 2.0](https://github.com/izumo-m/asana-api-cli/blob/main/LICENSE)
@@ -0,0 +1,82 @@
1
+ [project]
2
+ name = "asana-api-cli"
3
+ version = "1.2.0"
4
+ description = "Command-line wrapper around the official Asana Python SDK"
5
+ authors = [{name = "Masanao Izumo", email = "asana@masanao.site"}]
6
+ readme = "README.md"
7
+ license = "Apache-2.0"
8
+ requires-python = ">=3.12"
9
+ dependencies = [
10
+ "click>=8.1,<9",
11
+ "jq>=1.8,<2",
12
+ "tabulate>=0.9,<1",
13
+ "asana>=5.2.4,<6",
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/izumo-m/asana-api-cli"
18
+ Repository = "https://github.com/izumo-m/asana-api-cli"
19
+ Issues = "https://github.com/izumo-m/asana-api-cli/issues"
20
+
21
+ [project.scripts]
22
+ asana-api = "asana_api_cli.cli:main"
23
+
24
+ [dependency-groups]
25
+ dev = [
26
+ "ruff<1",
27
+ "pytest>=9,<10",
28
+ "build>=1,<2",
29
+ "twine>=6,<7",
30
+ ]
31
+
32
+ [build-system]
33
+ requires = ["setuptools>=68"]
34
+ build-backend = "setuptools.build_meta"
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["src"]
38
+
39
+ [tool.ruff]
40
+ target-version = "py310"
41
+ line-length = 100
42
+ extend-exclude = ["src/asana_api_cli/cli"] # auto-generated (tools/codegen.py)
43
+
44
+ [tool.ruff.format]
45
+ docstring-code-format = true
46
+ quote-style = "double"
47
+
48
+ [tool.ruff.lint]
49
+ select = ["E", "F", "W", "N", "B", "ISC", "G", "SIM", "Q"]
50
+ ignore = ["SIM102"]
51
+ unfixable = ["F401"] # do not auto-fix unused imports
52
+
53
+ [tool.ruff.lint.isort]
54
+ sections.tests = ["tests", "tests.*"]
55
+ section-order = [
56
+ "future",
57
+ "standard-library",
58
+ "third-party",
59
+ "first-party",
60
+ "tests",
61
+ "local-folder"
62
+ ]
63
+
64
+ [tool.vermin]
65
+ target = "3.12"
66
+ violations = true
67
+ backport = [
68
+ "typing_extensions",
69
+ "typing",
70
+ "dataclasses",
71
+ "asyncio",
72
+ "argparse",
73
+ "enum",
74
+ "importlib",
75
+ "zoneinfo"
76
+ ]
77
+ no_parse_comments = true
78
+
79
+ [tool.pytest.ini_options]
80
+ testpaths = ["tests"]
81
+ addopts = ""
82
+ disable_test_id_escaping_and_forfeit_all_rights_to_community_support = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from asana_api_cli.session import AsanaSession
2
+
3
+ __all__ = ["AsanaSession"]
@@ -0,0 +1,140 @@
1
+ # This file is auto-generated by tools/codegen.py — do not edit manually.
2
+ from __future__ import annotations
3
+
4
+ import click
5
+
6
+ from asana_api_cli.version import version_string
7
+
8
+ from asana_api_cli.session import runtime
9
+ from asana_api_cli.cli.access_requests import access_requests_group
10
+ from asana_api_cli.cli.allocations import allocations_group
11
+ from asana_api_cli.cli.attachments import attachments_group
12
+ from asana_api_cli.cli.audit_log_api import audit_log_api_group
13
+ from asana_api_cli.cli.batch_api import batch_api_group
14
+ from asana_api_cli.cli.budgets import budgets_group
15
+ from asana_api_cli.cli.custom_field_settings import custom_field_settings_group
16
+ from asana_api_cli.cli.custom_fields import custom_fields_group
17
+ from asana_api_cli.cli.custom_types import custom_types_group
18
+ from asana_api_cli.cli.events import events_group
19
+ from asana_api_cli.cli.exports import exports_group
20
+ from asana_api_cli.cli.goal_relationships import goal_relationships_group
21
+ from asana_api_cli.cli.goals import goals_group
22
+ from asana_api_cli.cli.jobs import jobs_group
23
+ from asana_api_cli.cli.memberships import memberships_group
24
+ from asana_api_cli.cli.organization_exports import organization_exports_group
25
+ from asana_api_cli.cli.portfolio_memberships import portfolio_memberships_group
26
+ from asana_api_cli.cli.portfolios import portfolios_group
27
+ from asana_api_cli.cli.project_briefs import project_briefs_group
28
+ from asana_api_cli.cli.project_memberships import project_memberships_group
29
+ from asana_api_cli.cli.project_portfolio_settings import project_portfolio_settings_group
30
+ from asana_api_cli.cli.project_statuses import project_statuses_group
31
+ from asana_api_cli.cli.project_templates import project_templates_group
32
+ from asana_api_cli.cli.projects import projects_group
33
+ from asana_api_cli.cli.rates import rates_group
34
+ from asana_api_cli.cli.reactions import reactions_group
35
+ from asana_api_cli.cli.roles import roles_group
36
+ from asana_api_cli.cli.rules import rules_group
37
+ from asana_api_cli.cli.sections import sections_group
38
+ from asana_api_cli.cli.status_updates import status_updates_group
39
+ from asana_api_cli.cli.stories import stories_group
40
+ from asana_api_cli.cli.tags import tags_group
41
+ from asana_api_cli.cli.task_templates import task_templates_group
42
+ from asana_api_cli.cli.tasks import tasks_group
43
+ from asana_api_cli.cli.team_memberships import team_memberships_group
44
+ from asana_api_cli.cli.teams import teams_group
45
+ from asana_api_cli.cli.time_periods import time_periods_group
46
+ from asana_api_cli.cli.time_tracking_categories import time_tracking_categories_group
47
+ from asana_api_cli.cli.time_tracking_entries import time_tracking_entries_group
48
+ from asana_api_cli.cli.timesheet_approval_statuses import timesheet_approval_statuses_group
49
+ from asana_api_cli.cli.typeahead import typeahead_group
50
+ from asana_api_cli.cli.user_task_lists import user_task_lists_group
51
+ from asana_api_cli.cli.users import users_group
52
+ from asana_api_cli.cli.webhooks import webhooks_group
53
+ from asana_api_cli.cli.workspace_memberships import workspace_memberships_group
54
+ from asana_api_cli.cli.workspaces import workspaces_group
55
+
56
+
57
+ @click.group()
58
+ @click.version_option(version_string(), prog_name="asana-api")
59
+ @click.option("--host", default=None, help="Override API base URL (default: https://app.asana.com/api/1.0)")
60
+ @click.option("--proxy", default=None, help="HTTP/HTTPS proxy URL")
61
+ @click.option("--no-verify-ssl", is_flag=True, default=False, help="Disable TLS certificate verification (insecure)")
62
+ @click.option("--ca-cert", "ca_cert", default=None, type=click.Path(exists=True, dir_okay=False), help="Path to a PEM bundle of trusted CA certificates")
63
+ @click.option("--page-limit", "page_limit", type=int, default=None, help="Default per-page size for paginated endpoints")
64
+ @click.option("--retries", type=int, default=None, help="Number of retries on 429/5xx responses (default: 5)")
65
+ @click.option("--timeout", type=float, default=None, help="Per-request timeout in seconds")
66
+ @click.option("--token-env", "token_env", default=None, help="Environment variable name holding the Asana access token (default: ASANA_ACCESS_TOKEN)")
67
+ @click.option("--temp-dir", "temp_dir", default=None, type=click.Path(file_okay=False), help="Directory for temporary downloads")
68
+ @click.option("--debug", is_flag=True, default=False, help="Print HTTP request/response to stderr for troubleshooting")
69
+ def main(
70
+ host: str | None,
71
+ proxy: str | None,
72
+ no_verify_ssl: bool,
73
+ ca_cert: str | None,
74
+ page_limit: int | None,
75
+ retries: int | None,
76
+ timeout: float | None,
77
+ token_env: str | None,
78
+ temp_dir: str | None,
79
+ debug: bool,
80
+ ) -> None:
81
+ """Asana API CLI (SDK-backed wrapper)."""
82
+ runtime.host = host
83
+ runtime.proxy = proxy
84
+ runtime.verify_ssl = not no_verify_ssl
85
+ runtime.ssl_ca_cert = ca_cert
86
+ runtime.page_limit = page_limit
87
+ runtime.retries = retries
88
+ runtime.timeout = timeout
89
+ if token_env:
90
+ runtime.token_env = token_env
91
+ runtime.temp_dir = temp_dir
92
+ runtime.debug = debug
93
+
94
+
95
+ main.add_command(access_requests_group)
96
+ main.add_command(allocations_group)
97
+ main.add_command(attachments_group)
98
+ main.add_command(audit_log_api_group)
99
+ main.add_command(batch_api_group)
100
+ main.add_command(budgets_group)
101
+ main.add_command(custom_field_settings_group)
102
+ main.add_command(custom_fields_group)
103
+ main.add_command(custom_types_group)
104
+ main.add_command(events_group)
105
+ main.add_command(exports_group)
106
+ main.add_command(goal_relationships_group)
107
+ main.add_command(goals_group)
108
+ main.add_command(jobs_group)
109
+ main.add_command(memberships_group)
110
+ main.add_command(organization_exports_group)
111
+ main.add_command(portfolio_memberships_group)
112
+ main.add_command(portfolios_group)
113
+ main.add_command(project_briefs_group)
114
+ main.add_command(project_memberships_group)
115
+ main.add_command(project_portfolio_settings_group)
116
+ main.add_command(project_statuses_group)
117
+ main.add_command(project_templates_group)
118
+ main.add_command(projects_group)
119
+ main.add_command(rates_group)
120
+ main.add_command(reactions_group)
121
+ main.add_command(roles_group)
122
+ main.add_command(rules_group)
123
+ main.add_command(sections_group)
124
+ main.add_command(status_updates_group)
125
+ main.add_command(stories_group)
126
+ main.add_command(tags_group)
127
+ main.add_command(task_templates_group)
128
+ main.add_command(tasks_group)
129
+ main.add_command(team_memberships_group)
130
+ main.add_command(teams_group)
131
+ main.add_command(time_periods_group)
132
+ main.add_command(time_tracking_categories_group)
133
+ main.add_command(time_tracking_entries_group)
134
+ main.add_command(timesheet_approval_statuses_group)
135
+ main.add_command(typeahead_group)
136
+ main.add_command(user_task_lists_group)
137
+ main.add_command(users_group)
138
+ main.add_command(webhooks_group)
139
+ main.add_command(workspace_memberships_group)
140
+ main.add_command(workspaces_group)
@@ -0,0 +1,66 @@
1
+ # This file is auto-generated by tools/codegen.py — do not edit manually.
2
+ from __future__ import annotations
3
+
4
+ from typing import Any
5
+
6
+ import click
7
+ from asana import AccessRequestsApi
8
+
9
+ from asana_api_cli.formatter import formatted
10
+ from asana_api_cli.session import AsanaSession, resolve_body, resolve_workspace
11
+
12
+
13
+ @click.group("access-requests")
14
+ def access_requests_group() -> None:
15
+ """AccessRequests commands."""
16
+
17
+
18
+ @access_requests_group.command("approve-access-request")
19
+ @click.option("--access-request", required=True, help="Globally unique identifier for the access request. If the method is called asynchronously, returns the request thread.")
20
+ @formatted
21
+ def approve_access_request(access_request: str) -> Any:
22
+ """Approve an access request"""
23
+ session = AsanaSession.from_env()
24
+ api = AccessRequestsApi(session.client)
25
+ opts: dict[str, Any] = {}
26
+ return api.approve_access_request(access_request)
27
+
28
+
29
+ @access_requests_group.command("create-access-request")
30
+ @click.option("--body", required=True, help="If the method is called asynchronously, returns the request thread.")
31
+ @formatted
32
+ def create_access_request(body: str) -> Any:
33
+ """Create an access request"""
34
+ parsed_body = resolve_body(body)
35
+ session = AsanaSession.from_env()
36
+ api = AccessRequestsApi(session.client)
37
+ opts: dict[str, Any] = {}
38
+ return api.create_access_request(parsed_body)
39
+
40
+
41
+ @access_requests_group.command("get-access-requests")
42
+ @click.option("--target", required=True, help="Globally unique identifier for the target object.")
43
+ @click.option("--opt-fields", default=None, help="This endpoint returns a resource which excludes some properties by default. To include those optional properties, set this query parameter to a comma-separated list of the properties you wish to in...")
44
+ @click.option("--user", default=None, help="A string identifying a user. This can either be the string \"me\", an email, or the gid of a user.")
45
+ @formatted
46
+ def get_access_requests(target: str, opt_fields: str | None, user: str | None) -> Any:
47
+ """Get access requests"""
48
+ session = AsanaSession.from_env()
49
+ api = AccessRequestsApi(session.client)
50
+ opts: dict[str, Any] = {}
51
+ if opt_fields is not None:
52
+ opts["opt_fields"] = opt_fields
53
+ if user is not None:
54
+ opts["user"] = user
55
+ return api.get_access_requests(target, opts)
56
+
57
+
58
+ @access_requests_group.command("reject-access-request")
59
+ @click.option("--access-request", required=True, help="Globally unique identifier for the access request. If the method is called asynchronously, returns the request thread.")
60
+ @formatted
61
+ def reject_access_request(access_request: str) -> Any:
62
+ """Reject an access request"""
63
+ session = AsanaSession.from_env()
64
+ api = AccessRequestsApi(session.client)
65
+ opts: dict[str, Any] = {}
66
+ return api.reject_access_request(access_request)