diff --git a/tableauserverclient/models/schedule_item.py b/tableauserverclient/models/schedule_item.py index 94823d6c2..5ece2f8fe 100644 --- a/tableauserverclient/models/schedule_item.py +++ b/tableauserverclient/models/schedule_item.py @@ -162,10 +162,7 @@ def from_response(cls, resp, ns): @classmethod def from_element(cls, parsed_response, ns): - all_warning_xml = parsed_response.findall('.//t:warning', namespaces=ns) - warnings = list() if len(all_warning_xml) > 0 else None - for warning_xml in all_warning_xml: - warnings.append(warning_xml.get('message', None)) + warnings = cls._read_warnings(parsed_response, ns) all_schedule_items = [] all_schedule_xml = parsed_response.findall('.//t:schedule', namespaces=ns) @@ -248,3 +245,22 @@ def _parse_element(schedule_xml, ns): return id, name, state, created_at, updated_at, schedule_type, \ next_run_at, end_schedule_at, execution_order, priority, interval_item + + @staticmethod + def parse_add_to_schedule_response(response, ns): + parsed_response = ET.fromstring(response.content) + warnings = ScheduleItem._read_warnings(parsed_response, ns) + all_task_xml = parsed_response.findall('.//t:task', namespaces=ns) + + error = "Status {}: {}".format(response.status_code, response.reason) \ + if response.status_code < 200 or response.status_code >= 300 else None + task_created = len(all_task_xml) > 0 + return error, warnings, task_created + + @staticmethod + def _read_warnings(parsed_response, ns): + all_warning_xml = parsed_response.findall('.//t:warning', namespaces=ns) + warnings = list() if len(all_warning_xml) > 0 else None + for warning_xml in all_warning_xml: + warnings.append(warning_xml.get('message', None)) + return warnings diff --git a/tableauserverclient/server/endpoint/schedules_endpoint.py b/tableauserverclient/server/endpoint/schedules_endpoint.py index ccba83565..06fb7e408 100644 --- a/tableauserverclient/server/endpoint/schedules_endpoint.py +++ b/tableauserverclient/server/endpoint/schedules_endpoint.py @@ -7,8 +7,8 @@ logger = logging.getLogger('tableau.endpoint.schedules') # Oh to have a first class Result concept in Python... -AddResponse = namedtuple('AddResponse', ('result', 'error')) -OK = AddResponse(result=True, error=None) +AddResponse = namedtuple('AddResponse', ('result', 'error', 'warnings', 'task_created')) +OK = AddResponse(result=True, error=None, warnings=None, task_created=None) class Schedules(Endpoint): @@ -70,17 +70,21 @@ def create(self, schedule_item): @api(version="2.8") def add_to_schedule(self, schedule_id, workbook=None, datasource=None, task_type=TaskItem.Type.ExtractRefresh): - def add_to(resource, type_, req_factory): id_ = resource.id url = "{0}/{1}/{2}s".format(self.siteurl, schedule_id, type_) add_req = req_factory(id_, task_type=task_type) response = self.put_request(url, add_req) - if response.status_code < 200 or response.status_code >= 300: - return AddResponse(result=False, - error="Status {}: {}".format(response.status_code, response.reason)) - logger.info("Added {} to {} to schedule {}".format(type_, id_, schedule_id)) - return OK + + error, warnings, task_created = ScheduleItem.parse_add_to_schedule_response( + response, self.parent_srv.namespace) + if task_created: + logger.info("Added {} to {} to schedule {}".format(type_, id_, schedule_id)) + + if error is not None or warnings is not None: + return AddResponse(result=False, error=error, warnings=warnings, task_created=task_created) + else: + return OK items = [] diff --git a/test/assets/schedule_add_datasource.xml b/test/assets/schedule_add_datasource.xml new file mode 100644 index 000000000..e57d2c8d2 --- /dev/null +++ b/test/assets/schedule_add_datasource.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/assets/schedule_add_workbook.xml b/test/assets/schedule_add_workbook.xml new file mode 100644 index 000000000..a6adb005e --- /dev/null +++ b/test/assets/schedule_add_workbook.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/assets/schedule_add_workbook_with_warnings.xml b/test/assets/schedule_add_workbook_with_warnings.xml new file mode 100644 index 000000000..0c376d018 --- /dev/null +++ b/test/assets/schedule_add_workbook_with_warnings.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/test/test_schedule.py b/test/test_schedule.py index 310a2b84a..b7b047d02 100644 --- a/test/test_schedule.py +++ b/test/test_schedule.py @@ -14,6 +14,9 @@ CREATE_WEEKLY_XML = os.path.join(TEST_ASSET_DIR, "schedule_create_weekly.xml") CREATE_MONTHLY_XML = os.path.join(TEST_ASSET_DIR, "schedule_create_monthly.xml") UPDATE_XML = os.path.join(TEST_ASSET_DIR, "schedule_update.xml") +ADD_WORKBOOK_TO_SCHEDULE = os.path.join(TEST_ASSET_DIR, "schedule_add_workbook.xml") +ADD_WORKBOOK_TO_SCHEDULE_WITH_WARNINGS = os.path.join(TEST_ASSET_DIR, "schedule_add_workbook_with_warnings.xml") +ADD_DATASOURCE_TO_SCHEDULE = os.path.join(TEST_ASSET_DIR, "schedule_add_datasource.xml") WORKBOOK_GET_BY_ID_XML = os.path.join(TEST_ASSET_DIR, 'workbook_get_by_id.xml') DATASOURCE_GET_BY_ID_XML = os.path.join(TEST_ASSET_DIR, 'datasource_get_by_id.xml') @@ -208,24 +211,42 @@ def test_add_workbook(self): with open(WORKBOOK_GET_BY_ID_XML, "rb") as f: workbook_response = f.read().decode("utf-8") + with open(ADD_WORKBOOK_TO_SCHEDULE, "rb") as f: + add_workbook_response = f.read().decode("utf-8") with requests_mock.mock() as m: - # TODO: Replace with real response m.get(self.server.workbooks.baseurl + '/bar', text=workbook_response) - m.put(baseurl + '/foo/workbooks', text="OK") + m.put(baseurl + '/foo/workbooks', text=add_workbook_response) workbook = self.server.workbooks.get_by_id("bar") result = self.server.schedules.add_to_schedule('foo', workbook=workbook) self.assertEqual(0, len(result), "Added properly") + def test_add_workbook_with_warnings(self): + self.server.version = "2.8" + baseurl = "{}/sites/{}/schedules".format(self.server.baseurl, self.server.site_id) + + with open(WORKBOOK_GET_BY_ID_XML, "rb") as f: + workbook_response = f.read().decode("utf-8") + with open(ADD_WORKBOOK_TO_SCHEDULE_WITH_WARNINGS, "rb") as f: + add_workbook_response = f.read().decode("utf-8") + with requests_mock.mock() as m: + m.get(self.server.workbooks.baseurl + '/bar', text=workbook_response) + m.put(baseurl + '/foo/workbooks', text=add_workbook_response) + workbook = self.server.workbooks.get_by_id("bar") + result = self.server.schedules.add_to_schedule('foo', workbook=workbook) + self.assertEqual(1, len(result), "Not added properly") + self.assertEqual(2, len(result[0].warnings)) + def test_add_datasource(self): self.server.version = "2.8" baseurl = "{}/sites/{}/schedules".format(self.server.baseurl, self.server.site_id) with open(DATASOURCE_GET_BY_ID_XML, "rb") as f: datasource_response = f.read().decode("utf-8") + with open(ADD_DATASOURCE_TO_SCHEDULE, "rb") as f: + add_datasource_response = f.read().decode("utf-8") with requests_mock.mock() as m: - # TODO: Replace with real response m.get(self.server.datasources.baseurl + '/bar', text=datasource_response) - m.put(baseurl + '/foo/datasources', text="OK") + m.put(baseurl + '/foo/datasources', text=add_datasource_response) datasource = self.server.datasources.get_by_id("bar") result = self.server.schedules.add_to_schedule('foo', datasource=datasource) self.assertEqual(0, len(result), "Added properly")