mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 03:10:42 -03:30
Merge branch 'release_3.3.0' into prompt-cleanup-v2
This commit is contained in:
commit
9b3bb2a9b3
@ -87,7 +87,11 @@ class InstanceManager(models.Manager):
|
||||
return node[0]
|
||||
raise RuntimeError("No instance found with the current cluster host id")
|
||||
|
||||
def register(self, uuid=settings.SYSTEM_UUID, hostname=settings.CLUSTER_HOST_ID):
|
||||
def register(self, uuid=None, hostname=None):
|
||||
if not uuid:
|
||||
uuid = settings.SYSTEM_UUID
|
||||
if not hostname:
|
||||
hostname = settings.CLUSTER_HOST_ID
|
||||
with advisory_lock('instance_registration_%s' % hostname):
|
||||
instance = self.filter(hostname=hostname)
|
||||
if instance.exists():
|
||||
|
||||
@ -55,7 +55,7 @@ from awx.main.queue import CallbackQueueDispatcher
|
||||
from awx.main.expect import run, isolated_manager
|
||||
from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, update_scm_url,
|
||||
check_proot_installed, build_proot_temp_dir, get_licenser,
|
||||
wrap_args_with_proot, OutputEventFilter, ignore_inventory_computed_fields,
|
||||
wrap_args_with_proot, OutputEventFilter, OutputVerboseFilter, ignore_inventory_computed_fields,
|
||||
ignore_inventory_group_removal, get_type_for_model, extract_ansible_vars)
|
||||
from awx.main.utils.reload import restart_local_services, stop_local_services
|
||||
from awx.main.utils.pglock import advisory_lock
|
||||
@ -821,19 +821,26 @@ class BaseTask(LogErrorsTask):
|
||||
|
||||
def get_stdout_handle(self, instance):
|
||||
'''
|
||||
Return an virtual file object for capturing stdout and events.
|
||||
Return an virtual file object for capturing stdout and/or events.
|
||||
'''
|
||||
dispatcher = CallbackQueueDispatcher()
|
||||
|
||||
def event_callback(event_data):
|
||||
event_data.setdefault(self.event_data_key, instance.id)
|
||||
if 'uuid' in event_data:
|
||||
cache_event = cache.get('ev-{}'.format(event_data['uuid']), None)
|
||||
if cache_event is not None:
|
||||
event_data.update(cache_event)
|
||||
dispatcher.dispatch(event_data)
|
||||
if isinstance(instance, (Job, AdHocCommand, ProjectUpdate)):
|
||||
def event_callback(event_data):
|
||||
event_data.setdefault(self.event_data_key, instance.id)
|
||||
if 'uuid' in event_data:
|
||||
cache_event = cache.get('ev-{}'.format(event_data['uuid']), None)
|
||||
if cache_event is not None:
|
||||
event_data.update(cache_event)
|
||||
dispatcher.dispatch(event_data)
|
||||
|
||||
return OutputEventFilter(event_callback)
|
||||
return OutputEventFilter(event_callback)
|
||||
else:
|
||||
def event_callback(event_data):
|
||||
event_data.setdefault(self.event_data_key, instance.id)
|
||||
dispatcher.dispatch(event_data)
|
||||
|
||||
return OutputVerboseFilter(event_callback)
|
||||
|
||||
def pre_run_hook(self, instance, **kwargs):
|
||||
'''
|
||||
|
||||
@ -5,7 +5,7 @@ from StringIO import StringIO
|
||||
|
||||
from six.moves import xrange
|
||||
|
||||
from awx.main.utils import OutputEventFilter
|
||||
from awx.main.utils import OutputEventFilter, OutputVerboseFilter
|
||||
|
||||
MAX_WIDTH = 78
|
||||
EXAMPLE_UUID = '890773f5-fe6d-4091-8faf-bdc8021d65dd'
|
||||
@ -145,3 +145,55 @@ def test_large_stdout_blob():
|
||||
f = OutputEventFilter(_callback)
|
||||
for x in range(1024 * 10):
|
||||
f.write('x' * 1024)
|
||||
|
||||
|
||||
def test_verbose_line_buffering():
|
||||
events = []
|
||||
|
||||
def _callback(event_data):
|
||||
events.append(event_data)
|
||||
|
||||
f = OutputVerboseFilter(_callback)
|
||||
f.write('one two\r\n\r\n')
|
||||
|
||||
assert len(events) == 2
|
||||
assert events[0]['start_line'] == 0
|
||||
assert events[0]['end_line'] == 1
|
||||
assert events[0]['stdout'] == 'one two'
|
||||
|
||||
assert events[1]['start_line'] == 1
|
||||
assert events[1]['end_line'] == 2
|
||||
assert events[1]['stdout'] == ''
|
||||
|
||||
f.write('three')
|
||||
assert len(events) == 2
|
||||
f.write('\r\nfou')
|
||||
|
||||
# three is not pushed to buffer until its line completes
|
||||
assert len(events) == 3
|
||||
assert events[2]['start_line'] == 2
|
||||
assert events[2]['end_line'] == 3
|
||||
assert events[2]['stdout'] == 'three'
|
||||
|
||||
f.write('r\r')
|
||||
f.write('\nfi')
|
||||
|
||||
assert events[3]['start_line'] == 3
|
||||
assert events[3]['end_line'] == 4
|
||||
assert events[3]['stdout'] == 'four'
|
||||
|
||||
f.write('ve')
|
||||
f.write('\r\n')
|
||||
|
||||
assert len(events) == 5
|
||||
assert events[4]['start_line'] == 4
|
||||
assert events[4]['end_line'] == 5
|
||||
assert events[4]['stdout'] == 'five'
|
||||
|
||||
f.close()
|
||||
|
||||
from pprint import pprint
|
||||
pprint(events)
|
||||
assert len(events) == 6
|
||||
|
||||
assert events[5]['event'] == 'EOF'
|
||||
|
||||
@ -48,7 +48,7 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore',
|
||||
'copy_m2m_relationships', 'prefetch_page_capabilities', 'to_python_boolean',
|
||||
'ignore_inventory_computed_fields', 'ignore_inventory_group_removal',
|
||||
'_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided',
|
||||
'get_current_apps', 'set_current_apps', 'OutputEventFilter',
|
||||
'get_current_apps', 'set_current_apps', 'OutputEventFilter', 'OutputVerboseFilter',
|
||||
'extract_ansible_vars', 'get_search_fields', 'get_system_task_capacity', 'get_cpu_capacity', 'get_mem_capacity',
|
||||
'wrap_args_with_proot', 'build_proot_temp_dir', 'check_proot_installed', 'model_to_dict',
|
||||
'model_instance_diff', 'timestamp_apiformat', 'parse_yaml_or_json', 'RequireDebugTrueOrTest',
|
||||
@ -1009,6 +1009,32 @@ class OutputEventFilter(object):
|
||||
self._current_event_data = None
|
||||
|
||||
|
||||
class OutputVerboseFilter(OutputEventFilter):
|
||||
'''
|
||||
File-like object that dispatches stdout data.
|
||||
Does not search for encoded job event data.
|
||||
Use for unified job types that do not encode job event data.
|
||||
'''
|
||||
def write(self, data):
|
||||
self._buffer.write(data)
|
||||
|
||||
# if the current chunk contains a line break
|
||||
if data and '\n' in data:
|
||||
# emit events for all complete lines we know about
|
||||
lines = self._buffer.getvalue().splitlines(True) # keep ends
|
||||
remainder = None
|
||||
# if last line is not a complete line, then exclude it
|
||||
if '\n' not in lines[-1]:
|
||||
remainder = lines.pop()
|
||||
# emit all complete lines
|
||||
for line in lines:
|
||||
self._emit_event(line)
|
||||
self._buffer = StringIO()
|
||||
# put final partial line back on buffer
|
||||
if remainder:
|
||||
self._buffer.write(remainder)
|
||||
|
||||
|
||||
def is_ansible_variable(key):
|
||||
return key.startswith('ansible_')
|
||||
|
||||
|
||||
@ -301,8 +301,8 @@ def _register_ldap(append=None):
|
||||
register(
|
||||
'AUTH_LDAP{}_GROUP_TYPE_PARAMS'.format(append_str),
|
||||
field_class=fields.LDAPGroupTypeParamsField,
|
||||
label=_('LDAP Group Type'),
|
||||
help_text=_('Parameters to send the chosen group type.'),
|
||||
label=_('LDAP Group Type Parameters'),
|
||||
help_text=_('Key value parameters to send the chosen group type init method.'),
|
||||
category=_('LDAP'),
|
||||
category_slug='ldap',
|
||||
default=collections.OrderedDict([
|
||||
|
||||
@ -113,7 +113,7 @@ export default ['i18n', function(i18n) {
|
||||
"class": 'btn-danger btn-xs',
|
||||
awToolTip: i18n._('Copy inventory'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: 'inventory.summary_fields.user_capabilities.edit'
|
||||
ngShow: '!inventory.pending_deletion && inventory.summary_fields.user_capabilities.edit'
|
||||
},
|
||||
view: {
|
||||
label: i18n._('View'),
|
||||
|
||||
@ -52,7 +52,8 @@ export default ['NotificationsList', 'i18n',
|
||||
dataTitle: i18n._('Ansible Environment'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||
ngShow: 'custom_virtualenvs_options.length > 0'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -52,17 +52,6 @@ export default ['i18n', 'NotificationsList', 'TemplateList',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg',
|
||||
awLookupWhen: '(project_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg'
|
||||
},
|
||||
custom_virtualenv: {
|
||||
label: i18n._('Ansible Environment'),
|
||||
type: 'select',
|
||||
defaultText: i18n._('Select Ansible Environment'),
|
||||
ngOptions: 'venv for venv in custom_virtualenvs_options track by venv',
|
||||
awPopOver: "<p>" + i18n._("Select the custom Python virtual environment for this project to run on.") + "</p>",
|
||||
dataTitle: i18n._('Ansible Environment'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
},
|
||||
scm_type: {
|
||||
label: i18n._('SCM Type'),
|
||||
type: 'select',
|
||||
@ -211,8 +200,21 @@ export default ['i18n', 'NotificationsList', 'TemplateList',
|
||||
dataTitle: i18n._('Cache Timeout'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
}
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||
subForm: 'sourceSubForm'
|
||||
},
|
||||
custom_virtualenv: {
|
||||
label: i18n._('Ansible Environment'),
|
||||
type: 'select',
|
||||
defaultText: i18n._('Select Ansible Environment'),
|
||||
ngOptions: 'venv for venv in custom_virtualenvs_options track by venv',
|
||||
awPopOver: "<p>" + i18n._("Select the custom Python virtual environment for this project to run on.") + "</p>",
|
||||
dataTitle: i18n._('Ansible Environment'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||
ngShow: 'custom_virtualenvs_options.length > 0'
|
||||
},
|
||||
},
|
||||
|
||||
buttons: {
|
||||
|
||||
@ -395,61 +395,66 @@ export default ['$compile', 'Attr', 'Icon',
|
||||
}
|
||||
if (field_action === 'pending_deletion') {
|
||||
innerTable += `<a ng-if='${list.iterator}.pending_deletion'>Pending Delete</a>`;
|
||||
}
|
||||
// Plug in Dropdown Component
|
||||
if (field_action === 'submit') {
|
||||
} else if (field_action === 'submit') {
|
||||
innerTable += `<at-launch-template template="${list.iterator}" ng-if="${list.iterator}.summary_fields.user_capabilities.start"></at-launch-template>`;
|
||||
} else {
|
||||
fAction = list.fieldActions[field_action];
|
||||
innerTable += "<button id=\"";
|
||||
innerTable += (fAction.id) ? fAction.id : field_action + "-action";
|
||||
innerTable += "\" ";
|
||||
innerTable += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
|
||||
innerTable += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : "";
|
||||
innerTable += "class=\"List-actionButton ";
|
||||
innerTable += (field_action === 'delete' || field_action === 'cancel') ? "List-actionButton--delete" : "";
|
||||
innerTable += "\" ";
|
||||
if(field_action === 'edit') {
|
||||
// editStateParams allows us to handle cases where a list might have different types of resources in it. As a result the edit
|
||||
// icon might now always point to the same state and differing states may have differing stateParams. Specifically this occurs
|
||||
// on the Templates list where editing a workflow job template takes you to a state where the param is workflow_job_template_id.
|
||||
// You can also edit a Job Template from this list so the stateParam there would be job_template_id.
|
||||
if(list.fieldActions[field_action].editStateParams) {
|
||||
let matchingConditions = handleEditStateParams(list.fieldActions[field_action].editStateParams);
|
||||
innerTable += `ng-class="{'List-editButton--selected' : ${matchingConditions.join(' || ')}}"`;
|
||||
}
|
||||
else if (list.iterator === 'inventory') {
|
||||
innerTable += `ng-class="{'List-editButton--selected': ($stateParams['${list.iterator}_id'] == ${list.iterator}.id) || ($stateParams['smartinventory_id'] == ${list.iterator}.id)}"`;
|
||||
}
|
||||
else if (list.iterator === 'host') {
|
||||
innerTable += `ng-class="{'List-editButton--selected': $stateParams['${list.iterator}_id'] == ${list.iterator}.id && $state.is('inventories.edit.hosts.edit') }"`;
|
||||
}
|
||||
else {
|
||||
innerTable += `ng-class="{'List-editButton--selected' : $stateParams['${list.iterator}_id'] == ${list.iterator}.id}"`;
|
||||
}
|
||||
}
|
||||
innerTable += (fAction.ngDisabled) ? "ng-disabled=\"" + fAction.ngDisabled + "\"" : "";
|
||||
innerTable += (fAction.awPopOver) ? "aw-pop-over=\"" + fAction.awPopOver + "\" " : "";
|
||||
innerTable += (fAction.dataPlacement) ? Attr(fAction, 'dataPlacement') : "";
|
||||
innerTable += (fAction.dataTitle) ? Attr(fAction, 'dataTitle') : "";
|
||||
for (itm in fAction) {
|
||||
if (itm !== 'ngHref' && itm !== 'href' && itm !== 'label' && itm !== 'icon' && itm !== 'class' &&
|
||||
itm !== 'iconClass' && itm !== "dataPlacement" && itm !== "awPopOver" &&
|
||||
itm !== "dataTitle") {
|
||||
innerTable += Attr(fAction, itm);
|
||||
}
|
||||
}
|
||||
innerTable += ">";
|
||||
if (fAction.iconClass) {
|
||||
innerTable += "<i class=\"" + fAction.iconClass + "\"></i>";
|
||||
// Plug in Dropdown Component
|
||||
if (field_action === 'submit' && list.fieldActions[field_action].relaunch === true) {
|
||||
innerTable += `<at-relaunch job="${list.iterator}"></at-relaunch>`;
|
||||
} else if (field_action === 'submit' && list.fieldActions[field_action].launch === true) {
|
||||
innerTable += `<at-launch-template template="${list.iterator}" ng-if="${list.iterator}.summary_fields.user_capabilities.start"></at-launch-template>`;
|
||||
} else {
|
||||
innerTable += SelectIcon({
|
||||
action: field_action
|
||||
});
|
||||
fAction = list.fieldActions[field_action];
|
||||
innerTable += "<button id=\"";
|
||||
innerTable += (fAction.id) ? fAction.id : field_action + "-action";
|
||||
innerTable += "\" ";
|
||||
innerTable += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
|
||||
innerTable += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : "";
|
||||
innerTable += "class=\"List-actionButton ";
|
||||
innerTable += (field_action === 'delete' || field_action === 'cancel') ? "List-actionButton--delete" : "";
|
||||
innerTable += "\" ";
|
||||
if(field_action === 'edit') {
|
||||
// editStateParams allows us to handle cases where a list might have different types of resources in it. As a result the edit
|
||||
// icon might now always point to the same state and differing states may have differing stateParams. Specifically this occurs
|
||||
// on the Templates list where editing a workflow job template takes you to a state where the param is workflow_job_template_id.
|
||||
// You can also edit a Job Template from this list so the stateParam there would be job_template_id.
|
||||
if(list.fieldActions[field_action].editStateParams) {
|
||||
let matchingConditions = handleEditStateParams(list.fieldActions[field_action].editStateParams);
|
||||
innerTable += `ng-class="{'List-editButton--selected' : ${matchingConditions.join(' || ')}}"`;
|
||||
}
|
||||
else if (list.iterator === 'inventory') {
|
||||
innerTable += `ng-class="{'List-editButton--selected': ($stateParams['${list.iterator}_id'] == ${list.iterator}.id) || ($stateParams['smartinventory_id'] == ${list.iterator}.id)}"`;
|
||||
}
|
||||
else if (list.iterator === 'host') {
|
||||
innerTable += `ng-class="{'List-editButton--selected': $stateParams['${list.iterator}_id'] == ${list.iterator}.id && $state.is('inventories.edit.hosts.edit') }"`;
|
||||
}
|
||||
else {
|
||||
innerTable += `ng-class="{'List-editButton--selected' : $stateParams['${list.iterator}_id'] == ${list.iterator}.id}"`;
|
||||
}
|
||||
}
|
||||
innerTable += (fAction.ngDisabled) ? "ng-disabled=\"" + fAction.ngDisabled + "\"" : "";
|
||||
innerTable += (fAction.awPopOver) ? "aw-pop-over=\"" + fAction.awPopOver + "\" " : "";
|
||||
innerTable += (fAction.dataPlacement) ? Attr(fAction, 'dataPlacement') : "";
|
||||
innerTable += (fAction.dataTitle) ? Attr(fAction, 'dataTitle') : "";
|
||||
for (itm in fAction) {
|
||||
if (itm !== 'ngHref' && itm !== 'href' && itm !== 'label' && itm !== 'icon' && itm !== 'class' &&
|
||||
itm !== 'iconClass' && itm !== "dataPlacement" && itm !== "awPopOver" &&
|
||||
itm !== "dataTitle") {
|
||||
innerTable += Attr(fAction, itm);
|
||||
}
|
||||
}
|
||||
innerTable += ">";
|
||||
if (fAction.iconClass) {
|
||||
innerTable += "<i class=\"" + fAction.iconClass + "\"></i>";
|
||||
} else {
|
||||
innerTable += SelectIcon({
|
||||
action: field_action
|
||||
});
|
||||
}
|
||||
//html += (fAction.label) ? "<span class=\"list-action-label\"> " + list.fieldActions[field_action].label +
|
||||
// "</span>" : "";
|
||||
innerTable += "</button>";
|
||||
}
|
||||
//html += (fAction.label) ? "<span class=\"list-action-label\"> " + list.fieldActions[field_action].label +
|
||||
// "</span>" : "";
|
||||
innerTable += "</button>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
.at-Upgrade--panel {
|
||||
align-items: center;
|
||||
background-color: @at-color-body-background-light;
|
||||
border-radius: 10px;
|
||||
color: @at-color-body-text;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: @at-font-size-jumbotron-text;
|
||||
height: ~"calc(100vh - 40px)";
|
||||
justify-content: center;
|
||||
margin-top: @at-space-10x;
|
||||
margin: @at-space-4x;
|
||||
padding: @at-space-10x;
|
||||
}
|
||||
}
|
||||
|
||||
.at-Upgrade--header {
|
||||
display: flex;
|
||||
|
||||
@ -240,7 +240,8 @@ function(NotificationsList, i18n) {
|
||||
dataTitle: i18n._('Ansible Environment'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||
ngShow: 'custom_virtualenvs_options.length > 0'
|
||||
},
|
||||
instance_groups: {
|
||||
label: i18n._('Instance Groups'),
|
||||
|
||||
@ -74,12 +74,13 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
let buildSendableNodeData = function() {
|
||||
// Create the node
|
||||
let sendableNodeData = {
|
||||
unified_job_template: params.node.unifiedJobTemplate.id
|
||||
unified_job_template: params.node.unifiedJobTemplate.id,
|
||||
credential: _.get(params, 'node.originalNodeObj.credential') || null
|
||||
};
|
||||
|
||||
if(_.has(params, 'node.promptData.extraVars')) {
|
||||
if(_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) {
|
||||
if(!sendableNodeData.extra_data) {
|
||||
if (_.has(params, 'node.promptData.extraVars')) {
|
||||
if (_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) {
|
||||
if (!sendableNodeData.extra_data) {
|
||||
sendableNodeData.extra_data = {};
|
||||
}
|
||||
|
||||
@ -87,15 +88,15 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
// Only include extra vars that differ from the template default vars
|
||||
_.forOwn(params.node.promptData.extraVars, (value, key) => {
|
||||
if(!defaultVars[key] || defaultVars[key] !== value) {
|
||||
if (!defaultVars[key] || defaultVars[key] !== value) {
|
||||
sendableNodeData.extra_data[key] = value;
|
||||
}
|
||||
});
|
||||
if(_.isEmpty(sendableNodeData.extra_data)) {
|
||||
if (_.isEmpty(sendableNodeData.extra_data)) {
|
||||
delete sendableNodeData.extra_data;
|
||||
}
|
||||
} else {
|
||||
if(_.has(params, 'node.promptData.extraVars') && !_.isEmpty(params.node.promptData.extraVars)) {
|
||||
if (_.has(params, 'node.promptData.extraVars') && !_.isEmpty(params.node.promptData.extraVars)) {
|
||||
sendableNodeData.extra_data = params.node.promptData.extraVars;
|
||||
}
|
||||
}
|
||||
@ -104,7 +105,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
// Check to see if the user has provided any prompt values that are different
|
||||
// from the defaults in the job template
|
||||
|
||||
if(params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) {
|
||||
if (params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) {
|
||||
sendableNodeData = PromptService.bundlePromptDataForSaving({
|
||||
promptData: params.node.promptData,
|
||||
dataToSave: sendableNodeData
|
||||
@ -117,26 +118,23 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
let continueRecursing = function(parentId) {
|
||||
$scope.totalIteratedNodes++;
|
||||
|
||||
if($scope.totalIteratedNodes === $scope.treeData.data.totalNodes) {
|
||||
if ($scope.totalIteratedNodes === $scope.treeData.data.totalNodes) {
|
||||
// We're done recursing, lets move on
|
||||
completionCallback();
|
||||
}
|
||||
else {
|
||||
if(params.node.children && params.node.children.length > 0) {
|
||||
} else {
|
||||
if (params.node.children && params.node.children.length > 0) {
|
||||
_.forEach(params.node.children, function(child) {
|
||||
if(child.edgeType === "success") {
|
||||
if (child.edgeType === "success") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
else if(child.edgeType === "failure") {
|
||||
} else if (child.edgeType === "failure") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
}, completionCallback);
|
||||
}
|
||||
else if(child.edgeType === "always") {
|
||||
} else if (child.edgeType === "always") {
|
||||
recursiveNodeUpdates({
|
||||
parentId: parentId,
|
||||
node: child
|
||||
@ -147,7 +145,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
}
|
||||
};
|
||||
|
||||
if(params.node.isNew) {
|
||||
if (params.node.isNew) {
|
||||
|
||||
TemplatesService.addWorkflowNode({
|
||||
url: $scope.treeData.workflow_job_template_obj.related.workflow_nodes,
|
||||
@ -155,7 +153,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
})
|
||||
.then(function(data) {
|
||||
|
||||
if(!params.node.isRoot) {
|
||||
if (!params.node.isRoot) {
|
||||
associateRequests.push({
|
||||
parentId: params.parentId,
|
||||
nodeId: data.data.id,
|
||||
@ -163,7 +161,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
});
|
||||
}
|
||||
|
||||
if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){
|
||||
if (_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){
|
||||
// This finds the credentials that were selected in the prompt but don't occur
|
||||
// in the template defaults
|
||||
let credentialsToPost = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) {
|
||||
@ -193,18 +191,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
error.status
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
if(params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) {
|
||||
} else {
|
||||
if (params.node.edited || !params.node.originalParentId || (params.node.originalParentId && params.parentId !== params.node.originalParentId)) {
|
||||
|
||||
if(params.node.edited) {
|
||||
if (params.node.edited) {
|
||||
|
||||
editRequests.push({
|
||||
id: params.node.nodeId,
|
||||
data: buildSendableNodeData()
|
||||
});
|
||||
|
||||
if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){
|
||||
if (_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){
|
||||
let credentialsNotInPriorCredentials = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) {
|
||||
let defaultCreds = params.node.promptData.launchConf.defaults.credentials ? params.node.promptData.launchConf.defaults.credentials : [];
|
||||
return !defaultCreds.some(function(defaultCred) {
|
||||
@ -243,20 +240,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep
|
||||
if ((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep
|
||||
|
||||
let parentIsDeleted = false;
|
||||
|
||||
_.forEach($scope.treeData.data.deletedNodes, function(deletedNode) {
|
||||
if(deletedNode === params.node.originalParentId) {
|
||||
if (deletedNode === params.node.originalParentId) {
|
||||
parentIsDeleted = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(!parentIsDeleted) {
|
||||
if (!parentIsDeleted) {
|
||||
disassociateRequests.push({
|
||||
parentId: params.node.originalParentId,
|
||||
nodeId: params.node.nodeId,
|
||||
@ -267,7 +263,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
// Can only associate if we have a parent.
|
||||
// If we don't have a parent then this is a root node
|
||||
// and the act of disassociating will make it a root node
|
||||
if(params.parentId) {
|
||||
if (params.parentId) {
|
||||
associateRequests.push({
|
||||
parentId: params.parentId,
|
||||
nodeId: params.node.nodeId,
|
||||
@ -275,8 +271,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else if(!params.node.originalParentId && params.parentId) {
|
||||
} else if (!params.node.originalParentId && params.parentId) {
|
||||
// This used to be a root node but is now not a root node
|
||||
associateRequests.push({
|
||||
parentId: params.parentId,
|
||||
@ -293,7 +288,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
let updateEdgeDropdownOptions = (optionsToInclude) => {
|
||||
// Not passing optionsToInclude will include all by default
|
||||
if(!optionsToInclude) {
|
||||
if (!optionsToInclude) {
|
||||
$scope.edgeTypeOptions = [
|
||||
{
|
||||
label: 'Always',
|
||||
@ -312,17 +307,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
$scope.edgeTypeOptions = [];
|
||||
|
||||
optionsToInclude.forEach((optionToInclude) => {
|
||||
if(optionToInclude === "always") {
|
||||
if (optionToInclude === "always") {
|
||||
$scope.edgeTypeOptions.push({
|
||||
label: 'Always',
|
||||
value: 'always'
|
||||
});
|
||||
} else if(optionToInclude === "success") {
|
||||
} else if (optionToInclude === "success") {
|
||||
$scope.edgeTypeOptions.push({
|
||||
label: 'On Success',
|
||||
value: 'success'
|
||||
});
|
||||
} else if(optionToInclude === "failure") {
|
||||
} else if (optionToInclude === "failure") {
|
||||
$scope.edgeTypeOptions.push({
|
||||
label: 'On Failure',
|
||||
value: 'failure'
|
||||
@ -346,9 +341,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
promptWatcher = $scope.$watchGroup(promptDataToWatch, function() {
|
||||
let missingPromptValue = false;
|
||||
if($scope.missingSurveyValue) {
|
||||
if ($scope.missingSurveyValue) {
|
||||
missingPromptValue = true;
|
||||
} else if(!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) {
|
||||
} else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) {
|
||||
missingPromptValue = true;
|
||||
}
|
||||
$scope.promptModalMissingReqFields = missingPromptValue;
|
||||
@ -365,7 +360,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
$scope.totalIteratedNodes = 0;
|
||||
|
||||
if($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) {
|
||||
if ($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) {
|
||||
let completionCallback = function() {
|
||||
|
||||
let disassociatePromises = disassociateRequests.map(function(request) {
|
||||
@ -376,13 +371,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
});
|
||||
});
|
||||
|
||||
let credentialPromises = credentialRequests.map(function(request) {
|
||||
return TemplatesService.postWorkflowNodeCredential({
|
||||
id: request.id,
|
||||
data: request.data
|
||||
});
|
||||
});
|
||||
|
||||
let editNodePromises = editRequests.map(function(request) {
|
||||
return TemplatesService.editWorkflowNode({
|
||||
id: request.id,
|
||||
@ -394,9 +382,16 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
|
||||
});
|
||||
|
||||
$q.all(disassociatePromises.concat(editNodePromises, deletePromises, credentialPromises))
|
||||
$q.all(disassociatePromises.concat(editNodePromises, deletePromises))
|
||||
.then(function() {
|
||||
|
||||
let credentialPromises = credentialRequests.map(function(request) {
|
||||
return TemplatesService.postWorkflowNodeCredential({
|
||||
id: request.id,
|
||||
data: request.data
|
||||
});
|
||||
});
|
||||
|
||||
let associatePromises = associateRequests.map(function(request) {
|
||||
return TemplatesService.associateWorkflowNode({
|
||||
parentId: request.parentId,
|
||||
@ -405,7 +400,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
});
|
||||
});
|
||||
|
||||
$q.all(associatePromises)
|
||||
$q.all(associatePromises.concat(credentialPromises))
|
||||
.then(function() {
|
||||
$scope.closeDialog();
|
||||
});
|
||||
@ -417,8 +412,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
node: child
|
||||
}, completionCallback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
|
||||
let deletePromises = $scope.treeData.data.deletedNodes.map(function(nodeId) {
|
||||
return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
|
||||
@ -522,11 +516,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
}
|
||||
}
|
||||
|
||||
if(promptWatcher) {
|
||||
if (promptWatcher) {
|
||||
promptWatcher();
|
||||
}
|
||||
|
||||
if(surveyQuestionWatcher) {
|
||||
if (surveyQuestionWatcher) {
|
||||
surveyQuestionWatcher();
|
||||
}
|
||||
|
||||
@ -549,11 +543,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
$scope.nodeBeingEdited.isActiveEdit = false;
|
||||
}
|
||||
|
||||
if(promptWatcher) {
|
||||
if (promptWatcher) {
|
||||
promptWatcher();
|
||||
}
|
||||
|
||||
if(surveyQuestionWatcher) {
|
||||
if (surveyQuestionWatcher) {
|
||||
surveyQuestionWatcher();
|
||||
}
|
||||
|
||||
@ -601,12 +595,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
let jobTemplate = new JobTemplate();
|
||||
|
||||
if(!_.isEmpty($scope.nodeBeingEdited.promptData)) {
|
||||
if (!_.isEmpty($scope.nodeBeingEdited.promptData)) {
|
||||
$scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData);
|
||||
} else if($scope.nodeBeingEdited.unifiedJobTemplate){
|
||||
} else if ($scope.nodeBeingEdited.unifiedJobTemplate){
|
||||
let promises = [jobTemplate.optionsLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id)];
|
||||
|
||||
if(_.has($scope, 'nodeBeingEdited.originalNodeObj.related.credentials')) {
|
||||
if (_.has($scope, 'nodeBeingEdited.originalNodeObj.related.credentials')) {
|
||||
Rest.setUrl($scope.nodeBeingEdited.originalNodeObj.related.credentials);
|
||||
promises.push(Rest.get());
|
||||
}
|
||||
@ -630,8 +624,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
const credentialHasScheduleOverride = (templateDefaultCred) => {
|
||||
let credentialHasOverride = false;
|
||||
workflowNodeCredentials.forEach((scheduleCred) => {
|
||||
if(templateDefaultCred.credential_type === scheduleCred.credential_type) {
|
||||
if(
|
||||
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)
|
||||
) {
|
||||
@ -643,9 +637,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
return credentialHasOverride;
|
||||
};
|
||||
|
||||
if(_.has(launchConf, 'defaults.credentials')) {
|
||||
if (_.has(launchConf, 'defaults.credentials')) {
|
||||
launchConf.defaults.credentials.forEach((defaultCred) => {
|
||||
if(!credentialHasScheduleOverride(defaultCred)) {
|
||||
if (!credentialHasScheduleOverride(defaultCred)) {
|
||||
defaultCredsWithoutOverrides.push(defaultCred);
|
||||
}
|
||||
});
|
||||
@ -653,7 +647,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
|
||||
|
||||
if(!launchConf.survey_enabled &&
|
||||
if (!launchConf.survey_enabled &&
|
||||
!launchConf.ask_inventory_on_launch &&
|
||||
!launchConf.ask_credential_on_launch &&
|
||||
!launchConf.ask_verbosity_on_launch &&
|
||||
@ -671,11 +665,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) {
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) {
|
||||
$scope.promptModalMissingReqFields = true;
|
||||
}
|
||||
|
||||
if(responses[1].data.survey_enabled) {
|
||||
if (responses[1].data.survey_enabled) {
|
||||
// go out and get the survey questions
|
||||
jobTemplate.getSurveyQuestions($scope.nodeBeingEdited.unifiedJobTemplate.id)
|
||||
.then((surveyQuestionRes) => {
|
||||
@ -700,7 +694,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
|
||||
let missingSurveyValue = false;
|
||||
_.each($scope.promptData.surveyQuestions, (question) => {
|
||||
if(question.required && (Empty(question.model) || question.model === [])) {
|
||||
if (question.required && (Empty(question.model) || question.model === [])) {
|
||||
missingSurveyValue = true;
|
||||
}
|
||||
});
|
||||
@ -709,8 +703,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
watchForPromptChanges();
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$scope.nodeBeingEdited.promptData = $scope.promptData = {
|
||||
launchConf: launchConf,
|
||||
launchOptions: launchOptions,
|
||||
@ -729,7 +722,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
$scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate;
|
||||
|
||||
if($scope.selectedTemplate.unified_job_type) {
|
||||
if ($scope.selectedTemplate.unified_job_type) {
|
||||
switch ($scope.selectedTemplate.unified_job_type) {
|
||||
case "job":
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
@ -741,8 +734,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
$scope.workflowMakerFormConfig.activeTab = "inventory_sync";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if($scope.selectedTemplate.type) {
|
||||
} else if ($scope.selectedTemplate.type) {
|
||||
switch ($scope.selectedTemplate.type) {
|
||||
case "job_template":
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
@ -767,19 +759,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
switch($scope.nodeBeingEdited.edgeType) {
|
||||
case "always":
|
||||
$scope.edgeType = {label: "Always", value: "always"};
|
||||
if(siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) {
|
||||
if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) {
|
||||
edgeDropdownOptions = ["always"];
|
||||
}
|
||||
break;
|
||||
case "success":
|
||||
$scope.edgeType = {label: "On Success", value: "success"};
|
||||
if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) {
|
||||
if (siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) {
|
||||
edgeDropdownOptions = ["success", "failure"];
|
||||
}
|
||||
break;
|
||||
case "failure":
|
||||
$scope.edgeType = {label: "On Failure", value: "failure"};
|
||||
if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) {
|
||||
if (siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) {
|
||||
edgeDropdownOptions = ["success", "failure"];
|
||||
}
|
||||
break;
|
||||
@ -857,13 +849,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
$scope.$broadcast("refreshWorkflowChart");
|
||||
|
||||
if($scope.placeholderNode) {
|
||||
if ($scope.placeholderNode) {
|
||||
let edgeType = {label: "On Success", value: "success"};
|
||||
if($scope.placeholderNode.isRoot) {
|
||||
if ($scope.placeholderNode.isRoot) {
|
||||
updateEdgeDropdownOptions(["always"]);
|
||||
edgeType = {label: "Always", value: "always"};
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// we need to update the possible edges based on any new siblings
|
||||
let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
|
||||
tree: $scope.treeData.data,
|
||||
@ -889,8 +880,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
}
|
||||
$scope.edgeType = edgeType;
|
||||
}
|
||||
else if($scope.nodeBeingEdited) {
|
||||
} else if ($scope.nodeBeingEdited) {
|
||||
let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
|
||||
tree: $scope.treeData.data,
|
||||
parentId: $scope.nodeBeingEdited.parent.id,
|
||||
@ -958,14 +948,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
$scope.selectedTemplate = angular.copy(selectedTemplate);
|
||||
|
||||
if(selectedTemplate.type === "job_template") {
|
||||
if (selectedTemplate.type === "job_template") {
|
||||
let jobTemplate = new JobTemplate();
|
||||
|
||||
$q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)])
|
||||
.then((responses) => {
|
||||
let launchConf = responses[1].data;
|
||||
|
||||
if(!launchConf.survey_enabled &&
|
||||
if (!launchConf.survey_enabled &&
|
||||
!launchConf.ask_inventory_on_launch &&
|
||||
!launchConf.ask_credential_on_launch &&
|
||||
!launchConf.ask_verbosity_on_launch &&
|
||||
@ -983,11 +973,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) {
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) {
|
||||
$scope.promptModalMissingReqFields = true;
|
||||
}
|
||||
|
||||
if(launchConf.survey_enabled) {
|
||||
if (launchConf.survey_enabled) {
|
||||
// go out and get the survey questions
|
||||
jobTemplate.getSurveyQuestions(selectedTemplate.id)
|
||||
.then((surveyQuestionRes) => {
|
||||
@ -1012,7 +1002,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
|
||||
let missingSurveyValue = false;
|
||||
_.each($scope.promptData.surveyQuestions, (question) => {
|
||||
if(question.required && (Empty(question.model) || question.model === [])) {
|
||||
if (question.required && (Empty(question.model) || question.model === [])) {
|
||||
missingSurveyValue = true;
|
||||
}
|
||||
});
|
||||
@ -1021,8 +1011,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
watchForPromptChanges();
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$scope.promptData = {
|
||||
launchConf: responses[1].data,
|
||||
launchOptions: responses[0].data,
|
||||
@ -1098,7 +1087,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with
|
||||
// this treeData object and removing the children object for some reason (?)
|
||||
// This happens on occasion and I think is a race condition (?)
|
||||
if(!$scope.treeData.data.children) {
|
||||
if (!$scope.treeData.data.children) {
|
||||
$scope.treeData.data.children = [];
|
||||
}
|
||||
|
||||
@ -1116,12 +1105,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
for(var i=0; i<data.data.results.length; i++) {
|
||||
allNodes.push(data.data.results[i]);
|
||||
}
|
||||
if(data.data.next) {
|
||||
if (data.data.next) {
|
||||
// Get the next page
|
||||
page++;
|
||||
getNodes();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// This is the last page
|
||||
buildTreeFromNodes();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user