From 7f452ee8d10f209caebc648420ea1fffb08c8cfe Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 25 Apr 2019 15:21:47 -0400 Subject: [PATCH 01/15] Add more sort options to new lists --- .../applications/applications.strings.js | 7 ------- .../list-applications-users.controller.js | 11 ++++++---- .../list-applications.controller.js | 4 +++- awx/ui/client/features/jobs/jobs.strings.js | 7 ------- .../features/jobs/jobsList.controller.js | 6 ++++-- .../features/projects/projects.strings.js | 5 ----- .../projects/projectsList.controller.js | 9 ++++---- .../features/templates/templates.strings.js | 5 ----- .../templates/templatesList.controller.js | 6 +++++- .../features/users/tokens/tokens.strings.js | 5 ----- .../tokens/users-tokens-list.controller.js | 11 ++++++---- .../lib/services/base-string.service.js | 21 +++++++++++++++++++ .../instance-groups.strings.js | 5 ----- .../instances/instance-modal.controller.js | 11 ++++++---- .../instances/instances.controller.js | 11 ++++++---- .../list/instance-groups-list.controller.js | 9 ++++---- 16 files changed, 71 insertions(+), 62 deletions(-) diff --git a/awx/ui/client/features/applications/applications.strings.js b/awx/ui/client/features/applications/applications.strings.js index 4c907acbfc..f6fb73f56a 100644 --- a/awx/ui/client/features/applications/applications.strings.js +++ b/awx/ui/client/features/applications/applications.strings.js @@ -38,13 +38,6 @@ function ApplicationsStrings (BaseString) { ns.inputs = { ORGANIZATION_PLACEHOLDER: t.s('SELECT AN ORGANIZATION') }; - - ns.sort = { - NAME_ASCENDING: t.s('Name (Ascending)'), - NAME_DESCENDING: t.s('Name (Descending)'), - USERNAME_ASCENDING: t.s('Username (Ascending)'), - USERNAME_DESCENDING: t.s('Username (Descending)') - }; } ApplicationsStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/features/applications/list-applications-users.controller.js b/awx/ui/client/features/applications/list-applications-users.controller.js index 83b33b8251..bb7dc70edd 100644 --- a/awx/ui/client/features/applications/list-applications-users.controller.js +++ b/awx/ui/client/features/applications/list-applications-users.controller.js @@ -41,10 +41,13 @@ function ListApplicationsUsersController ( vm.toolbarSortOptions = [ toolbarSortDefault, - { - label: `${strings.get('sort.USERNAME_DESCENDING')}`, - value: '-user__username' - } + { label: `${strings.get('sort.USERNAME_DESCENDING')}`, value: '-user__username' }, + { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, + { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' }, + { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, + { label: `${strings.get('sort.EXPIRES_ASCENDING')}`, value: 'expires' }, + { label: `${strings.get('sort.EXPIRES_DESCENDING')}`, value: '-expires' } ]; function setToolbarSort () { diff --git a/awx/ui/client/features/applications/list-applications.controller.js b/awx/ui/client/features/applications/list-applications.controller.js index df824ac52f..254b31930b 100644 --- a/awx/ui/client/features/applications/list-applications.controller.js +++ b/awx/ui/client/features/applications/list-applications.controller.js @@ -54,7 +54,9 @@ function ListApplicationsController ( vm.toolbarSortOptions = [ toolbarSortDefault, - { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' } + { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' }, + { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, + { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' } ]; vm.toolbarSortValue = toolbarSortDefault; diff --git a/awx/ui/client/features/jobs/jobs.strings.js b/awx/ui/client/features/jobs/jobs.strings.js index 6bc6225760..a5b69df13f 100644 --- a/awx/ui/client/features/jobs/jobs.strings.js +++ b/awx/ui/client/features/jobs/jobs.strings.js @@ -4,13 +4,6 @@ function JobsStrings (BaseString) { const { t } = this; const ns = this.jobs; - ns.sort = { - NAME_ASCENDING: t.s('Name (Ascending)'), - NAME_DESCENDING: t.s('Name (Descending)'), - START_TIME: t.s('Start Time'), - FINISH_TIME: t.s('Finish Time') - }; - ns.list = { PANEL_TITLE: t.s('JOBS'), ROW_ITEM_LABEL_STARTED: t.s('Started'), diff --git a/awx/ui/client/features/jobs/jobsList.controller.js b/awx/ui/client/features/jobs/jobsList.controller.js index 48bedb049e..9b99bc397d 100644 --- a/awx/ui/client/features/jobs/jobsList.controller.js +++ b/awx/ui/client/features/jobs/jobsList.controller.js @@ -46,7 +46,7 @@ function ListJobsController ( }, true); const toolbarSortDefault = { - label: `${strings.get('sort.FINISH_TIME')}`, + label: `${strings.get('sort.FINISH_TIME_DESCENDING')}`, value: '-finished' }; @@ -63,7 +63,9 @@ function ListJobsController ( vm.toolbarSortOptions = [ { label: `${strings.get('sort.NAME_ASCENDING')}`, value: 'name' }, { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' }, - { label: `${strings.get('sort.START_TIME')}`, value: 'finished' }, + { label: `${strings.get('sort.FINISH_TIME_ASCENDING')}`, value: 'finished' }, + { label: `${strings.get('sort.START_TIME_ASCENDING')}`, value: 'started' }, + { label: `${strings.get('sort.START_TIME_DESCENDING')}`, value: '-started' }, toolbarSortDefault ]; diff --git a/awx/ui/client/features/projects/projects.strings.js b/awx/ui/client/features/projects/projects.strings.js index 7512f0f339..c09f3104d9 100644 --- a/awx/ui/client/features/projects/projects.strings.js +++ b/awx/ui/client/features/projects/projects.strings.js @@ -46,11 +46,6 @@ function ProjectsStrings (BaseString) { HEADER: this.error.HEADER, CALL: this.error.CALL, }; - - ns.sort = { - NAME_ASCENDING: t.s('Name (Ascending)'), - NAME_DESCENDING: t.s('Name (Descending)') - }; } ProjectsStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/features/projects/projectsList.controller.js b/awx/ui/client/features/projects/projectsList.controller.js index b3557d7d00..3d2e1369fa 100644 --- a/awx/ui/client/features/projects/projectsList.controller.js +++ b/awx/ui/client/features/projects/projectsList.controller.js @@ -68,10 +68,11 @@ function projectsListController ( vm.toolbarSortOptions = [ toolbarSortDefault, - { - label: `${strings.get('sort.NAME_DESCENDING')}`, - value: '-name' - } + { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' }, + { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, + { label: `${strings.get('sort.LAST_JOB_RUN_ASCENDING')}`, value: 'last_job_run' }, + { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' } ]; vm.toolbarSortValue = toolbarSortDefault; diff --git a/awx/ui/client/features/templates/templates.strings.js b/awx/ui/client/features/templates/templates.strings.js index b536f665b3..d294988d01 100644 --- a/awx/ui/client/features/templates/templates.strings.js +++ b/awx/ui/client/features/templates/templates.strings.js @@ -143,11 +143,6 @@ function TemplatesStrings (BaseString) { CANCEL: t.s('CANCEL'), SAVE_AND_EXIT: t.s('SAVE & EXIT') }; - - ns.sort = { - NAME_ASCENDING: t.s('Name (Ascending)'), - NAME_DESCENDING: t.s('Name (Descending)') - }; } TemplatesStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/features/templates/templatesList.controller.js b/awx/ui/client/features/templates/templatesList.controller.js index b072029752..84c2d512fb 100644 --- a/awx/ui/client/features/templates/templatesList.controller.js +++ b/awx/ui/client/features/templates/templatesList.controller.js @@ -71,7 +71,11 @@ function ListTemplatesController( vm.toolbarSortOptions = [ toolbarSortDefault, - { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' } + { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' }, + { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, + { label: `${strings.get('sort.LAST_JOB_RUN_ASCENDING')}`, value: 'last_job_run' }, + { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' } ]; vm.toolbarSortValue = toolbarSortDefault; diff --git a/awx/ui/client/features/users/tokens/tokens.strings.js b/awx/ui/client/features/users/tokens/tokens.strings.js index 13487e9ede..2f84642eae 100644 --- a/awx/ui/client/features/users/tokens/tokens.strings.js +++ b/awx/ui/client/features/users/tokens/tokens.strings.js @@ -41,11 +41,6 @@ function TokensStrings (BaseString) { PERSONAL_ACCESS_TOKEN: t.s('Personal Access Token'), HEADER: appName => t.s('{{ appName }} Token', { appName }), }; - - ns.sort = { - NAME_ASCENDING: t.s('Name (Ascending)'), - NAME_DESCENDING: t.s('Name (Descending)') - }; } TokensStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/features/users/tokens/users-tokens-list.controller.js b/awx/ui/client/features/users/tokens/users-tokens-list.controller.js index aedac69bcb..008f30fda7 100644 --- a/awx/ui/client/features/users/tokens/users-tokens-list.controller.js +++ b/awx/ui/client/features/users/tokens/users-tokens-list.controller.js @@ -50,10 +50,13 @@ function ListTokensController ( vm.toolbarSortOptions = [ toolbarSortDefault, - { - label: `${strings.get('sort.NAME_DESCENDING')}`, - value: '-application__name' - } + { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-application__name' }, + { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, + { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' }, + { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, + { label: `${strings.get('sort.EXPIRES_ASCENDING')}`, value: 'expires' }, + { label: `${strings.get('sort.EXPIRES_DESCENDING')}`, value: '-expires' } ]; function setToolbarSort () { diff --git a/awx/ui/client/lib/services/base-string.service.js b/awx/ui/client/lib/services/base-string.service.js index 6db4249b49..df725d43a4 100644 --- a/awx/ui/client/lib/services/base-string.service.js +++ b/awx/ui/client/lib/services/base-string.service.js @@ -103,6 +103,27 @@ function BaseStringService (namespace) { CANCEL: resourceType => t.s('Cancel the {{resourceType}}', { resourceType }) }; + this.sort = { + NAME_ASCENDING: t.s('Name (Ascending)'), + NAME_DESCENDING: t.s('Name (Descending)'), + CREATED_ASCENDING: t.s('Created (Ascending)'), + CREATED_DESCENDING: t.s('Created (Descending)'), + MODIFIED_ASCENDING: t.s('Modified (Ascending)'), + MODIFIED_DESCENDING: t.s('Modified (Descending)'), + EXPIRES_ASCENDING: t.s('Expires (Ascending)'), + EXPIRES_DESCENDING: t.s('Expires (Descending)'), + LAST_JOB_RUN_ASCENDING: t.s('Last Run (Ascending)'), + LAST_JOB_RUN_DESCENDING: t.s('Last Run (Descending)'), + USERNAME_ASCENDING: t.s('Username (Ascending)'), + USERNAME_DESCENDING: t.s('Username (Descending)'), + START_TIME_ASCENDING: t.s('Start Time (Ascending)'), + START_TIME_DESCENDING: t.s('Start Time (Descending)'), + FINISH_TIME_ASCENDING: t.s('Finish Time (Ascending)'), + FINISH_TIME_DESCENDING: t.s('Finish Time (Descending)'), + UUID_ASCENDING: t.s('UUID (Ascending)'), + UUID_DESCENDING: t.s('UUID (Descending)') + }; + this.ALERT = ({ header, body }) => t.s('{{ header }} {{ body }}', { header, body }); /** diff --git a/awx/ui/client/src/instance-groups/instance-groups.strings.js b/awx/ui/client/src/instance-groups/instance-groups.strings.js index ca93c23140..0becb671f7 100644 --- a/awx/ui/client/src/instance-groups/instance-groups.strings.js +++ b/awx/ui/client/src/instance-groups/instance-groups.strings.js @@ -62,11 +62,6 @@ function InstanceGroupsStrings (BaseString) { ns.alert = { MISSING_PARAMETER: t.s('Instance Group parameter is missing.'), }; - - ns.sort = { - NAME_ASCENDING: t.s('Name (Ascending)'), - NAME_DESCENDING: t.s('Name (Descending)') - }; } InstanceGroupsStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js b/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js index 474e3712bf..fdafc621bb 100644 --- a/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js +++ b/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js @@ -42,10 +42,13 @@ function InstanceModalController ($scope, $state, Dataset, models, strings, Proc vm.toolbarSortValue = toolbarSortDefault; vm.toolbarSortOptions = [ toolbarSortDefault, - { - label: `${strings.get('sort.NAME_DESCENDING')}`, - value: '-hostname' - } + { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-hostname' }, + { label: `${strings.get('sort.UUID_ASCENDING')}`, value: 'uuid' }, + { label: `${strings.get('sort.UUID_DESCENDING')}`, value: '-uuid' }, + { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, + { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' }, + { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' } ]; const removeStateParamsListener = $scope.$watchCollection('$state.params', () => { diff --git a/awx/ui/client/src/instance-groups/instances/instances.controller.js b/awx/ui/client/src/instance-groups/instances/instances.controller.js index 329ee1ad19..7b54b13af1 100644 --- a/awx/ui/client/src/instance-groups/instances/instances.controller.js +++ b/awx/ui/client/src/instance-groups/instances/instances.controller.js @@ -24,10 +24,13 @@ function InstancesController ($scope, $state, $http, $transitions, models, strin vm.toolbarSortValue = toolbarSortDefault; vm.toolbarSortOptions = [ toolbarSortDefault, - { - label: `${strings.get('sort.NAME_DESCENDING')}`, - value: '-hostname' - } + { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-hostname' }, + { label: `${strings.get('sort.UUID_ASCENDING')}`, value: 'uuid' }, + { label: `${strings.get('sort.UUID_DESCENDING')}`, value: '-uuid' }, + { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, + { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' }, + { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' } ]; const removeStateParamsListener = $scope.$watchCollection('$state.params', () => { diff --git a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js index 766f968442..2037aef0d9 100644 --- a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js +++ b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js @@ -43,10 +43,11 @@ export default ['$scope', '$filter', '$state', 'Alert', 'resolvedModels', 'Datas vm.toolbarSortOptions = [ toolbarSortDefault, - { - label: `${strings.get('sort.NAME_DESCENDING')}`, - value: '-name' - } + { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' }, + { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, + { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' }, + { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' } ]; vm.toolbarSortValue = toolbarSortDefault; From 14045c10179460ec9823c0a3762f2b2227ccb02f Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Fri, 26 Apr 2019 10:33:47 -0400 Subject: [PATCH 02/15] fix undefined prop in template credential prompt initialization The prompt initialization code had a reference to an undefined prop. This updates the code to use the expected prop. --- awx/ui/client/src/templates/prompt/prompt.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/templates/prompt/prompt.controller.js b/awx/ui/client/src/templates/prompt/prompt.controller.js index 5bf6cee2df..d23dce9a54 100644 --- a/awx/ui/client/src/templates/prompt/prompt.controller.js +++ b/awx/ui/client/src/templates/prompt/prompt.controller.js @@ -154,7 +154,7 @@ export default [ 'ProcessErrors', 'CredentialTypeModel', 'TemplatesStrings', '$f _disabled: (order === 1 || vm.readOnlyPrompts) ? false : true, order: order }; - activeTab = activeTab || vm.steps.credentials.tab; + activeTab = activeTab || vm.steps.credential.tab; order++; } if(vm.promptDataClone.launchConf.ask_verbosity_on_launch || vm.promptDataClone.launchConf.ask_job_type_on_launch || vm.promptDataClone.launchConf.ask_limit_on_launch || vm.promptDataClone.launchConf.ask_tags_on_launch || vm.promptDataClone.launchConf.ask_skip_tags_on_launch || (vm.promptDataClone.launchConf.ask_variables_on_launch && !vm.promptDataClone.launchConf.ignore_ask_variables) || vm.promptDataClone.launchConf.ask_diff_mode_on_launch) { From 36e384e8ab3956bd72da6cd232eb9f8ff271a232 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Fri, 26 Apr 2019 11:18:39 -0400 Subject: [PATCH 03/15] hide dashboard tips on graph teardown event --- .../graphs/job-status/job-status-graph.directive.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js index 6fd48cf8cd..96e0b81786 100644 --- a/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js +++ b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js @@ -96,6 +96,11 @@ function JobStatusGraph($window, adjustGraphSize, templateUrl, i18n, moment, gra job_status_chart.interactiveLayer.tooltip.fixedTop(-10); //distance from the top of the chart to tooltip job_status_chart.interactiveLayer.tooltip.distance(-1); //distance from interactive line to tooltip + scope.$on('$destroy', function() { + job_status_chart.tooltip.hidden(true); + job_status_chart.interactiveLayer.tooltip.hidden(true); + }); + job_status_chart.xAxis .axisLabel(i18n._("TIME"))//.showMaxMin(true) .tickFormat(function(d) { From 3cd8d4f7e62f24bf3da4ff7ac1935fc45bc5d099 Mon Sep 17 00:00:00 2001 From: mabashian Date: Fri, 26 Apr 2019 11:52:07 -0400 Subject: [PATCH 04/15] Adds more sort options to lists --- awx/ui/client/features/jobs/jobsList.controller.js | 6 ++++++ .../features/projects/projectsList.controller.js | 4 +++- .../features/templates/templatesList.controller.js | 6 +++++- awx/ui/client/lib/services/base-string.service.js | 13 ++++++++++++- .../instances/instance-modal.controller.js | 4 +++- .../instances/instances.controller.js | 4 +++- 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/awx/ui/client/features/jobs/jobsList.controller.js b/awx/ui/client/features/jobs/jobsList.controller.js index 9b99bc397d..c4784e5a69 100644 --- a/awx/ui/client/features/jobs/jobsList.controller.js +++ b/awx/ui/client/features/jobs/jobsList.controller.js @@ -66,6 +66,12 @@ function ListJobsController ( { label: `${strings.get('sort.FINISH_TIME_ASCENDING')}`, value: 'finished' }, { label: `${strings.get('sort.START_TIME_ASCENDING')}`, value: 'started' }, { label: `${strings.get('sort.START_TIME_DESCENDING')}`, value: '-started' }, + { label: `${strings.get('sort.LAUNCHED_BY_ASCENDING')}`, value: 'created_by__id' }, + { label: `${strings.get('sort.LAUNCHED_BY_DESCENDING')}`, value: '-created_by__id' }, + { label: `${strings.get('sort.INVENTORY_ASCENDING')}`, value: 'unified_job_template__inventory__id' }, + { label: `${strings.get('sort.INVENTORY_DESCENDING')}`, value: '-unified_job_template__inventory__id' }, + { label: `${strings.get('sort.PROJECT_ASCENDING')}`, value: 'unified_job_template__project__id' }, + { label: `${strings.get('sort.PROJECT_DESCENDING')}`, value: '-unified_job_template__project__id' }, toolbarSortDefault ]; diff --git a/awx/ui/client/features/projects/projectsList.controller.js b/awx/ui/client/features/projects/projectsList.controller.js index 3d2e1369fa..d1ba3922f7 100644 --- a/awx/ui/client/features/projects/projectsList.controller.js +++ b/awx/ui/client/features/projects/projectsList.controller.js @@ -72,7 +72,9 @@ function projectsListController ( { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, { label: `${strings.get('sort.LAST_JOB_RUN_ASCENDING')}`, value: 'last_job_run' }, - { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' } + { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' }, + { label: `${strings.get('sort.ORGANIZATION_ASCENDING')}`, value: 'organization' }, + { label: `${strings.get('sort.ORGANIZATION_DESCENDING')}`, value: '-organization' } ]; vm.toolbarSortValue = toolbarSortDefault; diff --git a/awx/ui/client/features/templates/templatesList.controller.js b/awx/ui/client/features/templates/templatesList.controller.js index 84c2d512fb..7a0e13b6f8 100644 --- a/awx/ui/client/features/templates/templatesList.controller.js +++ b/awx/ui/client/features/templates/templatesList.controller.js @@ -75,7 +75,11 @@ function ListTemplatesController( { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, { label: `${strings.get('sort.LAST_JOB_RUN_ASCENDING')}`, value: 'last_job_run' }, - { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' } + { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' }, + { label: `${strings.get('sort.INVENTORY_ASCENDING')}`, value: 'job_template__inventory__id' }, + { label: `${strings.get('sort.INVENTORY_DESCENDING')}`, value: '-job_template__inventory__id' }, + { label: `${strings.get('sort.PROJECT_ASCENDING')}`, value: 'project__id' }, + { label: `${strings.get('sort.PROJECT_DESCENDING')}`, value: '-project__id' }, ]; vm.toolbarSortValue = toolbarSortDefault; diff --git a/awx/ui/client/lib/services/base-string.service.js b/awx/ui/client/lib/services/base-string.service.js index df725d43a4..e90c343678 100644 --- a/awx/ui/client/lib/services/base-string.service.js +++ b/awx/ui/client/lib/services/base-string.service.js @@ -121,7 +121,18 @@ function BaseStringService (namespace) { FINISH_TIME_ASCENDING: t.s('Finish Time (Ascending)'), FINISH_TIME_DESCENDING: t.s('Finish Time (Descending)'), UUID_ASCENDING: t.s('UUID (Ascending)'), - UUID_DESCENDING: t.s('UUID (Descending)') + UUID_DESCENDING: t.s('UUID (Descending)'), + LAUNCHED_BY_ASCENDING: t.s('Launched By (Ascending)'), + LAUNCHED_BY_DESCENDING: t.s('Launched By (Descending)'), + INVENTORY_ASCENDING: t.s('Inventory (Ascending)'), + INVENTORY_DESCENDING: t.s('Inventory (Descending)'), + PROJECT_ASCENDING: t.s('Project (Ascending)'), + PROJECT_DESCENDING: t.s('Project (Descending)'), + ORGANIZATION_ASCENDING: t.s('Organization (Ascending)'), + ORGANIZATION_DESCENDING: t.s('Organization (Descending)'), + CAPACITY_ASCENDING: t.s('Capacity (Ascending)'), + CAPACITY_DESCENDING: t.s('Capacity (Descending)') + }; this.ALERT = ({ header, body }) => t.s('{{ header }} {{ body }}', { header, body }); diff --git a/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js b/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js index fdafc621bb..7ca555d81e 100644 --- a/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js +++ b/awx/ui/client/src/instance-groups/instances/instance-modal.controller.js @@ -48,7 +48,9 @@ function InstanceModalController ($scope, $state, Dataset, models, strings, Proc { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' }, { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, - { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' } + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, + { label: `${strings.get('sort.CAPACITY_ASCENDING')}`, value: 'capacity' }, + { label: `${strings.get('sort.CAPACITY_DESCENDING')}`, value: '-capacity' } ]; const removeStateParamsListener = $scope.$watchCollection('$state.params', () => { diff --git a/awx/ui/client/src/instance-groups/instances/instances.controller.js b/awx/ui/client/src/instance-groups/instances/instances.controller.js index 7b54b13af1..dbd5645d0a 100644 --- a/awx/ui/client/src/instance-groups/instances/instances.controller.js +++ b/awx/ui/client/src/instance-groups/instances/instances.controller.js @@ -30,7 +30,9 @@ function InstancesController ($scope, $state, $http, $transitions, models, strin { label: `${strings.get('sort.CREATED_ASCENDING')}`, value: 'created' }, { label: `${strings.get('sort.CREATED_DESCENDING')}`, value: '-created' }, { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, - { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' } + { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, + { label: `${strings.get('sort.CAPACITY_ASCENDING')}`, value: 'capacity' }, + { label: `${strings.get('sort.CAPACITY_DESCENDING')}`, value: '-capacity' } ]; const removeStateParamsListener = $scope.$watchCollection('$state.params', () => { From b7e26b350787c5b64a6b108637876765ee3049c2 Mon Sep 17 00:00:00 2001 From: mabashian Date: Fri, 26 Apr 2019 13:15:37 -0400 Subject: [PATCH 05/15] Fix linting error --- awx/ui/client/lib/services/base-string.service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/awx/ui/client/lib/services/base-string.service.js b/awx/ui/client/lib/services/base-string.service.js index e90c343678..faf89389e7 100644 --- a/awx/ui/client/lib/services/base-string.service.js +++ b/awx/ui/client/lib/services/base-string.service.js @@ -132,7 +132,6 @@ function BaseStringService (namespace) { ORGANIZATION_DESCENDING: t.s('Organization (Descending)'), CAPACITY_ASCENDING: t.s('Capacity (Ascending)'), CAPACITY_DESCENDING: t.s('Capacity (Descending)') - }; this.ALERT = ({ header, body }) => t.s('{{ header }} {{ body }}', { header, body }); From 5d1346b956e4276ee5963480a47ffda050bbfc21 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Mon, 29 Apr 2019 20:32:50 -0400 Subject: [PATCH 06/15] Use AWX python interpreter for failure-event-handler. python3 isn't in the normal path when using SCLs. --- tools/scripts/failure-event-handler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/scripts/failure-event-handler b/tools/scripts/failure-event-handler index 9838696f1c..da5008f4ac 100755 --- a/tools/scripts/failure-event-handler +++ b/tools/scripts/failure-event-handler @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/awx-python import sys import os import signal From d1dc6007fdef9870603bcfabe8a222e9a6be7512 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 30 Apr 2019 12:25:43 -0400 Subject: [PATCH 07/15] use test callback from test form save hooks Enter key and other form submits are handled generically from the built-in save hooks on the form controller. This adds implementations for those hooks on the plugin test forms to make sure the expected handlers are always called. --- .../features/credentials/external-test-modal.component.js | 4 ++++ .../features/credentials/input-source-lookup.component.js | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/features/credentials/external-test-modal.component.js b/awx/ui/client/features/credentials/external-test-modal.component.js index 8600cc45f1..7b8ac82685 100644 --- a/awx/ui/client/features/credentials/external-test-modal.component.js +++ b/awx/ui/client/features/credentials/external-test-modal.component.js @@ -5,6 +5,10 @@ function ExternalTestModalController (strings) { vm.strings = strings; vm.title = strings.get('externalTest.TITLE'); + + vm.$onInit = () => { + vm.form.save = () => vm.onSubmit(); + }; } ExternalTestModalController.$inject = [ diff --git a/awx/ui/client/features/credentials/input-source-lookup.component.js b/awx/ui/client/features/credentials/input-source-lookup.component.js index 6d6d8841de..b76baf043b 100644 --- a/awx/ui/client/features/credentials/input-source-lookup.component.js +++ b/awx/ui/client/features/credentials/input-source-lookup.component.js @@ -6,7 +6,10 @@ function InputSourceLookupController (strings, wait) { vm.strings = strings; vm.title = strings.get('inputSources.TITLE'); - vm.$onInit = () => wait('start'); + vm.$onInit = () => { + wait('start'); + vm.form.save = () => vm.onTest(); + }; vm.onReady = () => { vm.isReady = true; From 9737ab620c1c5c9f9cfe5c014c73a482e14179ea Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 30 Apr 2019 13:17:15 -0400 Subject: [PATCH 08/15] require url scheme for credential type url inputs This adds a url formatting type for credential input string fields The validator for this formatting type will throw an error if the provided url string doesn't have a url schema. --- awx/main/credential_plugins/aim.py | 1 + awx/main/credential_plugins/azure_kv.py | 1 + awx/main/credential_plugins/conjur.py | 1 + awx/main/credential_plugins/hashivault.py | 1 + awx/main/fields.py | 11 ++++++- .../tests/functional/api/test_credential.py | 31 +++++++++++++++++++ 6 files changed, 45 insertions(+), 1 deletion(-) diff --git a/awx/main/credential_plugins/aim.py b/awx/main/credential_plugins/aim.py index d59fcb639d..8fde480b73 100644 --- a/awx/main/credential_plugins/aim.py +++ b/awx/main/credential_plugins/aim.py @@ -15,6 +15,7 @@ aim_inputs = { 'id': 'url', 'label': _('CyberArk AIM URL'), 'type': 'string', + 'format': 'url', }, { 'id': 'app_id', 'label': _('Application ID'), diff --git a/awx/main/credential_plugins/azure_kv.py b/awx/main/credential_plugins/azure_kv.py index ee193974d0..77b7394b1f 100644 --- a/awx/main/credential_plugins/azure_kv.py +++ b/awx/main/credential_plugins/azure_kv.py @@ -10,6 +10,7 @@ azure_keyvault_inputs = { 'id': 'url', 'label': _('Vault URL (DNS Name)'), 'type': 'string', + 'format': 'url', }, { 'id': 'client', 'label': _('Client ID'), diff --git a/awx/main/credential_plugins/conjur.py b/awx/main/credential_plugins/conjur.py index e7bcf9c859..c2a60f72b6 100644 --- a/awx/main/credential_plugins/conjur.py +++ b/awx/main/credential_plugins/conjur.py @@ -16,6 +16,7 @@ conjur_inputs = { 'id': 'url', 'label': _('Conjur URL'), 'type': 'string', + 'format': 'url', }, { 'id': 'api_key', 'label': _('API Key'), diff --git a/awx/main/credential_plugins/hashivault.py b/awx/main/credential_plugins/hashivault.py index fefa46cbae..f82af720cf 100644 --- a/awx/main/credential_plugins/hashivault.py +++ b/awx/main/credential_plugins/hashivault.py @@ -14,6 +14,7 @@ base_inputs = { 'id': 'url', 'label': _('Server URL'), 'type': 'string', + 'format': 'url', 'help_text': _('The URL to the HashiCorp Vault'), }, { 'id': 'token', diff --git a/awx/main/fields.py b/awx/main/fields.py index 963723319d..ab97552e81 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -490,6 +490,15 @@ def format_ssh_private_key(value): return True +@JSONSchemaField.format_checker.checks('url') +def format_url(value): + if urllib.parse.urlparse(value).scheme == '': + raise jsonschema.exceptions.FormatError( + 'Invalid URL: Missing url scheme (http, https, etc.)' + ) + return True + + class DynamicCredentialInputField(JSONSchemaField): """ Used to validate JSON for @@ -722,7 +731,7 @@ class CredentialTypeInputField(JSONSchemaField): 'type': 'object', 'properties': { 'type': {'enum': ['string', 'boolean']}, - 'format': {'enum': ['ssh_private_key']}, + 'format': {'enum': ['ssh_private_key', 'url']}, 'choices': { 'type': 'array', 'minItems': 1, diff --git a/awx/main/tests/functional/api/test_credential.py b/awx/main/tests/functional/api/test_credential.py index 2cb287b044..abf4cdc682 100644 --- a/awx/main/tests/functional/api/test_credential.py +++ b/awx/main/tests/functional/api/test_credential.py @@ -1942,3 +1942,34 @@ def test_create_credential_missing_user_team_org_xfail(post, admin, credentialty admin ) assert response.status_code == 400 + + +@pytest.mark.django_db +def test_create_credential_with_missing_url_schema_xfail(post, organization, admin): + credential_type = CredentialType( + kind='test', + name='MyTestCredentialType', + inputs = { + 'fields': [{ + 'id': 'server_url', + 'label': 'Server Url', + 'type': 'string', + 'format': 'url' + }] + } + ) + credential_type.save() + + params = { + 'name': 'Second Best credential ever', + 'organization': organization.pk, + 'credential_type': credential_type.pk, + 'inputs': {'server_url': 'foo.com'} + } + endpoint = reverse('api:credential_list', kwargs={'version': 'v2'}) + response = post(endpoint, params, admin) + assert response.status_code == 400 + + params['inputs'] = {'server_url': 'http://foo.com'} + response = post(endpoint, params, admin) + assert response.status_code == 201 From eaaad89a8c69f680b81062cb794f8e37f3d434e8 Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 1 May 2019 09:17:15 -0400 Subject: [PATCH 09/15] Removed inventory sorting from jobs list. Changed Last Run to Last Used in project sort filters. --- awx/ui/client/features/jobs/jobsList.controller.js | 2 -- awx/ui/client/features/projects/projectsList.controller.js | 4 ++-- awx/ui/client/lib/services/base-string.service.js | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/ui/client/features/jobs/jobsList.controller.js b/awx/ui/client/features/jobs/jobsList.controller.js index c4784e5a69..3881a4071c 100644 --- a/awx/ui/client/features/jobs/jobsList.controller.js +++ b/awx/ui/client/features/jobs/jobsList.controller.js @@ -68,8 +68,6 @@ function ListJobsController ( { label: `${strings.get('sort.START_TIME_DESCENDING')}`, value: '-started' }, { label: `${strings.get('sort.LAUNCHED_BY_ASCENDING')}`, value: 'created_by__id' }, { label: `${strings.get('sort.LAUNCHED_BY_DESCENDING')}`, value: '-created_by__id' }, - { label: `${strings.get('sort.INVENTORY_ASCENDING')}`, value: 'unified_job_template__inventory__id' }, - { label: `${strings.get('sort.INVENTORY_DESCENDING')}`, value: '-unified_job_template__inventory__id' }, { label: `${strings.get('sort.PROJECT_ASCENDING')}`, value: 'unified_job_template__project__id' }, { label: `${strings.get('sort.PROJECT_DESCENDING')}`, value: '-unified_job_template__project__id' }, toolbarSortDefault diff --git a/awx/ui/client/features/projects/projectsList.controller.js b/awx/ui/client/features/projects/projectsList.controller.js index d1ba3922f7..a295d6e52b 100644 --- a/awx/ui/client/features/projects/projectsList.controller.js +++ b/awx/ui/client/features/projects/projectsList.controller.js @@ -71,8 +71,8 @@ function projectsListController ( { label: `${strings.get('sort.NAME_DESCENDING')}`, value: '-name' }, { label: `${strings.get('sort.MODIFIED_ASCENDING')}`, value: 'modified' }, { label: `${strings.get('sort.MODIFIED_DESCENDING')}`, value: '-modified' }, - { label: `${strings.get('sort.LAST_JOB_RUN_ASCENDING')}`, value: 'last_job_run' }, - { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' }, + { label: `${strings.get('sort.LAST_USED_ASCENDING')}`, value: 'last_job_run' }, + { label: `${strings.get('sort.LAST_USED_DESCENDING')}`, value: '-last_job_run' }, { label: `${strings.get('sort.ORGANIZATION_ASCENDING')}`, value: 'organization' }, { label: `${strings.get('sort.ORGANIZATION_DESCENDING')}`, value: '-organization' } ]; diff --git a/awx/ui/client/lib/services/base-string.service.js b/awx/ui/client/lib/services/base-string.service.js index faf89389e7..3e7b3b7cd7 100644 --- a/awx/ui/client/lib/services/base-string.service.js +++ b/awx/ui/client/lib/services/base-string.service.js @@ -114,6 +114,8 @@ function BaseStringService (namespace) { EXPIRES_DESCENDING: t.s('Expires (Descending)'), LAST_JOB_RUN_ASCENDING: t.s('Last Run (Ascending)'), LAST_JOB_RUN_DESCENDING: t.s('Last Run (Descending)'), + LAST_USED_ASCENDING: t.s('Last Used (Ascending)'), + LAST_USED_DESCENDING: t.s('Last Used (Descending)'), USERNAME_ASCENDING: t.s('Username (Ascending)'), USERNAME_DESCENDING: t.s('Username (Descending)'), START_TIME_ASCENDING: t.s('Start Time (Ascending)'), From 84b21620b265fb0fd9fb17225a949729a3106348 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Wed, 1 May 2019 08:56:56 -0400 Subject: [PATCH 10/15] raise url string parsing error as validation error --- awx/main/fields.py | 6 +++++- awx/main/tests/functional/api/test_credential.py | 12 +++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index ab97552e81..4914cc8b07 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -492,7 +492,11 @@ def format_ssh_private_key(value): @JSONSchemaField.format_checker.checks('url') def format_url(value): - if urllib.parse.urlparse(value).scheme == '': + try: + scheme = urllib.parse.urlparse(value).scheme + except Exception as e: + raise jsonschema.exceptions.FormatError(str(e)) + if scheme == '': raise jsonschema.exceptions.FormatError( 'Invalid URL: Missing url scheme (http, https, etc.)' ) diff --git a/awx/main/tests/functional/api/test_credential.py b/awx/main/tests/functional/api/test_credential.py index abf4cdc682..0e82785eab 100644 --- a/awx/main/tests/functional/api/test_credential.py +++ b/awx/main/tests/functional/api/test_credential.py @@ -1945,7 +1945,7 @@ def test_create_credential_missing_user_team_org_xfail(post, admin, credentialty @pytest.mark.django_db -def test_create_credential_with_missing_url_schema_xfail(post, organization, admin): +def test_create_credential_with_invalid_url_xfail(post, organization, admin): credential_type = CredentialType( kind='test', name='MyTestCredentialType', @@ -1961,7 +1961,7 @@ def test_create_credential_with_missing_url_schema_xfail(post, organization, adm credential_type.save() params = { - 'name': 'Second Best credential ever', + 'name': 'Second Best Credential Ever', 'organization': organization.pk, 'credential_type': credential_type.pk, 'inputs': {'server_url': 'foo.com'} @@ -1969,7 +1969,13 @@ def test_create_credential_with_missing_url_schema_xfail(post, organization, adm endpoint = reverse('api:credential_list', kwargs={'version': 'v2'}) response = post(endpoint, params, admin) assert response.status_code == 400 + assert response.data['inputs']['server_url'] == ['Invalid URL: Missing url scheme (http, https, etc.)'] - params['inputs'] = {'server_url': 'http://foo.com'} + params['inputs']['server_url'] = 'https://[dead:beef' + response = post(endpoint, params, admin) + assert response.status_code == 400 + assert response.data['inputs']['server_url'] == ['Invalid IPv6 URL'] + + params['inputs']['server_url'] = 'http://foo.com' response = post(endpoint, params, admin) assert response.status_code == 201 From 46795cc71ef63135ec097f72e8c82448bedcca8d Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 1 May 2019 10:02:43 -0400 Subject: [PATCH 11/15] fix a bug that caused parent_uuid not to be persisted --- awx/main/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index d7521729b0..7e4b536135 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1042,7 +1042,7 @@ class BaseTask(object): AWX only saves the parent_uuid if the event is for a Job. ''' if event_data.get(self.event_data_key, None): - if event_data[self.event_data_key] != 'job_id': + if self.event_data_key != 'job_id': event_data.pop('parent_uuid', None) should_write_event = False event_data.setdefault(self.event_data_key, self.instance.id) From 04d22a930d662e4269b5b8337645232bb4de4d3e Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 2 May 2019 10:43:46 -0400 Subject: [PATCH 12/15] Fix inert event expand/collapse on refreshed job results --- awx/ui/client/features/output/_index.less | 4 ++++ .../features/output/index.controller.js | 8 ++++++-- .../client/features/output/render.service.js | 20 ++++++++++++++----- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index 8d5ac05b31..a4bef87569 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -79,6 +79,10 @@ &:hover div { background-color: white; } + + &--hidden { + display: none; + } } &-row--clickable { diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 2f4a3e2575..b9e81caac7 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -439,7 +439,7 @@ function togglePanelExpand () { const iconCollapsed = 'fa-angle-right'; const iconExpanded = 'fa-angle-down'; const iconSelector = '.at-Stdout-toggle > i'; -const lineCollapsed = 'hidden'; +const lineCollapsed = 'at-Stdout-row--hidden'; function toggleCollapseAll () { if (scroll.isPaused()) return; @@ -524,11 +524,15 @@ function togglePlayCollapse (uuid) { taskIcons.addClass(iconCollapsed); lines.addClass(lineCollapsed); + + descendants + .map(item => $(`.child-of-${item}`)) + .forEach(line => line.addClass(lineCollapsed)); } descendants .map(item => render.records[item]) - .filter(({ name }) => name === EVENT_START_TASK) + .filter((descRecord) => descRecord && descRecord.name === EVENT_START_TASK) .forEach(rec => { render.records[rec.uuid].isCollapsed = true; }); render.records[uuid].isCollapsed = !isCollapsed; diff --git a/awx/ui/client/features/output/render.service.js b/awx/ui/client/features/output/render.service.js index 0655dd0c71..f50bc27b40 100644 --- a/awx/ui/client/features/output/render.service.js +++ b/awx/ui/client/features/output/render.service.js @@ -204,7 +204,7 @@ function JobRenderService ($q, $compile, $sce, $window, strings) { return { html: '', count: 0 }; } - if (event.uuid && this.records[event.uuid]) { + if (event.uuid && this.records[event.uuid] && !this.records[event.uuid]._isIncomplete) { return { html: '', count: 0 }; } @@ -272,6 +272,9 @@ function JobRenderService ($q, $compile, $sce, $window, strings) { isClickable = true; } + const children = (this.records[event.uuid] && this.records[event.uuid].children) + ? this.records[event.uuid].children : []; + const record = { isClickable, id: event.id, @@ -285,6 +288,7 @@ function JobRenderService ($q, $compile, $sce, $window, strings) { lineCount: lines.length, isCollapsed: this.state.collapseAll, counters: [event.counter], + children }; if (event.parent_uuid) { @@ -307,12 +311,18 @@ function JobRenderService ($q, $compile, $sce, $window, strings) { if (event.parent_uuid) { if (this.records[event.parent_uuid]) { - if (this.records[event.parent_uuid].children && - !this.records[event.parent_uuid].children.includes(event.uuid)) { - this.records[event.parent_uuid].children.push(event.uuid); + if (this.records[event.parent_uuid].children) { + if (!this.records[event.parent_uuid].children.includes(event.uuid)) { + this.records[event.parent_uuid].children.push(event.uuid); + } } else { this.records[event.parent_uuid].children = [event.uuid]; } + } else { + this.records[event.parent_uuid] = { + _isIncomplete: true, + children: [event.uuid] + }; } } } @@ -397,7 +407,7 @@ function JobRenderService ($q, $compile, $sce, $window, strings) { if (record && record.isCollapsed) { if (record.level === 3 || record.level === 0) { - classList += ' hidden'; + classList += ' at-Stdout-row--hidden'; } } From 6bb99025885be0dc122dd556619295c76912d3db Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Thu, 2 May 2019 11:11:06 -0400 Subject: [PATCH 13/15] pin urllib3 to 1.24.3 to address CVE-2019-9740 --- requirements/requirements.txt | 2 +- requirements/requirements_ansible.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 6d9c19694a..cf2bb212a1 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -112,7 +112,7 @@ twilio==6.10.4 twisted==18.9.0 # via daphne txaio==18.8.1 # via autobahn typing==3.6.6 # via django-extensions -urllib3==1.24.1 # via requests +urllib3==1.24.3 # via requests uwsgi==2.0.17 uwsgitop==0.10.0 vine==1.2.0 # via amqp diff --git a/requirements/requirements_ansible.txt b/requirements/requirements_ansible.txt index 0be2c2861f..f3c638aa60 100644 --- a/requirements/requirements_ansible.txt +++ b/requirements/requirements_ansible.txt @@ -115,7 +115,7 @@ selectors2==2.0.1 # via ncclient six==1.11.0 # via azure-cli-core, bcrypt, cryptography, google-auth, isodate, keystoneauth1, knack, munch, ncclient, ntlm-auth, openstacksdk, ovirt-engine-sdk-python, packaging, pynacl, pyopenssl, python-dateutil, pyvmomi, pywinrm, stevedore stevedore==1.28.0 # via keystoneauth1 tabulate==0.7.7 # via azure-cli-core, knack -urllib3==1.24 # via requests +urllib3==1.24.3 # via requests wheel==0.30.0 # via azure-cli-core xmltodict==0.11.0 # via pywinrm From b754e0dbba3f28681a44589bf11d615816ba7924 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 2 May 2019 13:55:20 -0400 Subject: [PATCH 14/15] Fix template sorting by project --- awx/ui/client/features/templates/templatesList.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/templates/templatesList.controller.js b/awx/ui/client/features/templates/templatesList.controller.js index 7a0e13b6f8..8c95aa745a 100644 --- a/awx/ui/client/features/templates/templatesList.controller.js +++ b/awx/ui/client/features/templates/templatesList.controller.js @@ -78,8 +78,8 @@ function ListTemplatesController( { label: `${strings.get('sort.LAST_JOB_RUN_DESCENDING')}`, value: '-last_job_run' }, { label: `${strings.get('sort.INVENTORY_ASCENDING')}`, value: 'job_template__inventory__id' }, { label: `${strings.get('sort.INVENTORY_DESCENDING')}`, value: '-job_template__inventory__id' }, - { label: `${strings.get('sort.PROJECT_ASCENDING')}`, value: 'project__id' }, - { label: `${strings.get('sort.PROJECT_DESCENDING')}`, value: '-project__id' }, + { label: `${strings.get('sort.PROJECT_ASCENDING')}`, value: 'jobtemplate__project__id' }, + { label: `${strings.get('sort.PROJECT_DESCENDING')}`, value: '-jobtemplate__project__id' }, ]; vm.toolbarSortValue = toolbarSortDefault; From e560dccd369be0fea7ddd717be8822fffe026181 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Thu, 2 May 2019 14:32:24 -0400 Subject: [PATCH 15/15] require a valid netloc for Credential Type inputs w/ format=url --- awx/main/fields.py | 8 ++++-- .../tests/functional/api/test_credential.py | 25 +++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index 4914cc8b07..6ed4f4281d 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -493,13 +493,17 @@ def format_ssh_private_key(value): @JSONSchemaField.format_checker.checks('url') def format_url(value): try: - scheme = urllib.parse.urlparse(value).scheme + parsed = urllib.parse.urlparse(value) except Exception as e: raise jsonschema.exceptions.FormatError(str(e)) - if scheme == '': + if parsed.scheme == '': raise jsonschema.exceptions.FormatError( 'Invalid URL: Missing url scheme (http, https, etc.)' ) + if parsed.netloc == '': + raise jsonschema.exceptions.FormatError( + 'Invalid URL: {}'.format(value) + ) return True diff --git a/awx/main/tests/functional/api/test_credential.py b/awx/main/tests/functional/api/test_credential.py index 0e82785eab..879a3e4de6 100644 --- a/awx/main/tests/functional/api/test_credential.py +++ b/awx/main/tests/functional/api/test_credential.py @@ -1944,8 +1944,15 @@ def test_create_credential_missing_user_team_org_xfail(post, admin, credentialty assert response.status_code == 400 +@pytest.mark.parametrize('url, status, msg', [ + ('foo.com', 400, 'Invalid URL: Missing url scheme (http, https, etc.)'), + ('https://[dead:beef', 400, 'Invalid IPv6 URL'), + ('http:domain:8080', 400, 'Invalid URL: http:domain:8080'), + ('http:/domain:8080', 400, 'Invalid URL: http:/domain:8080'), + ('http://foo.com', 201, None) +]) @pytest.mark.django_db -def test_create_credential_with_invalid_url_xfail(post, organization, admin): +def test_create_credential_with_invalid_url_xfail(post, organization, admin, url, status, msg): credential_type = CredentialType( kind='test', name='MyTestCredentialType', @@ -1964,18 +1971,10 @@ def test_create_credential_with_invalid_url_xfail(post, organization, admin): 'name': 'Second Best Credential Ever', 'organization': organization.pk, 'credential_type': credential_type.pk, - 'inputs': {'server_url': 'foo.com'} + 'inputs': {'server_url': url} } endpoint = reverse('api:credential_list', kwargs={'version': 'v2'}) response = post(endpoint, params, admin) - assert response.status_code == 400 - assert response.data['inputs']['server_url'] == ['Invalid URL: Missing url scheme (http, https, etc.)'] - - params['inputs']['server_url'] = 'https://[dead:beef' - response = post(endpoint, params, admin) - assert response.status_code == 400 - assert response.data['inputs']['server_url'] == ['Invalid IPv6 URL'] - - params['inputs']['server_url'] = 'http://foo.com' - response = post(endpoint, params, admin) - assert response.status_code == 201 + assert response.status_code == status + if status != 201: + assert response.data['inputs']['server_url'] == [msg]