From c836fafb61066d54af6f9726b00a83e6ae8451af Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 31 May 2022 17:07:41 -0400 Subject: [PATCH 1/9] modifying schedules API to return a list of links --- awx/api/views/__init__.py | 4 ++-- awx/main/models/schedules.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index f864ab2d5e..fa858f7808 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -578,8 +578,8 @@ class ScheduleZoneInfo(APIView): swagger_topic = 'System Configuration' def get(self, request): - zones = [{'name': zone} for zone in models.Schedule.get_zoneinfo()] - return Response(zones) + zone_info = models.Schedule.get_zoneinfo_with_links() + return Response(zone_info) class LaunchConfigCredentialsBase(SubListAttachDetachAPIView): diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index 8f9caec131..f409b3ec03 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -88,6 +88,24 @@ class Schedule(PrimordialModel, LaunchTimeConfig): def get_zoneinfo(self): return sorted(get_zonefile_instance().zones) + @classmethod + def get_zoneinfo_with_links(self): + zone_instance = get_zonefile_instance() + return_val = {'zones': sorted(zone_instance.zones), 'links': {}} + for zone_name in return_val['zones']: + if str(zone_name) != str(zone_instance.zones[zone_name]._filename): + return_val['links'][zone_name] = zone_instance.zones[zone_name]._filename + return return_val + + @classmethod + def get_linked_timezone(self, timezone_name): + # Returns two values: + # A boolean True means its linked, false means its not a linked timezone + # The name of the link (or the same name if its not a link) + zone_instance = get_zonefile_instance() + file_name = zone_instance.zones[timezone_name]._filename + return file_name != timezone_name, file_name + @property def timezone(self): utc = tzutc() From b05ebe96233bb87f1b4f1bd2cb60cf956b729d11 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 31 May 2022 17:09:41 -0400 Subject: [PATCH 2/9] Starting UI change to warn if linked TZ is selected --- .../Schedule/shared/ScheduleForm.js | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/awx/ui/src/components/Schedule/shared/ScheduleForm.js b/awx/ui/src/components/Schedule/shared/ScheduleForm.js index 48da8e7664..ccccecc4b2 100644 --- a/awx/ui/src/components/Schedule/shared/ScheduleForm.js +++ b/awx/ui/src/components/Schedule/shared/ScheduleForm.js @@ -89,7 +89,7 @@ const generateRunOnTheDay = (days = []) => { return null; }; -function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) { +function ScheduleFormFields({ hasDaysToKeepField, zoneOptions, zoneLinks }) { const [timezone, timezoneMeta] = useField({ name: 'timezone', validate: required(t`Select a value for this field`), @@ -100,6 +100,15 @@ function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) { }); const [{ name: dateFieldName }] = useField('startDate'); const [{ name: timeFieldName }] = useField('startTime'); + const warnLinkedTZ = (event, selected_value) => { + console.log(selected_value) + console.log(event) + if(selected_value in zoneLinks) { + console.log("Warning: "+ selected_value +" is a link to "+ zoneLinks[selected_value] +" and will be saved as that.") + } + timezone.onChange(); + }; + return ( <> { const { data } = await SchedulesAPI.readZoneInfo(); @@ -225,19 +235,21 @@ function ScheduleForm({ creds = results; } - const zones = data.map((zone) => ({ - value: zone.name, - key: zone.name, - label: zone.name, + const zones = data.zones.map((zone) => ({ + value: zone, + key: zone, + label: zone, })); return { zoneOptions: zones, + zoneLinks: data.zones.links, credentials: creds || [], }; }, [schedule]), { zonesOptions: [], + zoneLinks: {}, credentials: [], isLoading: true, } From 0b63af8d4db71555d5587cb67270a901dc6459a9 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Tue, 31 May 2022 15:02:14 -0700 Subject: [PATCH 3/9] add schedules timezone link warning to UI --- .../Schedule/shared/ScheduleForm.js | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/awx/ui/src/components/Schedule/shared/ScheduleForm.js b/awx/ui/src/components/Schedule/shared/ScheduleForm.js index ccccecc4b2..8bd5dc5b3c 100644 --- a/awx/ui/src/components/Schedule/shared/ScheduleForm.js +++ b/awx/ui/src/components/Schedule/shared/ScheduleForm.js @@ -100,15 +100,24 @@ function ScheduleFormFields({ hasDaysToKeepField, zoneOptions, zoneLinks }) { }); const [{ name: dateFieldName }] = useField('startDate'); const [{ name: timeFieldName }] = useField('startTime'); - const warnLinkedTZ = (event, selected_value) => { - console.log(selected_value) - console.log(event) - if(selected_value in zoneLinks) { - console.log("Warning: "+ selected_value +" is a link to "+ zoneLinks[selected_value] +" and will be saved as that.") + const [timezoneMessage, setTimezoneMessage] = useState(''); + const warnLinkedTZ = (event, selectedValue) => { + if (zoneLinks[selectedValue]) { + setTimezoneMessage( + `Warning: ${selectedValue} is a link to ${zoneLinks[selectedValue]} and will be saved as that.` + ); + } else { + setTimezoneMessage(''); } - timezone.onChange(); + timezone.onChange(event, selectedValue); }; + let timezoneValidatedStatus = 'default'; + if (timezoneMeta.touched && timezoneMeta.error) { + timezoneValidatedStatus = 'error'; + } else if (timezoneMessage) { + timezoneValidatedStatus = 'warning'; + } return ( <> {isWizardOpen && ( Date: Tue, 7 Jun 2022 10:48:14 -0400 Subject: [PATCH 4/9] Removing unneeded function --- awx/main/models/schedules.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index f409b3ec03..8feee8f9d2 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -97,15 +97,6 @@ class Schedule(PrimordialModel, LaunchTimeConfig): return_val['links'][zone_name] = zone_instance.zones[zone_name]._filename return return_val - @classmethod - def get_linked_timezone(self, timezone_name): - # Returns two values: - # A boolean True means its linked, false means its not a linked timezone - # The name of the link (or the same name if its not a link) - zone_instance = get_zonefile_instance() - file_name = zone_instance.zones[timezone_name]._filename - return file_name != timezone_name, file_name - @property def timezone(self): utc = tzutc() From fe6d0ce9ccb8ba39ff60e90851e271e7a64b61c7 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 7 Jun 2022 10:50:21 -0400 Subject: [PATCH 5/9] Adding help text to until and timezone fields --- awx/api/serializers.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 8842f7b98d..b5085fe67b 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4681,8 +4681,16 @@ class SchedulePreviewSerializer(BaseSerializer): class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSerializer): show_capabilities = ['edit', 'delete'] - timezone = serializers.SerializerMethodField() - until = serializers.SerializerMethodField() + timezone = serializers.SerializerMethodField( + help_text=_( + 'The timezone this schedule runs in. This field is extracted from the RRULE. If the timezone in the RRULE is a link to another timezone this field will show the links name.' + ), + read_only=True, + ) + until = serializers.SerializerMethodField( + help_text=_('The date this schedule will end. This field is computed from the RRULE. If the schedule does not end an emptry string will be returned'), + read_only=True, + ) class Meta: model = Schedule From 48ebcd5918e8bc722334fec433394b3bd6d6b2cc Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 7 Jun 2022 16:09:16 -0400 Subject: [PATCH 6/9] Fixing assertion of schedule_zoneinfo --- awx/main/tests/functional/api/test_schedules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tests/functional/api/test_schedules.py b/awx/main/tests/functional/api/test_schedules.py index e38fd09647..9bd85b3c0e 100644 --- a/awx/main/tests/functional/api/test_schedules.py +++ b/awx/main/tests/functional/api/test_schedules.py @@ -500,7 +500,7 @@ def test_complex_schedule(post, admin_user, rrule, expected_result): def test_zoneinfo(get, admin_user): url = reverse('api:schedule_zoneinfo') r = get(url, admin_user, expect=200) - assert {'name': 'America/New_York'} in r.data + assert 'America/New_York' in r.data['zones'] @pytest.mark.django_db From 9abdafe1012e776f657916417ee03af38650fbd4 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 7 Jun 2022 16:10:37 -0400 Subject: [PATCH 7/9] Removing read_only as its the default setting --- awx/api/serializers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index b5085fe67b..9c36d30259 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4685,11 +4685,9 @@ class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSeria help_text=_( 'The timezone this schedule runs in. This field is extracted from the RRULE. If the timezone in the RRULE is a link to another timezone this field will show the links name.' ), - read_only=True, ) until = serializers.SerializerMethodField( help_text=_('The date this schedule will end. This field is computed from the RRULE. If the schedule does not end an emptry string will be returned'), - read_only=True, ) class Meta: From 1180634ba7568ab6c2d324515824dae9923717e9 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Wed, 8 Jun 2022 08:22:05 -0400 Subject: [PATCH 8/9] Fixing UI checks --- awx/ui/src/components/Schedule/shared/ScheduleForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/src/components/Schedule/shared/ScheduleForm.js b/awx/ui/src/components/Schedule/shared/ScheduleForm.js index 8bd5dc5b3c..86ad24f08f 100644 --- a/awx/ui/src/components/Schedule/shared/ScheduleForm.js +++ b/awx/ui/src/components/Schedule/shared/ScheduleForm.js @@ -243,7 +243,7 @@ function ScheduleForm({ creds = results; } - const zones = data.zones.map((zone) => ({ + const zones = (data.zones || []).map((zone) => ({ value: zone, key: zone, label: zone, From fddf292d47cf54ade6e75082a1bda8097c57b72d Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Fri, 10 Jun 2022 10:25:14 -0400 Subject: [PATCH 9/9] Additional changes from review --- awx/api/serializers.py | 2 +- awx/api/views/__init__.py | 3 +-- awx/main/models/schedules.py | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 9c36d30259..8bd722ce95 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4683,7 +4683,7 @@ class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSeria timezone = serializers.SerializerMethodField( help_text=_( - 'The timezone this schedule runs in. This field is extracted from the RRULE. If the timezone in the RRULE is a link to another timezone this field will show the links name.' + 'The timezone this schedule runs in. This field is extracted from the RRULE. If the timezone in the RRULE is a link to another timezone, the link will be reflected in this field.' ), ) until = serializers.SerializerMethodField( diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index fa858f7808..54dc24617b 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -578,8 +578,7 @@ class ScheduleZoneInfo(APIView): swagger_topic = 'System Configuration' def get(self, request): - zone_info = models.Schedule.get_zoneinfo_with_links() - return Response(zone_info) + return Response({'zones': models.Schedule.get_zoneinfo(), 'links': models.Schedule.get_zoneinfo_links()}) class LaunchConfigCredentialsBase(SubListAttachDetachAPIView): diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index 8feee8f9d2..29d43ec98d 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -85,16 +85,16 @@ class Schedule(PrimordialModel, LaunchTimeConfig): next_run = models.DateTimeField(null=True, default=None, editable=False, help_text=_("The next time that the scheduled action will run.")) @classmethod - def get_zoneinfo(self): + def get_zoneinfo(cls): return sorted(get_zonefile_instance().zones) @classmethod - def get_zoneinfo_with_links(self): + def get_zoneinfo_links(cls): + return_val = {} zone_instance = get_zonefile_instance() - return_val = {'zones': sorted(zone_instance.zones), 'links': {}} - for zone_name in return_val['zones']: + for zone_name in zone_instance.zones: if str(zone_name) != str(zone_instance.zones[zone_name]._filename): - return_val['links'][zone_name] = zone_instance.zones[zone_name]._filename + return_val[zone_name] = zone_instance.zones[zone_name]._filename return return_val @property