|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | + |
| 3 | +# This file is part of the Ingram Micro Cloud Blue Connect connect-cli. |
| 4 | +# Copyright (c) 2019-2024 Ingram Micro. All Rights Reserved. |
| 5 | + |
| 6 | +from collections import namedtuple |
| 7 | + |
| 8 | +from connect.client.rql import R |
| 9 | +from connect.client import ClientError, R |
| 10 | + |
| 11 | +from connect.cli.core.http import handle_http_error |
| 12 | +from connect.cli.plugins.shared.base import ProductSynchronizer |
| 13 | +from connect.cli.plugins.shared.constants import MESSAGES_HEADERS |
| 14 | + |
| 15 | + |
| 16 | +fields = (v.replace(' ', '_').lower() for v in MESSAGES_HEADERS.values()) |
| 17 | + |
| 18 | +_RowData = namedtuple('RowData', fields) |
| 19 | + |
| 20 | + |
| 21 | +class MessageSynchronizer(ProductSynchronizer): |
| 22 | + def __init__(self, client, progress, stats): |
| 23 | + self._mstats = stats['Messages'] |
| 24 | + super().__init__(client, progress) |
| 25 | + |
| 26 | + def _get_message(self, message_id): |
| 27 | + try: |
| 28 | + res = self._client.products[self._product_id].messages[message_id].get() |
| 29 | + except ClientError as error: |
| 30 | + if error.status_code == 404: |
| 31 | + return |
| 32 | + handle_http_error(error) |
| 33 | + return res |
| 34 | + |
| 35 | + def _create_message(self, data): |
| 36 | + try: |
| 37 | + payload = {'external_id': data.external_id, 'value': data.value, 'auto': data.auto} |
| 38 | + res = self._client.products[self._product_id].messages.create(payload) |
| 39 | + except ClientError as error: |
| 40 | + handle_http_error(error) |
| 41 | + return res |
| 42 | + |
| 43 | + def _update_message(self, data): |
| 44 | + try: |
| 45 | + payload = {'external_id': data.external_id, 'value': data.value, 'auto': data.auto} |
| 46 | + res = self._client.products[self._product_id].messages[data.id].update(payload) |
| 47 | + except ClientError as error: |
| 48 | + handle_http_error(error) |
| 49 | + return res |
| 50 | + |
| 51 | + def _delete_message(self, message_id): |
| 52 | + try: |
| 53 | + res = self._client.products[self._product_id].messages[message_id].delete() |
| 54 | + except ClientError as error: |
| 55 | + handle_http_error(error) |
| 56 | + return res |
| 57 | + |
| 58 | + def _process_create(self, row_idx, data, task): |
| 59 | + rql = R().external_id.eq(data.external_id) |
| 60 | + message = self._client.products[self._product_id].messages.filter(rql).first() |
| 61 | + if message: |
| 62 | + self._mstats.error( |
| 63 | + f'Cannot create message: message with external_id `{data.external_id}`' |
| 64 | + f' already exists with ID `{message["id"]}`.', |
| 65 | + row_idx, |
| 66 | + ) |
| 67 | + else: |
| 68 | + self._progress.update( |
| 69 | + task, |
| 70 | + description=f'Creating message {data.external_id}', |
| 71 | + ) |
| 72 | + try: |
| 73 | + new_message = self._create_message(data) |
| 74 | + self._mstats.created() |
| 75 | + self._update_sheet_row(self._ws, row_idx, new_message) |
| 76 | + except Exception as e: |
| 77 | + self._mstats.error(str(e), row_idx) |
| 78 | + |
| 79 | + def _process_update(self, row_idx, data, task): |
| 80 | + if not self._get_message(data.id): |
| 81 | + self._mstats.error( |
| 82 | + f'Cannot update message: message with ID `{data.id}` ' 'does not exist.', |
| 83 | + row_idx, |
| 84 | + ) |
| 85 | + else: |
| 86 | + self._progress.update( |
| 87 | + task, |
| 88 | + description=f'Updating message {data.id}', |
| 89 | + ) |
| 90 | + try: |
| 91 | + updated_message = self._update_message( |
| 92 | + data, |
| 93 | + ) |
| 94 | + self._mstats.updated() |
| 95 | + self._update_sheet_row(self._ws, row_idx, updated_message) |
| 96 | + except Exception as e: |
| 97 | + self._mstats.error(str(e), row_idx) |
| 98 | + |
| 99 | + def _process_delete(self, row_idx, data, task): |
| 100 | + if not self._get_message(data.id): |
| 101 | + self._mstats.error( |
| 102 | + f'Cannot delete message: message with ID `{data.id}` ' 'does not exist.', |
| 103 | + row_idx, |
| 104 | + ) |
| 105 | + else: |
| 106 | + self._progress.update( |
| 107 | + task, |
| 108 | + description=f'Deleting message {data.id}', |
| 109 | + ) |
| 110 | + try: |
| 111 | + self._delete_message(data.id) |
| 112 | + self._mstats.deleted() |
| 113 | + for c in range(1, self._ws.max_column + 1): |
| 114 | + self._ws.cell(row_idx, c, value='') |
| 115 | + except Exception as e: |
| 116 | + self._mstats.error(str(e), row_idx) |
| 117 | + |
| 118 | + def sync(self): # noqa: CCR001 |
| 119 | + self._ws = self._wb['Messages'] |
| 120 | + task = self._progress.add_task('Processing messages', total=self._ws.max_row - 1) |
| 121 | + for row_idx in range(2, self._ws.max_row + 1): |
| 122 | + data = _RowData(*[self._ws.cell(row_idx, col_idx).value for col_idx in range(1, 6)]) |
| 123 | + self._progress.update( |
| 124 | + task, |
| 125 | + description=f'Processing message {data.id}', |
| 126 | + advance=1, |
| 127 | + ) |
| 128 | + if data.action == '-': |
| 129 | + self._mstats.skipped() |
| 130 | + continue |
| 131 | + |
| 132 | + row_errors = self._validate_row(data) |
| 133 | + if row_errors: |
| 134 | + self._mstats.error(row_errors, row_idx) |
| 135 | + continue |
| 136 | + |
| 137 | + if data.action == 'create': |
| 138 | + self._process_create(row_idx, data, task) |
| 139 | + elif data.action == 'update': |
| 140 | + self._process_update(row_idx, data, task) |
| 141 | + elif data.action == 'delete': |
| 142 | + self._process_delete(row_idx, data, task) |
| 143 | + |
| 144 | + self._progress.update(task, completed=self._ws.max_row - 1) |
| 145 | + |
| 146 | + def _validate_row(self, row): # noqa: CCR001 |
| 147 | + errors = [] |
| 148 | + |
| 149 | + if row.action == 'create' and row.id: |
| 150 | + errors.append( |
| 151 | + 'the `ID` must not be specified for the `create` action.', |
| 152 | + ) |
| 153 | + return errors |
| 154 | + if row.action in ('update', 'delete') and not row.id: |
| 155 | + errors.append( |
| 156 | + 'the `ID` must be specified for the `update` or `delete` actions.', |
| 157 | + ) |
| 158 | + return errors |
| 159 | + if ( |
| 160 | + row.action in ('create', 'update', 'delete') |
| 161 | + and row.external_id is None |
| 162 | + or row.value is None |
| 163 | + ): |
| 164 | + errors.append( |
| 165 | + 'the `External ID` and `Value` must be specified for the `create`, `update` or `delete` actions.', |
| 166 | + ) |
| 167 | + return errors |
| 168 | + |
| 169 | + @staticmethod |
| 170 | + def _update_sheet_row(ws, row_idx, message): |
| 171 | + ws.cell(row_idx, 1, value=message['id']) |
| 172 | + ws.cell(row_idx, 2, value='-') |
| 173 | + ws.cell(row_idx, 3, value=message['external_id']) |
| 174 | + ws.cell(row_idx, 4, value=message['value']) |
| 175 | + ws.cell(row_idx, 5, value=message['auto']) |
0 commit comments