diff --git a/tableauserverclient/models/interval_item.py b/tableauserverclient/models/interval_item.py index 3ee1fee08..444674e19 100644 --- a/tableauserverclient/models/interval_item.py +++ b/tableauserverclient/models/interval_item.py @@ -246,21 +246,34 @@ def interval(self): @interval.setter def interval(self, interval_values): - # This is weird because the value could be a str or an int - # The only valid str is 'LastDay' so we check that first. If that's not it - # try to convert it to an int, if that fails because it's an incorrect string - # like 'badstring' we catch and re-raise. Otherwise we convert to int and check - # that it's in range 1-31 + # Valid monthly intervals strings can contain any of the following + # day numbers (1-31) (integer or string) + # relative day within the month (First, Second, ... Last) + # week days (Sunday, Monday, ... LastDay) + VALID_INTERVALS = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "LastDay", + "First", + "Second", + "Third", + "Fourth", + "Fifth", + "Last", + ] + for value in range(1, 32): + VALID_INTERVALS.append(str(value)) + VALID_INTERVALS.append(value) + for interval_value in interval_values: - error = "Invalid interval value for a monthly frequency: {}.".format(interval_value) - - if interval_value != "LastDay": - try: - if not (1 <= int(interval_value) <= 31): - raise ValueError(error) - except ValueError: - if interval_value != "LastDay": - raise ValueError(error) + if interval_value not in VALID_INTERVALS: + error = f"Invalid monthly interval: {interval_value}" + raise ValueError(error) self._interval = interval_values diff --git a/tableauserverclient/server/endpoint/endpoint.py b/tableauserverclient/server/endpoint/endpoint.py index 0e55d5739..be0602df5 100644 --- a/tableauserverclient/server/endpoint/endpoint.py +++ b/tableauserverclient/server/endpoint/endpoint.py @@ -144,7 +144,9 @@ def _make_request( loggable_response = self.log_response_safely(server_response) logger.debug("Server response from {0}".format(url)) - # logger.debug("\n\t{1}".format(loggable_response)) + # uncomment the following to log full responses in debug mode + # BE CAREFUL WHEN SHARING THESE RESULTS - MAY CONTAIN YOUR SENSITIVE DATA + # logger.debug(loggable_response) if content_type == "application/xml": self.parent_srv._namespace.detect(server_response.content) diff --git a/test/assets/schedule_get_monthly_id_2.xml b/test/assets/schedule_get_monthly_id_2.xml new file mode 100644 index 000000000..ca84297e7 --- /dev/null +++ b/test/assets/schedule_get_monthly_id_2.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/test/test_schedule.py b/test/test_schedule.py index 3bbf5709b..0377295d7 100644 --- a/test/test_schedule.py +++ b/test/test_schedule.py @@ -14,6 +14,7 @@ GET_HOURLY_ID_XML = os.path.join(TEST_ASSET_DIR, "schedule_get_hourly_id.xml") GET_DAILY_ID_XML = os.path.join(TEST_ASSET_DIR, "schedule_get_daily_id.xml") GET_MONTHLY_ID_XML = os.path.join(TEST_ASSET_DIR, "schedule_get_monthly_id.xml") +GET_MONTHLY_ID_2_XML = os.path.join(TEST_ASSET_DIR, "schedule_get_monthly_id_2.xml") GET_EMPTY_XML = os.path.join(TEST_ASSET_DIR, "schedule_get_empty.xml") CREATE_HOURLY_XML = os.path.join(TEST_ASSET_DIR, "schedule_create_hourly.xml") CREATE_DAILY_XML = os.path.join(TEST_ASSET_DIR, "schedule_create_daily.xml") @@ -158,6 +159,21 @@ def test_get_monthly_by_id(self) -> None: self.assertEqual("Active", schedule.state) self.assertEqual(("1", "2"), schedule.interval_item.interval) + def test_get_monthly_by_id_2(self) -> None: + self.server.version = "3.15" + with open(GET_MONTHLY_ID_2_XML, "rb") as f: + response_xml = f.read().decode("utf-8") + with requests_mock.mock() as m: + schedule_id = "8c5caf33-6223-4724-83c3-ccdc1e730a07" + baseurl = "{}/schedules/{}".format(self.server.baseurl, schedule_id) + m.get(baseurl, text=response_xml) + schedule = self.server.schedules.get_by_id(schedule_id) + self.assertIsNotNone(schedule) + self.assertEqual(schedule_id, schedule.id) + self.assertEqual("Monthly First Monday!", schedule.name) + self.assertEqual("Active", schedule.state) + self.assertEqual(("Monday", "First"), schedule.interval_item.interval) + def test_delete(self) -> None: with requests_mock.mock() as m: m.delete(self.baseurl + "/c9cff7f9-309c-4361-99ff-d4ba8c9f5467", status_code=204)