Skip to content

Commit 66be4e2

Browse files
author
Juliya Smith
authored
Reactivate (#209)
1 parent 071831e commit 66be4e2

File tree

4 files changed

+129
-18
lines changed

4 files changed

+129
-18
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ how a consumer would use the library (e.g. adding unit tests, updating documenta
1313
# Added
1414

1515
- The `devices` command is added. Included are:
16-
- `devices deactivate` to deactivate a single computer.
17-
- `devices show` to retrieve detailed information about a computer.
16+
- `devices deactivate` to deactivate a single device.
17+
- `devices reactivate` to reactivate a single device.
18+
- `devices show` to retrieve detailed information about a device.
1819
- `devices list` to retrieve info about many devices, including device settings.
1920
- `devices list-backup-sets` to retrieve detailed info about device backup sets.
2021
- `devices bulk deactivate` to deactivate a list of devices.
22+
- `devices bulk reactivate` to reactivate a list of devices.
2123

2224
- `code42 departing-employee list` command.
2325

src/code42cli/bulk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def run_bulk_process(row_handler, rows, progress_label=None):
7474
row_handler (callable): A callable that you define to process values from the row as
7575
either *args or **kwargs.
7676
rows (iterable): the rows to process.
77+
progress_label: a label that prints with the progress bar.
7778
"""
7879
processor = _create_bulk_processor(row_handler, rows, progress_label)
7980
return processor.run()
@@ -93,7 +94,6 @@ class BulkProcessor:
9394
and first row `1,test`, then `row_handler` should receive kwargs
9495
`prop_a: '1', prop_b: 'test'` when processing the first row. If it's a flat file, then
9596
`row_handler` only needs to take an extra arg.
96-
reader (CSVReader or FlatFileReader): A generator that reads rows and yields data into `row_handler`.
9797
"""
9898

9999
def __init__(self, row_handler, rows, worker=None, progress_label=None):

src/code42cli/cmds/devices.py

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ def devices(state):
2828
pass
2929

3030

31-
device_guid_argument = click.argument("device-guid", type=str)
31+
device_guid_argument = click.argument(
32+
"device-guid", type=str, callback=lambda ctx, param, arg: _verify_guid_type(arg),
33+
)
3234

3335
change_device_name_option = click.option(
3436
"--change-device-name",
@@ -59,20 +61,19 @@ def deactivate(state, device_guid, change_device_name, purge_date):
5961
_deactivate_device(state.sdk, device_guid, change_device_name, purge_date)
6062

6163

64+
@devices.command()
65+
@device_guid_argument
66+
@sdk_options()
67+
def reactivate(state, device_guid):
68+
"""Reactivate a device within Code42. Requires the device GUID to reactivate."""
69+
_reactivate_device(state.sdk, device_guid)
70+
71+
6272
def _deactivate_device(sdk, device_guid, change_device_name, purge_date):
6373
try:
64-
int(device_guid)
65-
except ValueError:
66-
raise Code42CLIError("Not a valid guid.")
67-
try:
68-
device = sdk.devices.get_by_guid(device_guid)
69-
sdk.devices.deactivate(device.data["computerId"])
74+
device = _change_device_activation(sdk, device_guid, "deactivate")
7075
except exceptions.Py42BadRequestError:
7176
raise Code42CLIError("The device {} is in legal hold.".format(device_guid))
72-
except exceptions.Py42NotFoundError:
73-
raise Code42CLIError("The device {} was not found.".format(device_guid))
74-
except exceptions.Py42ForbiddenError:
75-
raise Code42CLIError("Unable to deactivate {}.".format(device_guid))
7677
if purge_date:
7778
_update_cold_storage_purge_date(sdk, device_guid, purge_date)
7879
if change_device_name and not device.data["name"].startswith("deactivated_"):
@@ -86,6 +87,35 @@ def _deactivate_device(sdk, device_guid, change_device_name, purge_date):
8687
)
8788

8889

90+
def _reactivate_device(sdk, device_guid):
91+
_change_device_activation(sdk, device_guid, "reactivate")
92+
93+
94+
def _change_device_activation(sdk, device_guid, cmd_str):
95+
try:
96+
device = sdk.devices.get_by_guid(device_guid)
97+
device_id = device.data["computerId"]
98+
if cmd_str == "reactivate":
99+
sdk.devices.reactivate(device_id)
100+
elif cmd_str == "deactivate":
101+
sdk.devices.deactivate(device_id)
102+
return device
103+
except exceptions.Py42NotFoundError:
104+
raise Code42CLIError("The device {} was not found.".format(device_guid))
105+
except exceptions.Py42ForbiddenError:
106+
raise Code42CLIError("Unable to {} {}.".format(cmd_str, device_guid))
107+
108+
109+
def _verify_guid_type(device_guid):
110+
if device_guid is None:
111+
return
112+
try:
113+
int(device_guid)
114+
return device_guid
115+
except ValueError:
116+
raise Code42CLIError("Not a valid guid.")
117+
118+
89119
def _update_cold_storage_purge_date(sdk, guid, purge_date):
90120
archives_response = sdk.archive.get_all_by_device_guid(guid)
91121
archive_guid_list = [
@@ -455,3 +485,27 @@ def handle_row(**row):
455485
handle_row, csv_rows, progress_label="Deactivating devices:"
456486
)
457487
formatter.echo_formatted_list(result_rows)
488+
489+
490+
@bulk.command(name="reactivate")
491+
@read_csv_arg(headers=["guid"])
492+
@format_option
493+
@sdk_options()
494+
def bulk_reactivate(state, csv_rows, format):
495+
"""Reactivate all devices from the provided CSV containing a 'guid' column."""
496+
sdk = state.sdk
497+
csv_rows[0]["reactivated"] = False
498+
formatter = OutputFormatter(format, {key: key for key in csv_rows[0].keys()})
499+
500+
def handle_row(**row):
501+
try:
502+
_reactivate_device(sdk, row["guid"])
503+
row["reactivated"] = "True"
504+
except Exception as e:
505+
row["reactivated"] = "False: {}".format(e)
506+
return row
507+
508+
result_rows = run_bulk_process(
509+
handle_row, csv_rows, progress_label="Reactivating devices:"
510+
)
511+
formatter.echo_formatted_list(result_rows)

tests/cmds/test_devices.py

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def mock_backup_set(mocker):
254254

255255

256256
@pytest.fixture
257-
def deactivate_response(mocker):
257+
def empty_successful_response(mocker):
258258
return _create_py42_response(mocker, "")
259259

260260

@@ -303,15 +303,25 @@ def archives_list_success(cli_state):
303303

304304

305305
@pytest.fixture
306-
def deactivate_device_success(cli_state, deactivate_response):
307-
cli_state.sdk.devices.deactivate.return_value = deactivate_response
306+
def deactivate_device_success(cli_state, empty_successful_response):
307+
cli_state.sdk.devices.deactivate.return_value = empty_successful_response
308+
309+
310+
@pytest.fixture
311+
def reactivate_device_success(cli_state, empty_successful_response):
312+
cli_state.sdk.devices.reactivate.return_value = empty_successful_response
308313

309314

310315
@pytest.fixture
311316
def deactivate_device_not_found_failure(cli_state):
312317
cli_state.sdk.devices.deactivate.side_effect = Py42NotFoundError(HTTPError())
313318

314319

320+
@pytest.fixture
321+
def reactivate_device_not_found_failure(cli_state):
322+
cli_state.sdk.devices.reactivate.side_effect = Py42NotFoundError(HTTPError())
323+
324+
315325
@pytest.fixture
316326
def deactivate_device_in_legal_hold_failure(cli_state):
317327
cli_state.sdk.devices.deactivate.side_effect = Py42BadRequestError(HTTPError())
@@ -322,6 +332,11 @@ def deactivate_device_not_allowed_failure(cli_state):
322332
cli_state.sdk.devices.deactivate.side_effect = Py42ForbiddenError(HTTPError())
323333

324334

335+
@pytest.fixture
336+
def reactivate_device_not_allowed_failure(cli_state):
337+
cli_state.sdk.devices.reactivate.side_effect = Py42ForbiddenError(HTTPError())
338+
339+
325340
@pytest.fixture
326341
def backupusage_success(cli_state, backupusage_response):
327342
cli_state.sdk.devices.get_by_guid.return_value = backupusage_response
@@ -440,6 +455,33 @@ def test_deactivate_fails_if_device_deactivation_forbidden(
440455
assert "Unable to deactivate {}.".format(TEST_DEVICE_GUID) in result.output
441456

442457

458+
def test_reactivate_reactivates_device(
459+
runner, cli_state, deactivate_device_success, get_device_by_guid_success
460+
):
461+
runner.invoke(cli, ["devices", "reactivate", TEST_DEVICE_GUID], obj=cli_state)
462+
cli_state.sdk.devices.reactivate.assert_called_once_with(TEST_DEVICE_ID)
463+
464+
465+
def test_reactivate_fails_if_device_does_not_exist(
466+
runner, cli_state, reactivate_device_not_found_failure
467+
):
468+
result = runner.invoke(
469+
cli, ["devices", "reactivate", TEST_DEVICE_GUID], obj=cli_state
470+
)
471+
assert result.exit_code == 1
472+
assert f"The device {TEST_DEVICE_GUID} was not found." in result.output
473+
474+
475+
def test_reactivate_fails_if_device_reactivation_forbidden(
476+
runner, cli_state, reactivate_device_not_allowed_failure
477+
):
478+
result = runner.invoke(
479+
cli, ["devices", "reactivate", TEST_DEVICE_GUID], obj=cli_state
480+
)
481+
assert result.exit_code == 1
482+
assert f"Unable to reactivate {TEST_DEVICE_GUID}." in result.output
483+
484+
443485
def test_show_prints_device_info(runner, cli_state, backupusage_success):
444486
result = runner.invoke(cli, ["devices", "show", TEST_DEVICE_GUID], obj=cli_state)
445487
assert "SNWINTEST1" in result.output
@@ -581,7 +623,7 @@ def test_add_backup_set_settings_to_dataframe_returns_one_line_per_backup_set(
581623

582624

583625
def test_bulk_deactivate_uses_expected_arguments(runner, mocker, cli_state):
584-
bulk_processor = mocker.patch("{}.run_bulk_process".format(_NAMESPACE))
626+
bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process")
585627
with runner.isolated_filesystem():
586628
with open("test_bulk_deactivate.csv", "w") as csv:
587629
csv.writelines(["guid,username\n", "test,value\n"])
@@ -598,3 +640,16 @@ def test_bulk_deactivate_uses_expected_arguments(runner, mocker, cli_state):
598640
"purge_date": None,
599641
}
600642
]
643+
644+
645+
def test_bulk_reactivate_uses_expected_arguments(runner, mocker, cli_state):
646+
bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process")
647+
with runner.isolated_filesystem():
648+
with open("test_bulk_reactivate.csv", "w") as csv:
649+
csv.writelines(["guid,username\n", "test,value\n"])
650+
runner.invoke(
651+
cli,
652+
["devices", "bulk", "reactivate", "test_bulk_reactivate.csv"],
653+
obj=cli_state,
654+
)
655+
assert bulk_processor.call_args[0][1] == [{"guid": "test", "reactivated": False}]

0 commit comments

Comments
 (0)