Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions DOCKER.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ docker run --rm -e CLOUDINARY_URL cloudinary-cli uploader -ls
# List all admin methods
docker run --rm -e CLOUDINARY_URL cloudinary-cli admin -ls

# List all collections methods
docker run --rm -e CLOUDINARY_URL cloudinary-cli collections -ls

# Get help for specific commands
docker run --rm -e CLOUDINARY_URL cloudinary-cli uploader --help
docker run --rm -e CLOUDINARY_URL cloudinary-cli search --help
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ cld logout # Revokes and removes a saved OAuth login.
cld search --help # Shows usage for the Search API.
cld admin # Lists Admin API methods.
cld uploader # Lists Upload API methods.
cld collections # Lists Collections API methods.
cld agent signup # For AI agents: creates a Cloudinary account on behalf of a human.
cld agent cloud # For AI agents: creates a temporary cloud that works immediately.
```
Expand Down Expand Up @@ -236,6 +237,32 @@ cld admin transformation my_new_transformation

_**Note:** you can omit 'admin' from the command when calling an Admin API method._

## Collections API

Enables you to run any methods that can be called through the collections API, to organize assets into collections and share them through public pages.

You can find documentation for each of the Collections API methods at [https://cloudinary.com/documentation/collections_api](https://cloudinary.com/documentation/collections_api).

The basic syntax using the Collections API is as follows:

```
cld [cli options] collections [command options] [method] [method parameters]
```

Example: create a collection, add an asset to it and publish it as a public page:

```
cld collections create_collection "Summer campaign" -o description "Campaign assets"
cld collections add_collection_assets <collection_id> '["<asset_id>"]'
cld collections create_collection_public_page <collection_id>
```

Create a dynamic collection from a search expression:

```
cld collections create_collection "Summer images" -o type dynamic -O configuration '{"expression": "tags=summer"}'
```

## Search API

Runs the admin API search method, allowing you to use a Lucene query string as the expression.
Expand Down
2 changes: 2 additions & 0 deletions cloudinary_cli/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from cloudinary_cli.core.admin import admin
from cloudinary_cli.core.agent import agent_group
from cloudinary_cli.core.auth import login, logout
from cloudinary_cli.core.collections import collections
from cloudinary_cli.core.config import config_command
from cloudinary_cli.core.search import search, search_folders
from cloudinary_cli.core.uploader import uploader
Expand All @@ -22,6 +23,7 @@
admin,
uploader,
provisioning,
collections,
url,
utils
]
29 changes: 29 additions & 0 deletions cloudinary_cli/core/collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from click import command, argument, option
import cloudinary.collections

from cloudinary_cli.utils.api_utils import handle_api_command


@command("collections",
short_help="Run any methods that can be called through the collections API.",
help="""\b
Run any methods that can be called through the collections API.
Format: cld <cli options> collections <command options> <method> <method parameters>
\te.g. cld collections create_collection "Summer campaign" -o description "Campaign assets"
\t OR
\t cld collections add_collection_assets <collection_id> '["<asset_id>"]'
""")
@argument("params", nargs=-1)
@option("-o", "--optional_parameter", multiple=True, nargs=2,
help="Pass an optional parameter as a string, with no parsing. e.g. -o query summer")
@option("-O", "--optional_parameter_parsed", multiple=True, nargs=2,
help="Pass an optional parameter and parse its value as JSON or a boolean. "
"e.g. -O configuration '{\"expression\": \"tags=summer\"}'")
@option("-ls", "--ls", is_flag=True, help="List all available methods in the Collections API.")
@option("--save", nargs=1, help="Save output to a file.")
@option("-d", "--doc", is_flag=True, help="Open the Collections API reference in a browser.")
def collections(params, optional_parameter, optional_parameter_parsed, ls, save, doc):
return handle_api_command(params, optional_parameter, optional_parameter_parsed, ls, save, doc,
doc_url="https://cloudinary.com/documentation/collections_api",
api_instance=cloudinary.collections,
api_name="collections")
3 changes: 2 additions & 1 deletion cloudinary_cli/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@

not_callable = ('is_appengine_sandbox', 'call_tags_api', 'call_context_api', 'call_cacheable_api', 'call_api',
'call_metadata_api', 'call_json_api', 'only', 'transformation_string', 'account_config',
'reset_config', 'upload_large_part', 'upload_image', 'upload_resource', 'build_eager')
'reset_config', 'upload_large_part', 'upload_image', 'upload_resource', 'build_eager',
'base64url_decode')

BLOCK_SIZE = 65536

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cloudinary>=1.46.3
cloudinary>=1.47.0
pygments
jinja2
click
Expand Down
1 change: 1 addition & 0 deletions test/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class TestCLI(unittest.TestCase):
COMMANDS = [
'admin',
'agent',
'collections',
'config',
'login',
'logout',
Expand Down
17 changes: 17 additions & 0 deletions test/test_cli_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ def test_provisioning(self, mocker):
self.assertEqual(0, result.exit_code, result.output)
self.assertIn('"foo": "bar"', result.output)

@unittest.skipUnless(CONFIG_PRESENT, REQUIRES_CONFIG)
@patch(URLLIB3_REQUEST)
def test_collections(self, mocker):
mocker.return_value = API_MOCK_RESPONSE
result = self.runner.invoke(cli, ['collections', 'add_collection_assets', 'col1', '["a1", "a2"]'])

self.assertEqual(0, result.exit_code, result.output)
self.assertIn('"foo": "bar"', result.output)
self.assertIn('/collections/col1/assets', mocker.call_args[1]['url'])

def test_collections_ls(self):
result = self.runner.invoke(cli, ['collections', '-ls'])

self.assertEqual(0, result.exit_code, result.output)
self.assertIn('create_collection', result.output)
self.assertNotIn('base64url_decode', result.output)


class TestDestructiveBulkConfirmation(unittest.TestCase):
runner = CliRunner()
Expand Down
Loading