-
Notifications
You must be signed in to change notification settings - Fork 10
Creating PR with the latest changes #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
c89024c
Creating PR with the latest changes
tommyg-cld 546ed06
Updating PR
tommyg-cld 11828cc
Updating PR
tommyg-cld f0c42e5
Updating PR
tommyg-cld 356983d
Updating PR
tommyg-cld 08e727f
Updating PR
tommyg-cld 0d2f1c4
Updating PR
tommyg-cld 7114274
Updating PR
tommyg-cld ffd5285
Updating PR
tommyg-cld bb95c45
Updating PR
tommyg-cld ceb2a30
Updating PR with latest logic changes
tommyg-cld 6160549
Updating PR with fixes for the comments
tommyg-cld 4f94af0
Added a comment
tommyg-cld 9a89159
Updaing help
tommyg-cld 11875d7
Refactor code, normalize parameters
const-cloudinary 8a02162
Update cloudinary_cli/modules/clone.py
tommyg-cld 39fdcde
Update cloudinary_cli/modules/clone.py
tommyg-cld 89d576f
Update cloudinary_cli/modules/clone.py
tommyg-cld 97d55e1
Update cloudinary_cli/modules/clone.py
tommyg-cld c0f9f5d
Fixing doc strings and removing target as an optional parameter
tommyg-cld 218dfdf
Merge branch 'master' into feature/add-new-copy-module
const-cloudinary c8a329b
Bump python version in tests
const-cloudinary 62fe136
Fix typo
const-cloudinary File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| from click import command, option, style, argument | ||
| from cloudinary_cli.utils.utils import normalize_list_params, print_help_and_exit | ||
| import cloudinary | ||
| from cloudinary_cli.utils.utils import run_tasks_concurrently | ||
| from cloudinary_cli.utils.api_utils import upload_file | ||
| from cloudinary_cli.utils.config_utils import load_config, get_cloudinary_config, config_to_dict | ||
| from cloudinary_cli.defaults import logger | ||
| from cloudinary_cli.core.search import execute_single_request, handle_auto_pagination | ||
|
|
||
| DEFAULT_MAX_RESULTS = 500 | ||
|
|
||
|
|
||
| @command("clone", | ||
| short_help="""Clone assets from one product environment to another.""", | ||
| help=""" | ||
| \b | ||
| Clone assets from one product environment to another with/without tags and/or context (structured metadata is not currently supported). | ||
| Source will be your `CLOUDINARY_URL` environment variable but you also can specify a different source using the `-c/-C` option. | ||
| Cloning restricted assets is also not supported currently. | ||
| Format: cld clone <target_environment> <command options> | ||
| `<target_environment>` can be a CLOUDINARY_URL or a saved config (see `config` command) | ||
| Example 1 (Copy all assets including tags and context using CLOUDINARY URL): | ||
| cld clone cloudinary://<api_key>:<api_secret>@<cloudname> -fi tags,context | ||
| Example 2 (Copy all assets with a specific tag via a search expression using a saved config): | ||
| cld clone <config_name> -se "tags:<tag_name>" | ||
| """) | ||
| @argument("target") | ||
| @option("-F", "--force", is_flag=True, | ||
| help="Skip confirmation.") | ||
| @option("-ow", "--overwrite", is_flag=True, default=False, | ||
| help="Specify whether to overwrite existing assets.") | ||
| @option("-w", "--concurrent_workers", type=int, default=30, | ||
| help="Specify the number of concurrent network threads.") | ||
| @option("-fi", "--fields", multiple=True, | ||
| help="Specify whether to copy tags and/or context. Valid options: `tags,context`.") | ||
| @option("-se", "--search_exp", default="", | ||
| help="Define a search expression to filter the assets to clone.") | ||
| @option("--async", "async_", is_flag=True, default=False, | ||
const-cloudinary marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| help="Clone the assets asynchronously.") | ||
| @option("-nu", "--notification_url", | ||
| help="Webhook notification URL.") | ||
| def clone(target, force, overwrite, concurrent_workers, fields, search_exp, async_, notification_url): | ||
| if not target: | ||
| print_help_and_exit() | ||
|
|
||
| target_config = get_cloudinary_config(target) | ||
| if not target_config: | ||
| logger.error("The specified config does not exist or the CLOUDINARY_URL scheme provided is invalid" | ||
| " (expecting to start with 'cloudinary://').") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this allow for a saved config? |
||
| return False | ||
|
|
||
| if cloudinary.config().cloud_name == target_config.cloud_name: | ||
| logger.error("Target environment cannot be the same as source environment.") | ||
| return False | ||
|
|
||
| source_assets = search_assets(force, search_exp) | ||
|
|
||
| upload_list = [] | ||
| for r in source_assets.get('resources'): | ||
| updated_options, asset_url = process_metadata(r, overwrite, async_, notification_url, | ||
| normalize_list_params(fields)) | ||
| updated_options.update(config_to_dict(target_config)) | ||
| upload_list.append((asset_url, {**updated_options})) | ||
|
|
||
| if not upload_list: | ||
| logger.error(style(f'No assets found in {cloudinary.config().cloud_name}', fg="red")) | ||
| return False | ||
|
|
||
| logger.info(style(f'Copying {len(upload_list)} asset(s) from {cloudinary.config().cloud_name} to {target_config.cloud_name}', fg="blue")) | ||
|
|
||
| run_tasks_concurrently(upload_file, upload_list, concurrent_workers) | ||
|
|
||
| return True | ||
|
|
||
|
|
||
| def search_assets(force, search_exp): | ||
| search = cloudinary.search.Search().expression(search_exp) | ||
| search.fields(['tags', 'context', 'access_control', 'secure_url', 'display_name']) | ||
| search.max_results(DEFAULT_MAX_RESULTS) | ||
|
|
||
| res = execute_single_request(search, fields_to_keep="") | ||
| res = handle_auto_pagination(res, search, force, fields_to_keep="") | ||
|
|
||
| return res | ||
|
|
||
|
|
||
| def process_metadata(res, overwrite, async_, notification_url, copy_fields=""): | ||
| cloned_options = {} | ||
| asset_url = res.get('secure_url') | ||
| cloned_options['public_id'] = res.get('public_id') | ||
| cloned_options['type'] = res.get('type') | ||
| cloned_options['resource_type'] = res.get('resource_type') | ||
| cloned_options['overwrite'] = overwrite | ||
| cloned_options['async'] = async_ | ||
| if "tags" in copy_fields: | ||
| cloned_options['tags'] = res.get('tags') | ||
| if "context" in copy_fields: | ||
| cloned_options['context'] = res.get('context') | ||
| if res.get('folder'): | ||
| # This is required to put the asset in the correct asset_folder | ||
| # when copying from a fixed to DF (dynamic folder) cloud as if | ||
| # you just pass a `folder` param to a DF cloud, it will append | ||
| # this to the `public_id` and we don't want this. | ||
| cloned_options['asset_folder'] = res.get('folder') | ||
const-cloudinary marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| elif res.get('asset_folder'): | ||
| cloned_options['asset_folder'] = res.get('asset_folder') | ||
| if res.get('display_name'): | ||
| cloned_options['display_name'] = res.get('display_name') | ||
| if notification_url: | ||
| cloned_options['notification_url'] = notification_url | ||
|
|
||
| return cloned_options, asset_url | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to show an example, or state the valid options.