mirror of
https://github.com/ZwareBear/awx.git
synced 2026-03-20 07:43:35 -05:00
Merge pull request #12340 from john-westcott-iv/shedule_timezone_12255
Add documentation around schedule timezone change
This commit is contained in:
@@ -4684,8 +4684,14 @@ class SchedulePreviewSerializer(BaseSerializer):
|
|||||||
class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSerializer):
|
class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSerializer):
|
||||||
show_capabilities = ['edit', 'delete']
|
show_capabilities = ['edit', 'delete']
|
||||||
|
|
||||||
timezone = serializers.SerializerMethodField()
|
timezone = serializers.SerializerMethodField(
|
||||||
until = 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, the link will be reflected in this field.'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
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'),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Schedule
|
model = Schedule
|
||||||
|
|||||||
@@ -578,8 +578,7 @@ class ScheduleZoneInfo(APIView):
|
|||||||
swagger_topic = 'System Configuration'
|
swagger_topic = 'System Configuration'
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
zones = [{'name': zone} for zone in models.Schedule.get_zoneinfo()]
|
return Response({'zones': models.Schedule.get_zoneinfo(), 'links': models.Schedule.get_zoneinfo_links()})
|
||||||
return Response(zones)
|
|
||||||
|
|
||||||
|
|
||||||
class LaunchConfigCredentialsBase(SubListAttachDetachAPIView):
|
class LaunchConfigCredentialsBase(SubListAttachDetachAPIView):
|
||||||
|
|||||||
@@ -85,9 +85,18 @@ 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."))
|
next_run = models.DateTimeField(null=True, default=None, editable=False, help_text=_("The next time that the scheduled action will run."))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_zoneinfo(self):
|
def get_zoneinfo(cls):
|
||||||
return sorted(get_zonefile_instance().zones)
|
return sorted(get_zonefile_instance().zones)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_zoneinfo_links(cls):
|
||||||
|
return_val = {}
|
||||||
|
zone_instance = get_zonefile_instance()
|
||||||
|
for zone_name in zone_instance.zones:
|
||||||
|
if str(zone_name) != str(zone_instance.zones[zone_name]._filename):
|
||||||
|
return_val[zone_name] = zone_instance.zones[zone_name]._filename
|
||||||
|
return return_val
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def timezone(self):
|
def timezone(self):
|
||||||
utc = tzutc()
|
utc = tzutc()
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ def test_complex_schedule(post, admin_user, rrule, expected_result):
|
|||||||
def test_zoneinfo(get, admin_user):
|
def test_zoneinfo(get, admin_user):
|
||||||
url = reverse('api:schedule_zoneinfo')
|
url = reverse('api:schedule_zoneinfo')
|
||||||
r = get(url, admin_user, expect=200)
|
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
|
@pytest.mark.django_db
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ const generateRunOnTheDay = (days = []) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) {
|
function ScheduleFormFields({ hasDaysToKeepField, zoneOptions, zoneLinks }) {
|
||||||
const [timezone, timezoneMeta] = useField({
|
const [timezone, timezoneMeta] = useField({
|
||||||
name: 'timezone',
|
name: 'timezone',
|
||||||
validate: required(t`Select a value for this field`),
|
validate: required(t`Select a value for this field`),
|
||||||
@@ -100,6 +100,24 @@ function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) {
|
|||||||
});
|
});
|
||||||
const [{ name: dateFieldName }] = useField('startDate');
|
const [{ name: dateFieldName }] = useField('startDate');
|
||||||
const [{ name: timeFieldName }] = useField('startTime');
|
const [{ name: timeFieldName }] = useField('startTime');
|
||||||
|
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(event, selectedValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
let timezoneValidatedStatus = 'default';
|
||||||
|
if (timezoneMeta.touched && timezoneMeta.error) {
|
||||||
|
timezoneValidatedStatus = 'error';
|
||||||
|
} else if (timezoneMessage) {
|
||||||
|
timezoneValidatedStatus = 'warning';
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -124,17 +142,17 @@ function ScheduleFormFields({ hasDaysToKeepField, zoneOptions }) {
|
|||||||
<FormGroup
|
<FormGroup
|
||||||
name="timezone"
|
name="timezone"
|
||||||
fieldId="schedule-timezone"
|
fieldId="schedule-timezone"
|
||||||
helperTextInvalid={timezoneMeta.error}
|
helperTextInvalid={timezoneMeta.error || timezoneMessage}
|
||||||
isRequired
|
isRequired
|
||||||
validated={
|
validated={timezoneValidatedStatus}
|
||||||
!timezoneMeta.touched || !timezoneMeta.error ? 'default' : 'error'
|
|
||||||
}
|
|
||||||
label={t`Local time zone`}
|
label={t`Local time zone`}
|
||||||
|
helperText={timezoneMessage}
|
||||||
>
|
>
|
||||||
<AnsibleSelect
|
<AnsibleSelect
|
||||||
id="schedule-timezone"
|
id="schedule-timezone"
|
||||||
data={zoneOptions}
|
data={zoneOptions}
|
||||||
{...timezone}
|
{...timezone}
|
||||||
|
onChange={warnLinkedTZ}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
@@ -212,7 +230,7 @@ function ScheduleForm({
|
|||||||
request: loadScheduleData,
|
request: loadScheduleData,
|
||||||
error: contentError,
|
error: contentError,
|
||||||
isLoading: contentLoading,
|
isLoading: contentLoading,
|
||||||
result: { zoneOptions, credentials },
|
result: { zoneOptions, zoneLinks, credentials },
|
||||||
} = useRequest(
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
const { data } = await SchedulesAPI.readZoneInfo();
|
const { data } = await SchedulesAPI.readZoneInfo();
|
||||||
@@ -225,19 +243,21 @@ function ScheduleForm({
|
|||||||
creds = results;
|
creds = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zones = data.map((zone) => ({
|
const zones = (data.zones || []).map((zone) => ({
|
||||||
value: zone.name,
|
value: zone,
|
||||||
key: zone.name,
|
key: zone,
|
||||||
label: zone.name,
|
label: zone,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
zoneOptions: zones,
|
zoneOptions: zones,
|
||||||
|
zoneLinks: data.links,
|
||||||
credentials: creds || [],
|
credentials: creds || [],
|
||||||
};
|
};
|
||||||
}, [schedule]),
|
}, [schedule]),
|
||||||
{
|
{
|
||||||
zonesOptions: [],
|
zonesOptions: [],
|
||||||
|
zoneLinks: {},
|
||||||
credentials: [],
|
credentials: [],
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
}
|
}
|
||||||
@@ -630,6 +650,7 @@ function ScheduleForm({
|
|||||||
<ScheduleFormFields
|
<ScheduleFormFields
|
||||||
hasDaysToKeepField={hasDaysToKeepField}
|
hasDaysToKeepField={hasDaysToKeepField}
|
||||||
zoneOptions={zoneOptions}
|
zoneOptions={zoneOptions}
|
||||||
|
zoneLinks={zoneLinks}
|
||||||
/>
|
/>
|
||||||
{isWizardOpen && (
|
{isWizardOpen && (
|
||||||
<SchedulePromptableFields
|
<SchedulePromptableFields
|
||||||
|
|||||||
Reference in New Issue
Block a user