Skip to content

Commit

Permalink
Warn when both --info and explicit header args set the same value
Browse files Browse the repository at this point in the history
  • Loading branch information
vbaltrusaitis-reef committed Nov 19, 2023
1 parent 97097f9 commit 4c8c12c
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 68 deletions.
135 changes: 68 additions & 67 deletions b2/console_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from concurrent.futures import Executor, Future, ThreadPoolExecutor
from contextlib import suppress
from enum import Enum
from typing import Any, BinaryIO, Dict, List, Optional, Tuple
from typing import Any, BinaryIO, Callable, Dict, List, Optional, Tuple

import argcomplete
import b2sdk
Expand Down Expand Up @@ -393,6 +393,68 @@ def _get_file_retention_setting(cls, args):
return FileRetentionSetting(file_retention_mode, args.retainUntil)


class HeaderFlagsMixin(Described):
@classmethod
def _setup_parser(cls, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
'--cache-control',
help=
"optional Cache-Control header, value based on RFC 2616 section 14.9, example: 'public, max-age=86400')"
)
parser.add_argument(
'--content-disposition',
help=
"optional Content-Disposition header, value based on RFC 2616 section 19.5.1, example: 'attachment; filename=\"fname.ext\"'"
)
parser.add_argument(
'--content-encoding',
help=
"optional Content-Encoding header, value based on RFC 2616 section 14.11, example: 'gzip'"
)
parser.add_argument(
'--content-language',
help=
"optional Content-Language header, value based on RFC 2616 section 14.12, example: 'mi, en'"
)
parser.add_argument(
'--expires',
help=
"optional Expires header, value based on RFC 2616 section 14.21, example: 'Thu, 01 Dec 2050 16:00:00 GMT'"
)
super()._setup_parser(parser)

def _file_info_with_header_args(self, args, file_info: dict[str, str] | None) -> dict[str, str] | None:
"""Construct an updated file_info dictionary.
Print a warning if any of file_info items will be overwritten by explicit header arguments.
"""
add_file_info = {}
overwritten = []
if args.cache_control is not None:
add_file_info['b2-cache-control'] = args.cache_control
if args.content_disposition is not None:
add_file_info['b2-content-disposition'] = args.content_disposition
if args.content_encoding is not None:
add_file_info['b2-content-encoding'] = args.content_encoding
if args.content_language is not None:
add_file_info['b2-content-language'] = args.content_language
if args.expires is not None:
add_file_info['b2-expires'] = args.expires

for key, value in add_file_info.items():
if file_info is not None and key in file_info and file_info[key] != value:
overwritten.append(key)

if overwritten:
self._print_stderr(
'The following file info items will be overwritten by explicit arguments:\n ' +
'\n '.join(f'{key} = {add_file_info[key]}' for key in overwritten)
)

if add_file_info:
return {**(file_info or {}), **add_file_info}
return file_info


class LegalHoldMixin(Described):
"""
Setting legal holds requires the **writeFileLegalHolds** capability, and only works in bucket
Expand Down Expand Up @@ -1010,7 +1072,7 @@ def run(self, args):

@B2.register_subcommand
class CopyFileById(
DestinationSseMixin, SourceSseMixin, FileRetentionSettingMixin, LegalHoldMixin, Command
HeaderFlagsMixin, DestinationSseMixin, SourceSseMixin, FileRetentionSettingMixin, LegalHoldMixin, Command
):
"""
Copy a file version to the given bucket (server-side, **not** via download+upload).
Expand Down Expand Up @@ -1053,31 +1115,6 @@ def _setup_parser(cls, parser):
parser.add_argument('--metadataDirective', default=None, help=argparse.SUPPRESS)
parser.add_argument('--contentType')
parser.add_argument('--range', type=parse_range)
parser.add_argument(
'--cache-control',
help=
"optional Cache-Control header, value based on RFC 2616 section 14.9, example: 'public, max-age=86400')"
)
parser.add_argument(
'--content-disposition',
help=
"optional Content-Disposition header, value based on RFC 2616 section 19.5.1, example: 'attachment; filename=\"fname.ext\"'"
)
parser.add_argument(
'--content-encoding',
help=
"optional Content-Encoding header, value based on RFC 2616 section 14.11, example: 'gzip'"
)
parser.add_argument(
'--content-language',
help=
"optional Content-Language header, value based on RFC 2616 section 14.12, example: 'mi, en'"
)
parser.add_argument(
'--expires',
help=
"optional Expires header, value based on RFC 2616 section 14.21, example: 'Thu, 01 Dec 2050 16:00:00 GMT'"
)

info_group = parser.add_mutually_exclusive_group()

Expand All @@ -1097,6 +1134,7 @@ def run(self, args):
file_infos = self._parse_file_infos(args.info)
elif args.noInfo:
file_infos = {}
file_infos = self._file_info_with_header_args(args, file_infos)

if args.metadataDirective is not None:
self._print_stderr(
Expand Down Expand Up @@ -1136,11 +1174,6 @@ def run(self, args):
file_retention=file_retention,
source_file_info=source_file_info,
source_content_type=source_content_type,
cache_control=args.cache_control,
expires=args.expires,
content_disposition=args.content_disposition,
content_encoding=args.content_encoding,
content_language=args.content_language,
)
self._print_json(file_version)
return 0
Expand Down Expand Up @@ -2892,6 +2925,7 @@ def _setup_parser(cls, parser):


class UploadFileMixin(
HeaderFlagsMixin,
MinPartSizeMixin,
ThreadsMixin,
ProgressMixin,
Expand Down Expand Up @@ -2920,31 +2954,6 @@ def _setup_parser(cls, parser):
parser.add_argument(
'--sha1', help="SHA-1 of the data being uploaded for verifying file integrity"
)
parser.add_argument(
'--cache-control',
help=
"optional Cache-Control header, value based on RFC 2616 section 14.9, example: 'public, max-age=86400')"
)
parser.add_argument(
'--content-disposition',
help=
"optional Content-Disposition header, value based on RFC 2616 section 19.5.1, example: 'attachment; filename=\"fname.ext\"'"
)
parser.add_argument(
'--content-encoding',
help=
"optional Content-Encoding header, value based on RFC 2616 section 14.11, example: 'gzip'"
)
parser.add_argument(
'--content-language',
help=
"optional Content-Language header, value based on RFC 2616 section 14.12, example: 'mi, en'"
)
parser.add_argument(
'--expires',
help=
"optional Expires header, value based on RFC 2616 section 14.21, example: 'Thu, 01 Dec 2050 16:00:00 GMT'"
)
parser.add_argument(
'--info',
action='append',
Expand Down Expand Up @@ -2991,25 +3000,17 @@ def get_execute_kwargs(self, args) -> dict:
else:
file_infos[SRC_LAST_MODIFIED_MILLIS] = str(int(mtime * 1000))

file_infos = self._file_info_with_header_args(args, file_infos)

return {
"bucket":
self.api.get_bucket_by_name(args.bucketName),
"cache_control":
args.cache_control,
"content_disposition":
args.content_disposition,
"content_encoding":
args.content_encoding,
"content_language":
args.content_language,
"content_type":
args.contentType,
"custom_upload_timestamp":
args.custom_upload_timestamp,
"encryption":
self._get_destination_sse_setting(args),
"expires":
args.expires,
"file_info":
file_infos,
"file_name":
Expand Down
10 changes: 9 additions & 1 deletion test/integration/test_b2_command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -2714,7 +2714,7 @@ def assert_expected(file_info, expected=expected_file_info):
for key, val in expected.items():
assert file_info[key] == val

file_version = b2_tool.should_succeed_json(
status, stdout, stderr = b2_tool.execute(
[
'upload-file',
'--quiet',
Expand All @@ -2723,10 +2723,18 @@ def assert_expected(file_info, expected=expected_file_info):
str(sample_filepath),
'sample_file',
*args,
'--info', 'b2-content-disposition=will-be-overwritten',
]
)
assert status == 0
file_version = json.loads(stdout)
assert_expected(file_version['fileInfo'])

# Since we used both --info and --content-disposition to set b2-content-disposition,
# a warning should be emitted
assert 'will be overwritten' in stderr and 'b2-content-disposition = attachment' in stderr


copied_version = b2_tool.should_succeed_json(
[
'copy-file-by-id', '--quiet', *args, '--contentType', 'text/plain',
Expand Down

0 comments on commit 4c8c12c

Please sign in to comment.