mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 18:40:01 -03:30
Merge pull request #1667 from ryanpetrello/and-you-get-a-timezone
change timezone behavior slightly for Schedule.rrule to make things simpler for UI folks
This commit is contained in:
commit
6c0af2ef57
@ -4511,9 +4511,19 @@ class SchedulePreviewSerializer(BaseSerializer):
|
||||
class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSerializer):
|
||||
show_capabilities = ['edit', 'delete']
|
||||
|
||||
timezone = serializers.SerializerMethodField()
|
||||
until = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Schedule
|
||||
fields = ('*', 'unified_job_template', 'enabled', 'dtstart', 'dtend', 'rrule', 'next_run',)
|
||||
fields = ('*', 'unified_job_template', 'enabled', 'dtstart', 'dtend', 'rrule', 'next_run', 'timezone',
|
||||
'until')
|
||||
|
||||
def get_timezone(self, obj):
|
||||
return obj.timezone
|
||||
|
||||
def get_until(self, obj):
|
||||
return obj.until
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(ScheduleSerializer, self).get_related(obj)
|
||||
|
||||
@ -745,11 +745,11 @@ class ScheduleZoneInfo(APIView):
|
||||
swagger_topic = 'System Configuration'
|
||||
|
||||
def get(self, request):
|
||||
from dateutil.zoneinfo import get_zonefile_instance
|
||||
return Response([
|
||||
zones = [
|
||||
{'name': zone}
|
||||
for zone in sorted(get_zonefile_instance().zones)
|
||||
])
|
||||
for zone in Schedule.get_zoneinfo()
|
||||
]
|
||||
return Response(zones)
|
||||
|
||||
|
||||
class LaunchConfigCredentialsBase(SubListAttachDetachAPIView):
|
||||
|
||||
@ -1,15 +1,19 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
import logging
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
|
||||
import dateutil.rrule
|
||||
from dateutil.tz import datetime_exists
|
||||
import dateutil.parser
|
||||
from dateutil.tz import datetime_exists, tzutc
|
||||
from dateutil.zoneinfo import get_zonefile_instance
|
||||
|
||||
# Django
|
||||
from django.db import models
|
||||
from django.db.models.query import QuerySet
|
||||
from django.utils.timezone import now
|
||||
from django.utils.timezone import now, make_aware
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
# AWX
|
||||
@ -27,6 +31,9 @@ logger = logging.getLogger('awx.main.models.schedule')
|
||||
__all__ = ['Schedule']
|
||||
|
||||
|
||||
UTC_TIMEZONES = {x: tzutc() for x in dateutil.parser.parserinfo().UTCZONE}
|
||||
|
||||
|
||||
class ScheduleFilterMethods(object):
|
||||
|
||||
def enabled(self, enabled=True):
|
||||
@ -94,13 +101,98 @@ class Schedule(CommonModel, LaunchTimeConfig):
|
||||
help_text=_("The next time that the scheduled action will run.")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_zoneinfo(self):
|
||||
return sorted(get_zonefile_instance().zones)
|
||||
|
||||
@property
|
||||
def timezone(self):
|
||||
utc = tzutc()
|
||||
all_zones = Schedule.get_zoneinfo()
|
||||
all_zones.sort(key = lambda x: -len(x))
|
||||
for r in Schedule.rrulestr(self.rrule)._rrule:
|
||||
if r._dtstart:
|
||||
tzinfo = r._dtstart.tzinfo
|
||||
if tzinfo is utc:
|
||||
return 'UTC'
|
||||
fname = tzinfo._filename
|
||||
for zone in all_zones:
|
||||
if fname.endswith(zone):
|
||||
return zone
|
||||
logger.warn('Could not detect valid zoneinfo for {}'.format(self.rrule))
|
||||
return ''
|
||||
|
||||
@property
|
||||
def until(self):
|
||||
# The UNTIL= datestamp (if any) coerced from UTC to the local naive time
|
||||
# of the DTSTART
|
||||
for r in Schedule.rrulestr(self.rrule)._rrule:
|
||||
if r._until:
|
||||
local_until = r._until.astimezone(r._dtstart.tzinfo)
|
||||
naive_until = local_until.replace(tzinfo=None)
|
||||
return naive_until.isoformat()
|
||||
return ''
|
||||
|
||||
@classmethod
|
||||
def coerce_naive_until(cls, rrule):
|
||||
#
|
||||
# RFC5545 specifies that the UNTIL rule part MUST ALWAYS be a date
|
||||
# with UTC time. This is extra work for API implementers because
|
||||
# it requires them to perform DTSTART local -> UTC datetime coercion on
|
||||
# POST and UTC -> DTSTART local coercion on GET.
|
||||
#
|
||||
# This block of code is a departure from the RFC. If you send an
|
||||
# rrule like this to the API (without a Z on the UNTIL):
|
||||
#
|
||||
# DTSTART;TZID=America/New_York:20180502T150000 RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20180502T180000
|
||||
#
|
||||
# ...we'll assume that the naive UNTIL is intended to match the DTSTART
|
||||
# timezone (America/New_York), and so we'll coerce to UTC _for you_
|
||||
# automatically.
|
||||
#
|
||||
if 'until=' in rrule.lower():
|
||||
# if DTSTART;TZID= is used, coerce "naive" UNTIL values
|
||||
# to the proper UTC date
|
||||
match_until = re.match(".*?UNTIL\=(?P<until>[0-9]+T[0-9]+)(?P<utcflag>Z?)", rrule)
|
||||
if not len(match_until.group('utcflag')):
|
||||
# rrule = DTSTART;TZID=America/New_York:20200601T120000 RRULE:...;UNTIL=20200601T170000
|
||||
|
||||
# Find the UNTIL=N part of the string
|
||||
# naive_until = 20200601T170000
|
||||
naive_until = match_until.group('until')
|
||||
|
||||
# What is the DTSTART timezone for:
|
||||
# DTSTART;TZID=America/New_York:20200601T120000 RRULE:...;UNTIL=20200601T170000Z
|
||||
# local_tz = tzfile('/usr/share/zoneinfo/America/New_York')
|
||||
local_tz = dateutil.rrule.rrulestr(
|
||||
rrule.replace(naive_until, naive_until + 'Z'),
|
||||
tzinfos=UTC_TIMEZONES
|
||||
)._dtstart.tzinfo
|
||||
|
||||
# Make a datetime object with tzinfo=<the DTSTART timezone>
|
||||
# localized_until = datetime.datetime(2020, 6, 1, 17, 0, tzinfo=tzfile('/usr/share/zoneinfo/America/New_York'))
|
||||
localized_until = make_aware(
|
||||
datetime.datetime.strptime(naive_until, "%Y%m%dT%H%M%S"),
|
||||
local_tz
|
||||
)
|
||||
|
||||
# Coerce the datetime to UTC and format it as a string w/ Zulu format
|
||||
# utc_until = 20200601T220000Z
|
||||
utc_until = localized_until.astimezone(pytz.utc).strftime('%Y%m%dT%H%M%SZ')
|
||||
|
||||
# rrule was: DTSTART;TZID=America/New_York:20200601T120000 RRULE:...;UNTIL=20200601T170000
|
||||
# rrule is now: DTSTART;TZID=America/New_York:20200601T120000 RRULE:...;UNTIL=20200601T220000Z
|
||||
rrule = rrule.replace(naive_until, utc_until)
|
||||
return rrule
|
||||
|
||||
@classmethod
|
||||
def rrulestr(cls, rrule, **kwargs):
|
||||
"""
|
||||
Apply our own custom rrule parsing requirements
|
||||
"""
|
||||
rrule = Schedule.coerce_naive_until(rrule)
|
||||
kwargs['forceset'] = True
|
||||
x = dateutil.rrule.rrulestr(rrule, **kwargs)
|
||||
x = dateutil.rrule.rrulestr(rrule, tzinfos=UTC_TIMEZONES, **kwargs)
|
||||
|
||||
for r in x._rrule:
|
||||
if r._dtstart and r._dtstart.tzinfo is None:
|
||||
@ -158,4 +250,5 @@ class Schedule(CommonModel, LaunchTimeConfig):
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.update_computed_fields()
|
||||
self.rrule = Schedule.coerce_naive_until(self.rrule)
|
||||
super(Schedule, self).save(*args, **kwargs)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.utils.timezone import now
|
||||
import mock
|
||||
import pytest
|
||||
import pytz
|
||||
@ -131,31 +132,19 @@ def test_utc_until(job_template, until, dtend):
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize('dtstart, until', [
|
||||
['20180601T120000Z', '20180602T170000'],
|
||||
['TZID=America/New_York:20180601T120000', '20180602T170000'],
|
||||
['DTSTART:20380601T120000Z', '20380601T170000'], # noon UTC to 5PM UTC
|
||||
['DTSTART;TZID=America/New_York:20380601T120000', '20380601T170000'], # noon EST to 5PM EST
|
||||
])
|
||||
def test_tzinfo_naive_until(job_template, dtstart, until):
|
||||
rrule = 'DTSTART;{} RRULE:FREQ=DAILY;INTERVAL=1;UNTIL={}'.format(dtstart, until) # noqa
|
||||
rrule = '{} RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL={}'.format(dtstart, until) # noqa
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
s.save()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_until_must_be_utc(job_template):
|
||||
rrule = 'DTSTART;TZID=America/New_York:20180601T120000 RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20180602T000000' # noqa the Z is required
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
with pytest.raises(ValueError) as e:
|
||||
s.save()
|
||||
assert 'RRULE UNTIL values must be specified in UTC' in str(e)
|
||||
s.save()
|
||||
gen = Schedule.rrulestr(s.rrule).xafter(now(), count=20)
|
||||
assert len(list(gen)) == 6 # noon, 1PM, 2, 3, 4, 5PM
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@ -203,3 +192,85 @@ def test_beginning_of_time(job_template):
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
s.save()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize('rrule, tz', [
|
||||
['DTSTART:20300112T210000Z RRULE:FREQ=DAILY;INTERVAL=1', 'UTC'],
|
||||
['DTSTART;TZID=America/New_York:20300112T210000 RRULE:FREQ=DAILY;INTERVAL=1', 'America/New_York']
|
||||
])
|
||||
def test_timezone_property(job_template, rrule, tz):
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
assert s.timezone == tz
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_utc_until_property(job_template):
|
||||
rrule = 'DTSTART:20380601T120000Z RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T170000Z'
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
s.save()
|
||||
|
||||
assert s.rrule.endswith('20380601T170000Z')
|
||||
assert s.until == '2038-06-01T17:00:00'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_localized_until_property(job_template):
|
||||
rrule = 'DTSTART;TZID=America/New_York:20380601T120000 RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T220000Z'
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
s.save()
|
||||
|
||||
assert s.rrule.endswith('20380601T220000Z')
|
||||
assert s.until == '2038-06-01T17:00:00'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_utc_naive_coercion(job_template):
|
||||
rrule = 'DTSTART:20380601T120000Z RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T170000'
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
s.save()
|
||||
|
||||
assert s.rrule.endswith('20380601T170000Z')
|
||||
assert s.until == '2038-06-01T17:00:00'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_est_naive_coercion(job_template):
|
||||
rrule = 'DTSTART;TZID=America/New_York:20380601T120000 RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T170000'
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
s.save()
|
||||
|
||||
assert s.rrule.endswith('20380601T220000Z') # 5PM EDT = 10PM UTC
|
||||
assert s.until == '2038-06-01T17:00:00'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_empty_until_property(job_template):
|
||||
rrule = 'DTSTART;TZID=America/New_York:20380601T120000 RRULE:FREQ=HOURLY;INTERVAL=1'
|
||||
s = Schedule(
|
||||
name='Some Schedule',
|
||||
rrule=rrule,
|
||||
unified_job_template=job_template
|
||||
)
|
||||
s.save()
|
||||
assert s.until == ''
|
||||
|
||||
@ -10,6 +10,8 @@ import controller from '../../scheduler/schedulerList.controller';
|
||||
import addController from '../../scheduler/schedulerAdd.controller';
|
||||
import editController from '../../scheduler/schedulerEdit.controller';
|
||||
import { N_ } from '../../i18n';
|
||||
import editScheduleResolve from '../../scheduler/editSchedule.resolve';
|
||||
|
||||
|
||||
export default
|
||||
angular.module('managementJobScheduler', [])
|
||||
@ -99,6 +101,7 @@ angular.module('managementJobScheduler', [])
|
||||
templateUrl: templateUrl('management-jobs/scheduler/schedulerForm'),
|
||||
controller: 'managementJobEditController'
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: editScheduleResolve()
|
||||
});
|
||||
}]);
|
||||
|
||||
39
awx/ui/client/src/scheduler/editSchedule.resolve.js
Normal file
39
awx/ui/client/src/scheduler/editSchedule.resolve.js
Normal file
@ -0,0 +1,39 @@
|
||||
function editScheduleResolve () {
|
||||
const resolve = {
|
||||
scheduleResolve: ['Rest', '$stateParams', 'GetBasePath', 'ProcessErrors',
|
||||
(Rest, $stateParams, GetBasePath, ProcessErrors) => {
|
||||
var path = `${GetBasePath('schedules')}${parseInt($stateParams.schedule_id)}/`;
|
||||
// const path = GetBasePath('schedules') + parseInt($stateParams.schedule_id) + '/');
|
||||
Rest.setUrl(path);
|
||||
return Rest.get()
|
||||
.then(function(data) {
|
||||
return (data.data);
|
||||
}).catch(function(response) {
|
||||
ProcessErrors(null, response.data, response.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get schedule info. GET returned status: ' +
|
||||
response.status
|
||||
});
|
||||
});
|
||||
}
|
||||
],
|
||||
timezonesResolve: ['Rest', '$stateParams', 'GetBasePath', 'ProcessErrors',
|
||||
(Rest, $stateParams, GetBasePath, ProcessErrors) => {
|
||||
var path = `${GetBasePath('schedules')}zoneinfo`;
|
||||
Rest.setUrl(path);
|
||||
return Rest.get()
|
||||
.then(function(data) {
|
||||
return (data.data);
|
||||
}).catch(function(response) {
|
||||
ProcessErrors(null, response.data, response.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get zoneinfo. GET returned status: ' +
|
||||
response.status
|
||||
});
|
||||
});
|
||||
}
|
||||
]
|
||||
};
|
||||
return resolve;
|
||||
}
|
||||
export default editScheduleResolve;
|
||||
@ -1,5 +1,21 @@
|
||||
export default
|
||||
function RRuleToAPI() {
|
||||
|
||||
// This function removes the 'Z' from the UNTIL portion of the
|
||||
// rrule. The API will default to using the timezone that is
|
||||
// specified in the TZID as the locale for the UNTIL.
|
||||
function parseOutZ (rrule) {
|
||||
let until = rrule.split('UNTIL=');
|
||||
if(_.has(until, '1')){
|
||||
rrule = until[0];
|
||||
until = until[1].replace('Z', '');
|
||||
return `${rrule}UNTIL=${until}`;
|
||||
} else {
|
||||
return rrule;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return function(rrule, scope) {
|
||||
let localTime = scope.schedulerLocalTime;
|
||||
let timeZone = scope.schedulerTimeZone.name;
|
||||
@ -7,6 +23,8 @@ export default
|
||||
let response = rrule.replace(/(^.*(?=DTSTART))(DTSTART.*?)(=.*?;)(.*$)/, (str, p1, p2, p3, p4) => {
|
||||
return p2 + ';TZID=' + timeZone + ':' + localTime + ' ' + 'RRULE:' + p4;
|
||||
});
|
||||
|
||||
response = parseOutZ(response);
|
||||
return response;
|
||||
};
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import SchedulePost from './factories/schedule-post.factory';
|
||||
import ToggleSchedule from './factories/toggle-schedule.factory';
|
||||
import SchedulesList from './schedules.list';
|
||||
import ScheduledJobsList from './scheduled-jobs.list';
|
||||
import editScheduleResolve from './editSchedule.resolve';
|
||||
|
||||
export default
|
||||
angular.module('scheduler', [])
|
||||
@ -121,7 +122,8 @@ export default
|
||||
ncyBreadcrumb: {
|
||||
parent: 'jobTemplateSchedules',
|
||||
label: '{{schedule_obj.name}}'
|
||||
}
|
||||
},
|
||||
resolve: editScheduleResolve()
|
||||
});
|
||||
|
||||
// workflows
|
||||
@ -212,7 +214,8 @@ export default
|
||||
ncyBreadcrumb: {
|
||||
parent: 'workflowJobTemplateSchedules',
|
||||
label: '{{schedule_obj.name}}'
|
||||
}
|
||||
},
|
||||
resolve: editScheduleResolve()
|
||||
});
|
||||
// projects
|
||||
$stateExtender.addState({
|
||||
@ -301,7 +304,8 @@ export default
|
||||
controller: 'schedulerEditController',
|
||||
templateUrl: templateUrl("scheduler/schedulerForm"),
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: editScheduleResolve()
|
||||
});
|
||||
// upcoming scheduled jobs
|
||||
$stateExtender.addState({
|
||||
|
||||
@ -409,8 +409,20 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait',
|
||||
}
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element: '.MakeSelect2',
|
||||
multiple: false
|
||||
var callSelect2 = function() {
|
||||
CreateSelect2({
|
||||
element: '.MakeSelect2',
|
||||
multiple: false
|
||||
});
|
||||
$("#schedulerTimeZone").select2({
|
||||
width:'100%',
|
||||
containerCssClass: 'Form-dropDown',
|
||||
placeholder: 'SEARCH'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on("updateSchedulerSelects", function() {
|
||||
callSelect2();
|
||||
});
|
||||
callSelect2();
|
||||
}];
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
export default ['$filter', '$state', '$stateParams', 'Wait', '$scope', 'moment',
|
||||
'$rootScope', '$http', 'CreateSelect2', 'ParseTypeChange', 'ParentObject', 'ProcessErrors', 'Rest',
|
||||
'GetBasePath', 'SchedulerInit', 'SchedulePost', 'JobTemplateModel', '$q', 'Empty', 'PromptService', 'RRuleToAPI',
|
||||
'WorkflowJobTemplateModel', 'TemplatesStrings',
|
||||
'WorkflowJobTemplateModel', 'TemplatesStrings', 'scheduleResolve', 'timezonesResolve',
|
||||
function($filter, $state, $stateParams, Wait, $scope, moment,
|
||||
$rootScope, $http, CreateSelect2, ParseTypeChange, ParentObject, ProcessErrors, Rest,
|
||||
GetBasePath, SchedulerInit, SchedulePost, JobTemplate, $q, Empty, PromptService, RRuleToAPI,
|
||||
WorkflowJobTemplate, TemplatesStrings
|
||||
WorkflowJobTemplate, TemplatesStrings, scheduleResolve, timezonesResolve
|
||||
) {
|
||||
|
||||
let schedule, scheduler, scheduleCredentials = [];
|
||||
@ -87,6 +87,11 @@ function($filter, $state, $stateParams, Wait, $scope, moment,
|
||||
element: '.MakeSelect2',
|
||||
multiple: false
|
||||
});
|
||||
$("#schedulerTimeZone").select2({
|
||||
width:'100%',
|
||||
containerCssClass: 'Form-dropDown',
|
||||
placeholder: 'SEARCH'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on("updateSchedulerSelects", function() {
|
||||
@ -118,337 +123,351 @@ function($filter, $state, $stateParams, Wait, $scope, moment,
|
||||
|
||||
Wait('start');
|
||||
|
||||
// Get the existing record
|
||||
Rest.setUrl(GetBasePath('schedules') + parseInt($stateParams.schedule_id) + '/');
|
||||
Rest.get()
|
||||
.then(({data}) => {
|
||||
schedule = data;
|
||||
try {
|
||||
schedule.extra_data = JSON.parse(schedule.extra_data);
|
||||
} catch(e) {
|
||||
// do nothing
|
||||
}
|
||||
//sets the timezone dropdown to the timezone specified by the API
|
||||
function setTimezone () {
|
||||
$scope.schedulerTimeZone = _.find($scope.timeZones, function(x) {
|
||||
return x.name === scheduleResolve.timezone;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.extraVars = (data.extra_data === '' || _.isEmpty(data.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(data.extra_data);
|
||||
// sets the UNTIL portion of the schedule form after the angular-scheduler
|
||||
// sets it, but this function reads the 'until' key/value pair directly
|
||||
// from the schedule GET response.
|
||||
function setUntil (scheduler) {
|
||||
let { until } = scheduleResolve;
|
||||
if(until !== ''){
|
||||
const date = moment(until);
|
||||
const endDt = moment.parseZone(date).format("MM/DD/YYYY");
|
||||
const endHour = date.format('HH');
|
||||
const endMinute = date.format('mm');
|
||||
const endSecond = date.format('ss');
|
||||
scheduler.scope.schedulerEndDt = endDt;
|
||||
scheduler.scope.schedulerEndHour = endHour;
|
||||
scheduler.scope.schedulerEndMinute = endMinute;
|
||||
scheduler.scope.schedulerEndSecond = endSecond;
|
||||
}
|
||||
}
|
||||
|
||||
if (_.has(schedule, 'summary_fields.unified_job_template.unified_job_type') &&
|
||||
schedule.summary_fields.unified_job_template.unified_job_type === 'system_job'){
|
||||
$scope.cleanupJob = true;
|
||||
}
|
||||
function init() {
|
||||
schedule = scheduleResolve;
|
||||
|
||||
$scope.schedule_obj = data;
|
||||
try {
|
||||
schedule.extra_data = JSON.parse(schedule.extra_data);
|
||||
} catch(e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
$('#form-container').empty();
|
||||
scheduler = SchedulerInit({ scope: $scope, requireFutureStartTime: false });
|
||||
$scope.extraVars = (scheduleResolve.extra_data === '' || _.isEmpty(scheduleResolve.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(scheduleResolve.extra_data);
|
||||
|
||||
$http.get('/api/v2/schedules/zoneinfo/').then(({data}) => {
|
||||
scheduler.scope.timeZones = data;
|
||||
scheduler.scope.schedulerTimeZone = _.find(data, function(x) {
|
||||
let tz = $scope.schedule_obj.rrule.match(/TZID=\s*(.*?)\s*:/);
|
||||
if (_.has(tz, '1')) {
|
||||
return x.name === tz[1];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (_.has(schedule, 'summary_fields.unified_job_template.unified_job_type') &&
|
||||
schedule.summary_fields.unified_job_template.unified_job_type === 'system_job'){
|
||||
$scope.cleanupJob = true;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
scheduler.inject('form-container', false);
|
||||
scheduler.injectDetail('occurrences', false);
|
||||
$scope.schedule_obj = scheduleResolve;
|
||||
|
||||
if (!/DTSTART/.test(schedule.rrule)) {
|
||||
schedule.rrule += ";DTSTART=" + schedule.dtstart.replace(/\.\d+Z$/,'Z');
|
||||
}
|
||||
schedule.rrule = schedule.rrule.replace(/ RRULE:/,';');
|
||||
schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART=');
|
||||
$scope.$on("htmlDetailReady", function() {
|
||||
scheduler.setRRule(schedule.rrule);
|
||||
scheduler.setName(schedule.name);
|
||||
$scope.hideForm = false;
|
||||
$('#form-container').empty();
|
||||
scheduler = SchedulerInit({ scope: $scope, requireFutureStartTime: false });
|
||||
|
||||
$scope.$watchGroup(["schedulerName",
|
||||
"schedulerStartDt",
|
||||
"schedulerStartHour",
|
||||
"schedulerStartMinute",
|
||||
"schedulerStartSecond",
|
||||
"schedulerTimeZone",
|
||||
"schedulerFrequency",
|
||||
"schedulerInterval",
|
||||
"monthlyRepeatOption",
|
||||
"monthDay",
|
||||
"monthlyOccurrence",
|
||||
"monthlyWeekDay",
|
||||
"yearlyRepeatOption",
|
||||
"yearlyMonth",
|
||||
"yearlyMonthDay",
|
||||
"yearlyOccurrence",
|
||||
"yearlyWeekDay",
|
||||
"yearlyOtherMonth",
|
||||
"schedulerEnd",
|
||||
"schedulerOccurrenceCount",
|
||||
"schedulerEndDt"
|
||||
], function() {
|
||||
$rootScope.$broadcast("loadSchedulerDetailPane");
|
||||
}, true);
|
||||
scheduler.scope.timeZones = timezonesResolve;
|
||||
setTimezone();
|
||||
|
||||
$scope.$watch("weekDays", function() {
|
||||
$rootScope.$broadcast("loadSchedulerDetailPane");
|
||||
}, true);
|
||||
scheduler.inject('form-container', false);
|
||||
scheduler.injectDetail('occurrences', false);
|
||||
|
||||
$rootScope.$broadcast("loadSchedulerDetailPane");
|
||||
Wait('stop');
|
||||
});
|
||||
|
||||
$scope.showRRuleDetail = false;
|
||||
if (!/DTSTART/.test(schedule.rrule)) {
|
||||
schedule.rrule += ";DTSTART=" + schedule.dtstart.replace(/\.\d+Z$/,'Z');
|
||||
}
|
||||
schedule.rrule = schedule.rrule.replace(/ RRULE:/,';');
|
||||
schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART=');
|
||||
$scope.$on("htmlDetailReady", function() {
|
||||
scheduler.setRRule(schedule.rrule);
|
||||
scheduler.setName(schedule.name);
|
||||
setTimezone();
|
||||
setUntil(scheduler);
|
||||
$scope.hideForm = false;
|
||||
|
||||
if ($scope.cleanupJob){
|
||||
$scope.schedulerPurgeDays = Number(schedule.extra_data.days);
|
||||
}
|
||||
$scope.$watchGroup(["schedulerName",
|
||||
"schedulerStartDt",
|
||||
"schedulerStartHour",
|
||||
"schedulerStartMinute",
|
||||
"schedulerStartSecond",
|
||||
"schedulerTimeZone",
|
||||
"schedulerFrequency",
|
||||
"schedulerInterval",
|
||||
"monthlyRepeatOption",
|
||||
"monthDay",
|
||||
"monthlyOccurrence",
|
||||
"monthlyWeekDay",
|
||||
"yearlyRepeatOption",
|
||||
"yearlyMonth",
|
||||
"yearlyMonthDay",
|
||||
"yearlyOccurrence",
|
||||
"yearlyWeekDay",
|
||||
"yearlyOtherMonth",
|
||||
"schedulerEnd",
|
||||
"schedulerOccurrenceCount",
|
||||
"schedulerEndDt"
|
||||
], function() {
|
||||
$rootScope.$broadcast("loadSchedulerDetailPane");
|
||||
}, true);
|
||||
|
||||
if ($state.current.name === 'jobTemplateSchedules.edit'){
|
||||
$scope.$watch("weekDays", function() {
|
||||
$rootScope.$broadcast("loadSchedulerDetailPane");
|
||||
}, true);
|
||||
|
||||
let jobTemplate = new JobTemplate();
|
||||
|
||||
Rest.setUrl(data.related.credentials);
|
||||
|
||||
$q.all([jobTemplate.optionsLaunch(ParentObject.id), jobTemplate.getLaunch(ParentObject.id), Rest.get()])
|
||||
.then((responses) => {
|
||||
let launchOptions = responses[0].data,
|
||||
launchConf = responses[1].data;
|
||||
|
||||
scheduleCredentials = responses[2].data.results;
|
||||
|
||||
let watchForPromptChanges = () => {
|
||||
let promptValuesToWatch = [
|
||||
'promptData.prompts.inventory.value',
|
||||
'promptData.prompts.jobType.value',
|
||||
'promptData.prompts.verbosity.value',
|
||||
'missingSurveyValue'
|
||||
];
|
||||
|
||||
$scope.$watchGroup(promptValuesToWatch, function() {
|
||||
let missingPromptValue = false;
|
||||
if ($scope.missingSurveyValue) {
|
||||
missingPromptValue = true;
|
||||
} else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) {
|
||||
missingPromptValue = true;
|
||||
}
|
||||
$scope.promptModalMissingReqFields = missingPromptValue;
|
||||
});
|
||||
};
|
||||
|
||||
let prompts = PromptService.processPromptValues({
|
||||
launchConf: responses[1].data,
|
||||
launchOptions: responses[0].data,
|
||||
currentValues: data
|
||||
});
|
||||
|
||||
let defaultCredsWithoutOverrides = [];
|
||||
|
||||
const credentialHasScheduleOverride = (templateDefaultCred) => {
|
||||
let credentialHasOverride = false;
|
||||
scheduleCredentials.forEach((scheduleCred) => {
|
||||
if (templateDefaultCred.credential_type === scheduleCred.credential_type) {
|
||||
if (
|
||||
(!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
|
||||
(templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
|
||||
) {
|
||||
credentialHasOverride = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return credentialHasOverride;
|
||||
};
|
||||
|
||||
if (_.has(launchConf, 'defaults.credentials')) {
|
||||
launchConf.defaults.credentials.forEach((defaultCred) => {
|
||||
if (!credentialHasScheduleOverride(defaultCred)) {
|
||||
defaultCredsWithoutOverrides.push(defaultCred);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
prompts.credentials.value = defaultCredsWithoutOverrides.concat(scheduleCredentials);
|
||||
|
||||
if (!launchConf.ask_variables_on_launch) {
|
||||
$scope.noVars = true;
|
||||
}
|
||||
|
||||
if (!launchConf.survey_enabled &&
|
||||
!launchConf.ask_inventory_on_launch &&
|
||||
!launchConf.ask_credential_on_launch &&
|
||||
!launchConf.ask_verbosity_on_launch &&
|
||||
!launchConf.ask_job_type_on_launch &&
|
||||
!launchConf.ask_limit_on_launch &&
|
||||
!launchConf.ask_tags_on_launch &&
|
||||
!launchConf.ask_skip_tags_on_launch &&
|
||||
!launchConf.ask_diff_mode_on_launch &&
|
||||
!launchConf.survey_enabled &&
|
||||
!launchConf.credential_needed_to_start &&
|
||||
!launchConf.inventory_needed_to_start &&
|
||||
launchConf.passwords_needed_to_start.length === 0 &&
|
||||
launchConf.variables_needed_to_start.length === 0) {
|
||||
$scope.showPromptButton = false;
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
// Ignore the fact that variables might be promptable on launch
|
||||
// Promptable variables will happen in the schedule form
|
||||
launchConf.ignore_ask_variables = true;
|
||||
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has(data, 'summary_fields.inventory')) {
|
||||
$scope.promptModalMissingReqFields = true;
|
||||
}
|
||||
|
||||
if (responses[1].data.survey_enabled) {
|
||||
// go out and get the survey questions
|
||||
jobTemplate.getSurveyQuestions(ParentObject.id)
|
||||
.then((surveyQuestionRes) => {
|
||||
|
||||
let processed = PromptService.processSurveyQuestions({
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
extra_data: _.cloneDeep(data.extra_data)
|
||||
});
|
||||
|
||||
$scope.missingSurveyValue = processed.missingSurveyValue;
|
||||
|
||||
$scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
|
||||
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'extraVars',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'SchedulerForm-extraVars',
|
||||
readOnly: !$scope.schedule_obj.summary_fields.user_capabilities.edit
|
||||
});
|
||||
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
template: ParentObject.id
|
||||
};
|
||||
|
||||
$scope.$watch('promptData.surveyQuestions', () => {
|
||||
let missingSurveyValue = false;
|
||||
_.each($scope.promptData.surveyQuestions, (question) => {
|
||||
if (question.required && (Empty(question.model) || question.model === [])) {
|
||||
missingSurveyValue = true;
|
||||
}
|
||||
});
|
||||
$scope.missingSurveyValue = missingSurveyValue;
|
||||
}, true);
|
||||
|
||||
watchForPromptChanges();
|
||||
});
|
||||
} else {
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
template: ParentObject.id
|
||||
};
|
||||
watchForPromptChanges();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if ($state.current.name === 'workflowJobTemplateSchedules.edit') {
|
||||
let workflowJobTemplate = new WorkflowJobTemplate();
|
||||
|
||||
$q.all([workflowJobTemplate.optionsLaunch(ParentObject.id), workflowJobTemplate.getLaunch(ParentObject.id)])
|
||||
.then((responses) => {
|
||||
let launchOptions = responses[0].data,
|
||||
launchConf = responses[1].data;
|
||||
|
||||
let watchForPromptChanges = () => {
|
||||
$scope.$watch('missingSurveyValue', function() {
|
||||
$scope.promptModalMissingReqFields = $scope.missingSurveyValue ? true : false;
|
||||
});
|
||||
};
|
||||
|
||||
let prompts = PromptService.processPromptValues({
|
||||
launchConf: responses[1].data,
|
||||
launchOptions: responses[0].data,
|
||||
currentValues: data
|
||||
});
|
||||
|
||||
if(!launchConf.survey_enabled) {
|
||||
$scope.showPromptButton = false;
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if(responses[1].data.survey_enabled) {
|
||||
// go out and get the survey questions
|
||||
workflowJobTemplate.getSurveyQuestions(ParentObject.id)
|
||||
.then((surveyQuestionRes) => {
|
||||
|
||||
let processed = PromptService.processSurveyQuestions({
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
extra_data: _.cloneDeep(data.extra_data)
|
||||
});
|
||||
|
||||
$scope.missingSurveyValue = processed.missingSurveyValue;
|
||||
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
template: ParentObject.id
|
||||
};
|
||||
|
||||
$scope.$watch('promptData.surveyQuestions', () => {
|
||||
let missingSurveyValue = false;
|
||||
_.each($scope.promptData.surveyQuestions, (question) => {
|
||||
if(question.required && (Empty(question.model) || question.model === [])) {
|
||||
missingSurveyValue = true;
|
||||
}
|
||||
});
|
||||
$scope.missingSurveyValue = missingSurveyValue;
|
||||
}, true);
|
||||
|
||||
watchForPromptChanges();
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
template: ParentObject.id
|
||||
};
|
||||
watchForPromptChanges();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// extra_data field is not manifested in the UI when scheduling a Management Job
|
||||
if ($state.current.name !== 'managementJobsList.schedule.add' && $state.current.name !== 'managementJobsList.schedule.edit'){
|
||||
if ($state.current.name === 'projectSchedules.edit' ||
|
||||
$state.current.name === 'inventories.edit.inventory_sources.edit.schedules.edit' ||
|
||||
$state.current.name === 'workflowJobTemplateSchedules.add'
|
||||
){
|
||||
$scope.noVars = true;
|
||||
} else {
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'extraVars',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'SchedulerForm-extraVars',
|
||||
readOnly: !$scope.schedule_obj.summary_fields.user_capabilities.edit
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to retrieve schedule ' + parseInt($stateParams.schedule_id) + ' GET returned: ' + status });
|
||||
$rootScope.$broadcast("loadSchedulerDetailPane");
|
||||
Wait('stop');
|
||||
});
|
||||
|
||||
$scope.showRRuleDetail = false;
|
||||
scheduler.setRRule(schedule.rrule);
|
||||
scheduler.setName(schedule.name);
|
||||
scheduler.scope.timeZones = timezonesResolve;
|
||||
scheduler.scope.schedulerTimeZone = scheduleResolve.timezone;
|
||||
if ($scope.cleanupJob){
|
||||
$scope.schedulerPurgeDays = Number(schedule.extra_data.days);
|
||||
}
|
||||
|
||||
if ($state.current.name === 'jobTemplateSchedules.edit'){
|
||||
|
||||
let jobTemplate = new JobTemplate();
|
||||
|
||||
Rest.setUrl(scheduleResolve.related.credentials);
|
||||
|
||||
$q.all([jobTemplate.optionsLaunch(ParentObject.id), jobTemplate.getLaunch(ParentObject.id), Rest.get()])
|
||||
.then((responses) => {
|
||||
let launchOptions = responses[0].data,
|
||||
launchConf = responses[1].data;
|
||||
|
||||
scheduleCredentials = responses[2].data.results;
|
||||
|
||||
let watchForPromptChanges = () => {
|
||||
let promptValuesToWatch = [
|
||||
'promptData.prompts.inventory.value',
|
||||
'promptData.prompts.jobType.value',
|
||||
'promptData.prompts.verbosity.value',
|
||||
'missingSurveyValue'
|
||||
];
|
||||
|
||||
$scope.$watchGroup(promptValuesToWatch, function() {
|
||||
let missingPromptValue = false;
|
||||
if ($scope.missingSurveyValue) {
|
||||
missingPromptValue = true;
|
||||
} else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) {
|
||||
missingPromptValue = true;
|
||||
}
|
||||
$scope.promptModalMissingReqFields = missingPromptValue;
|
||||
});
|
||||
};
|
||||
|
||||
let prompts = PromptService.processPromptValues({
|
||||
launchConf: responses[1].data,
|
||||
launchOptions: responses[0].data,
|
||||
currentValues: scheduleResolve
|
||||
});
|
||||
|
||||
let defaultCredsWithoutOverrides = [];
|
||||
|
||||
const credentialHasScheduleOverride = (templateDefaultCred) => {
|
||||
let credentialHasOverride = false;
|
||||
scheduleCredentials.forEach((scheduleCred) => {
|
||||
if (templateDefaultCred.credential_type === scheduleCred.credential_type) {
|
||||
if (
|
||||
(!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
|
||||
(templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
|
||||
) {
|
||||
credentialHasOverride = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return credentialHasOverride;
|
||||
};
|
||||
|
||||
if (_.has(launchConf, 'defaults.credentials')) {
|
||||
launchConf.defaults.credentials.forEach((defaultCred) => {
|
||||
if (!credentialHasScheduleOverride(defaultCred)) {
|
||||
defaultCredsWithoutOverrides.push(defaultCred);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
prompts.credentials.value = defaultCredsWithoutOverrides.concat(scheduleCredentials);
|
||||
|
||||
if (!launchConf.ask_variables_on_launch) {
|
||||
$scope.noVars = true;
|
||||
}
|
||||
|
||||
if (!launchConf.survey_enabled &&
|
||||
!launchConf.ask_inventory_on_launch &&
|
||||
!launchConf.ask_credential_on_launch &&
|
||||
!launchConf.ask_verbosity_on_launch &&
|
||||
!launchConf.ask_job_type_on_launch &&
|
||||
!launchConf.ask_limit_on_launch &&
|
||||
!launchConf.ask_tags_on_launch &&
|
||||
!launchConf.ask_skip_tags_on_launch &&
|
||||
!launchConf.ask_diff_mode_on_launch &&
|
||||
!launchConf.survey_enabled &&
|
||||
!launchConf.credential_needed_to_start &&
|
||||
!launchConf.inventory_needed_to_start &&
|
||||
launchConf.passwords_needed_to_start.length === 0 &&
|
||||
launchConf.variables_needed_to_start.length === 0) {
|
||||
$scope.showPromptButton = false;
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
// Ignore the fact that variables might be promptable on launch
|
||||
// Promptable variables will happen in the schedule form
|
||||
launchConf.ignore_ask_variables = true;
|
||||
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has(scheduleResolve, 'summary_fields.inventory')) {
|
||||
$scope.promptModalMissingReqFields = true;
|
||||
}
|
||||
|
||||
if (responses[1].data.survey_enabled) {
|
||||
// go out and get the survey questions
|
||||
jobTemplate.getSurveyQuestions(ParentObject.id)
|
||||
.then((surveyQuestionRes) => {
|
||||
|
||||
let processed = PromptService.processSurveyQuestions({
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
extra_data: _.cloneDeep(scheduleResolve.extra_data)
|
||||
});
|
||||
|
||||
$scope.missingSurveyValue = processed.missingSurveyValue;
|
||||
|
||||
$scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
|
||||
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'extraVars',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'SchedulerForm-extraVars',
|
||||
readOnly: !$scope.schedule_obj.summary_fields.user_capabilities.edit
|
||||
});
|
||||
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
template: ParentObject.id
|
||||
};
|
||||
|
||||
$scope.$watch('promptData.surveyQuestions', () => {
|
||||
let missingSurveyValue = false;
|
||||
_.each($scope.promptData.surveyQuestions, (question) => {
|
||||
if (question.required && (Empty(question.model) || question.model === [])) {
|
||||
missingSurveyValue = true;
|
||||
}
|
||||
});
|
||||
$scope.missingSurveyValue = missingSurveyValue;
|
||||
}, true);
|
||||
|
||||
watchForPromptChanges();
|
||||
});
|
||||
} else {
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
template: ParentObject.id
|
||||
};
|
||||
watchForPromptChanges();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if ($state.current.name === 'workflowJobTemplateSchedules.edit') {
|
||||
let workflowJobTemplate = new WorkflowJobTemplate();
|
||||
|
||||
$q.all([workflowJobTemplate.optionsLaunch(ParentObject.id), workflowJobTemplate.getLaunch(ParentObject.id)])
|
||||
.then((responses) => {
|
||||
let launchOptions = responses[0].data,
|
||||
launchConf = responses[1].data;
|
||||
|
||||
let watchForPromptChanges = () => {
|
||||
$scope.$watch('missingSurveyValue', function() {
|
||||
$scope.promptModalMissingReqFields = $scope.missingSurveyValue ? true : false;
|
||||
});
|
||||
};
|
||||
|
||||
let prompts = PromptService.processPromptValues({
|
||||
launchConf: responses[1].data,
|
||||
launchOptions: responses[0].data,
|
||||
currentValues: scheduleResolve
|
||||
});
|
||||
|
||||
if(!launchConf.survey_enabled) {
|
||||
$scope.showPromptButton = false;
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if(responses[1].data.survey_enabled) {
|
||||
// go out and get the survey questions
|
||||
workflowJobTemplate.getSurveyQuestions(ParentObject.id)
|
||||
.then((surveyQuestionRes) => {
|
||||
|
||||
let processed = PromptService.processSurveyQuestions({
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
extra_data: _.cloneDeep(scheduleResolve.extra_data)
|
||||
});
|
||||
|
||||
$scope.missingSurveyValue = processed.missingSurveyValue;
|
||||
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
surveyQuestions: surveyQuestionRes.data.spec,
|
||||
template: ParentObject.id
|
||||
};
|
||||
|
||||
$scope.$watch('promptData.surveyQuestions', () => {
|
||||
let missingSurveyValue = false;
|
||||
_.each($scope.promptData.surveyQuestions, (question) => {
|
||||
if(question.required && (Empty(question.model) || question.model === [])) {
|
||||
missingSurveyValue = true;
|
||||
}
|
||||
});
|
||||
$scope.missingSurveyValue = missingSurveyValue;
|
||||
}, true);
|
||||
|
||||
watchForPromptChanges();
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
prompts: prompts,
|
||||
template: ParentObject.id
|
||||
};
|
||||
watchForPromptChanges();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// extra_data field is not manifested in the UI when scheduling a Management Job
|
||||
if ($state.current.name !== 'managementJobsList.schedule.add' && $state.current.name !== 'managementJobsList.schedule.edit'){
|
||||
if ($state.current.name === 'projectSchedules.edit' ||
|
||||
$state.current.name === 'inventories.edit.inventory_sources.edit.schedules.edit' ||
|
||||
$state.current.name === 'workflowJobTemplateSchedules.add'
|
||||
){
|
||||
$scope.noVars = true;
|
||||
} else {
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'extraVars',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'SchedulerForm-extraVars',
|
||||
readOnly: !$scope.schedule_obj.summary_fields.user_capabilities.edit
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
init();
|
||||
|
||||
callSelect2();
|
||||
}];
|
||||
|
||||
@ -122,7 +122,6 @@
|
||||
</label>
|
||||
<select
|
||||
ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd)"
|
||||
class="MakeSelect2"
|
||||
name="schedulerTimeZone"
|
||||
id="schedulerTimeZone"
|
||||
ng-model="schedulerTimeZone"
|
||||
@ -130,6 +129,7 @@
|
||||
required class="form-control input-sm"
|
||||
ng-change="scheduleTimeChange()" >
|
||||
</select>
|
||||
<option></option>
|
||||
</div>
|
||||
<div class="form-group SchedulerForm-formGroup">
|
||||
<label class="Form-inputLabel">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user