From 45d037a2cc9bd2139c647f3222f4388d9d4ee96e Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Wed, 25 Apr 2018 21:12:13 -0400 Subject: [PATCH 1/6] don't prefer destructuring for arrays within es-lint --- awx/ui/.eslintrc.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/awx/ui/.eslintrc.js b/awx/ui/.eslintrc.js index 217601f5f9..9a42d34ab2 100644 --- a/awx/ui/.eslintrc.js +++ b/awx/ui/.eslintrc.js @@ -54,6 +54,18 @@ module.exports = { 'no-multiple-empty-lines': ['error', { max: 1 }], 'object-curly-newline': 'off', 'space-before-function-paren': ['error', 'always'], - 'no-trailing-spaces': ['error'] - } + 'no-trailing-spaces': ['error'], + 'prefer-destructuring': ['error', { + 'VariableDeclarator': { + 'array': false, + 'object': true + }, + 'AssignmentExpression': { + 'array': false, + 'object': true + } + }, { + 'enforceForRenamedProperties': false + }] + } }; From 72254758f652f9c67aef7de60a86e881b23d171a Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Thu, 26 Apr 2018 13:22:39 -0400 Subject: [PATCH 2/6] add support for additional selects --- .../add-applications.controller.js | 6 +--- .../add-edit-applications.view.html | 2 ++ .../edit-applications.controller.js | 6 +--- .../lib/components/form/form.directive.js | 4 ++- .../lib/components/input/base.controller.js | 2 +- .../lib/components/input/select.directive.js | 4 ++- awx/ui/client/lib/models/Application.js | 31 ++++++++++++++++++- 7 files changed, 41 insertions(+), 14 deletions(-) diff --git a/awx/ui/client/features/applications/add-applications.controller.js b/awx/ui/client/features/applications/add-applications.controller.js index 173460157e..23453734fe 100644 --- a/awx/ui/client/features/applications/add-applications.controller.js +++ b/awx/ui/client/features/applications/add-applications.controller.js @@ -3,10 +3,8 @@ function AddApplicationsController (models, $state, strings) { const { application, me, organization } = models; const omit = [ - 'authorization_grant_type', 'client_id', 'client_secret', - 'client_type', 'created', 'modified', 'related', @@ -54,9 +52,7 @@ function AddApplicationsController (models, $state, strings) { vm.form.save = data => { const hiddenData = { - authorization_grant_type: 'implicit', - user: me.get('id'), - client_type: 'public' + user: me.get('id') }; const payload = _.merge(data, hiddenData); diff --git a/awx/ui/client/features/applications/add-edit-applications.view.html b/awx/ui/client/features/applications/add-edit-applications.view.html index a8d8d68e6c..9f2bbb96d3 100644 --- a/awx/ui/client/features/applications/add-edit-applications.view.html +++ b/awx/ui/client/features/applications/add-edit-applications.view.html @@ -15,6 +15,8 @@ + + diff --git a/awx/ui/client/features/applications/edit-applications.controller.js b/awx/ui/client/features/applications/edit-applications.controller.js index 6279b642ee..6b7e21aed4 100644 --- a/awx/ui/client/features/applications/edit-applications.controller.js +++ b/awx/ui/client/features/applications/edit-applications.controller.js @@ -4,10 +4,8 @@ function EditApplicationsController (models, $state, strings, $scope) { const { me, application, organization } = models; const omit = [ - 'authorization_grant_type', 'client_id', 'client_secret', - 'client_type', 'created', 'modified', 'related', @@ -90,9 +88,7 @@ function EditApplicationsController (models, $state, strings, $scope) { vm.form.save = data => { const hiddenData = { - authorization_grant_type: 'implicit', - user: me.get('id'), - client_type: 'public' + user: me.get('id') }; const payload = _.merge(data, hiddenData); diff --git a/awx/ui/client/lib/components/form/form.directive.js b/awx/ui/client/lib/components/form/form.directive.js index dc19c61e2a..e0ac85c595 100644 --- a/awx/ui/client/lib/components/form/form.directive.js +++ b/awx/ui/client/lib/components/form/form.directive.js @@ -76,7 +76,9 @@ function AtFormController (eventService, strings) { return values; } - if (component.state._key && typeof component.state._value === 'object') { + if (component.state._format === 'selectFromOptions') { + values[component.state.id] = component.state._value[0]; + } else if (component.state._key && typeof component.state._value === 'object') { values[component.state.id] = component.state._value[component.state._key]; } else if (component.state._group) { values[component.state._key] = values[component.state._key] || {}; diff --git a/awx/ui/client/lib/components/input/base.controller.js b/awx/ui/client/lib/components/input/base.controller.js index 5c27d903f0..75caad2f07 100644 --- a/awx/ui/client/lib/components/input/base.controller.js +++ b/awx/ui/client/lib/components/input/base.controller.js @@ -14,7 +14,7 @@ function BaseInputController (strings) { scope.state._required = scope.state.required || false; scope.state._isValid = scope.state._isValid || false; scope.state._disabled = scope.state._disabled || false; - scope.state._activeModel = '_value'; + scope.state._activeModel = scope.state._activeModel || '_value'; if (scope.state.ask_at_runtime) { scope.state._displayPromptOnLaunch = true; diff --git a/awx/ui/client/lib/components/input/select.directive.js b/awx/ui/client/lib/components/input/select.directive.js index 8507573a61..ddc0e23766 100644 --- a/awx/ui/client/lib/components/input/select.directive.js +++ b/awx/ui/client/lib/components/input/select.directive.js @@ -59,7 +59,9 @@ function AtInputSelectController (baseInputController, eventService) { }; vm.updateDisplayModel = () => { - if (scope.state._format === 'array') { + if (scope.state._format === 'selectFromOptions') { + scope.displayModel = scope.state._value[1]; + } else if (scope.state._format === 'array') { scope.displayModel = scope.state._value; } else if (scope.state._format === 'objects') { scope.displayModel = scope.state._value[scope.state._display]; diff --git a/awx/ui/client/lib/models/Application.js b/awx/ui/client/lib/models/Application.js index 5c8cdbd066..1fa79a3661 100644 --- a/awx/ui/client/lib/models/Application.js +++ b/awx/ui/client/lib/models/Application.js @@ -1,6 +1,21 @@ let Base; function createFormSchema (method, config) { + function mungeSelectFromOptions (configObj, value) { + configObj.choices = [['', '']].concat(configObj.choices); + configObj._data = configObj.choices; + configObj._exp = 'choice[1] for choice in state._data'; + configObj._format = 'selectFromOptions'; + + configObj._data.forEach((val, i) => { + if (val[0] === value) { + configObj._value = configObj._data[i]; + } + }); + + return configObj; + } + if (!config) { config = method; method = 'GET'; @@ -15,11 +30,25 @@ function createFormSchema (method, config) { Object.keys(schema).forEach(key => { schema[key].id = key; - if (this.has(key)) { + if (this.has(key) && schema[key].type !== 'choice') { schema[key]._value = this.get(key); } + + if (schema[key].type === 'choice') { + schema[key] = mungeSelectFromOptions(schema[key], this.get(key)); + } }); + // necessary because authorization_grant_type is not changeable on update + if (method === 'put') { + schema.authorization_grant_type = mungeSelectFromOptions(Object.assign({}, this + .options('actions.GET.authorization_grant_type')), this + .get('authorization_grant_type')); + + schema.authorization_grant_type._required = false; + schema.authorization_grant_type._disabled = true; + } + return schema; } From 3bcc48402cc9d03721ce612030fe023d08c8338b Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Thu, 26 Apr 2018 13:23:03 -0400 Subject: [PATCH 3/6] add support for applications activity stream --- awx/ui/client/features/applications/index.js | 2 -- awx/ui/client/src/activity-stream/get-target-title.factory.js | 3 +++ .../src/activity-stream/model-to-base-path-key.factory.js | 3 +++ .../streamDropdownNav/stream-dropdown-nav.directive.js | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/features/applications/index.js b/awx/ui/client/features/applications/index.js index ca70be6165..448ea12f88 100644 --- a/awx/ui/client/features/applications/index.js +++ b/awx/ui/client/features/applications/index.js @@ -62,7 +62,6 @@ function ApplicationsRun ($stateExtender, strings) { }, data: { activityStream: true, - // TODO: double-check activity stream works activityStreamTarget: 'application' }, views: { @@ -111,7 +110,6 @@ function ApplicationsRun ($stateExtender, strings) { }, data: { activityStream: true, - // TODO: double-check activity stream works activityStreamTarget: 'application' }, views: { diff --git a/awx/ui/client/src/activity-stream/get-target-title.factory.js b/awx/ui/client/src/activity-stream/get-target-title.factory.js index 3921f2d530..f782769c5c 100644 --- a/awx/ui/client/src/activity-stream/get-target-title.factory.js +++ b/awx/ui/client/src/activity-stream/get-target-title.factory.js @@ -43,6 +43,9 @@ export default function GetTargetTitle(i18n) { case 'template': rtnTitle = i18n._('TEMPLATES'); break; + case 'application': + rtnTitle = i18n._('APPLICATIONS'); + break; } return rtnTitle; diff --git a/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js b/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js index 10285871d0..f6a45e6bdd 100644 --- a/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js +++ b/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js @@ -18,6 +18,9 @@ export default function ModelToBasePathKey() { var basePathKey; switch(model) { + case 'application': + basePathKey = 'applications'; + break; case 'project': basePathKey = 'projects'; break; diff --git a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js index 7231cdd9c2..601e65f1ac 100644 --- a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js +++ b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js @@ -21,6 +21,7 @@ export default ['templateUrl', 'i18n', function(templateUrl, i18n) { $scope.options = [ {label: i18n._('All Activity'), value: 'dashboard'}, + {label: i18n._('Applications'), value: 'application'}, {label: i18n._('Credentials'), value: 'credential'}, {label: i18n._('Hosts'), value: 'host'}, {label: i18n._('Inventories'), value: 'inventory'}, From f99792e604d326732c163cd30a97af3be3f2da69 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Thu, 26 Apr 2018 17:06:20 -0400 Subject: [PATCH 4/6] make selects work with ng required --- awx/ui/client/lib/components/input/base.controller.js | 3 ++- awx/ui/client/lib/models/Application.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/lib/components/input/base.controller.js b/awx/ui/client/lib/components/input/base.controller.js index 75caad2f07..32fa30b458 100644 --- a/awx/ui/client/lib/components/input/base.controller.js +++ b/awx/ui/client/lib/components/input/base.controller.js @@ -49,7 +49,8 @@ function BaseInputController (strings) { scope.state._touched = true; } - if (scope.state._required && !scope.state._value && !scope.state._displayValue) { + if (scope.state._required && (!scope.state._value || !scope.state._value[0]) && + !scope.state._displayValue) { isValid = false; message = vm.strings.get('message.REQUIRED_INPUT_MISSING'); } else if (scope.state._validate) { diff --git a/awx/ui/client/lib/models/Application.js b/awx/ui/client/lib/models/Application.js index 1fa79a3661..2a364eb9e7 100644 --- a/awx/ui/client/lib/models/Application.js +++ b/awx/ui/client/lib/models/Application.js @@ -2,7 +2,7 @@ let Base; function createFormSchema (method, config) { function mungeSelectFromOptions (configObj, value) { - configObj.choices = [['', '']].concat(configObj.choices); + configObj.choices = [[null, '']].concat(configObj.choices); configObj._data = configObj.choices; configObj._exp = 'choice[1] for choice in state._data'; configObj._format = 'selectFromOptions'; From 6ab64590d745a601b69f7907ecd02be416fa3e39 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 27 Apr 2018 11:37:17 -0400 Subject: [PATCH 5/6] update app activity stream target to o_auth2_application --- awx/ui/client/features/applications/index.js | 9 ++++----- .../src/activity-stream/get-target-title.factory.js | 2 +- .../activity-stream/model-to-base-path-key.factory.js | 2 +- .../streamDropdownNav/stream-dropdown-nav.directive.js | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/features/applications/index.js b/awx/ui/client/features/applications/index.js index 448ea12f88..af4783884f 100644 --- a/awx/ui/client/features/applications/index.js +++ b/awx/ui/client/features/applications/index.js @@ -62,7 +62,7 @@ function ApplicationsRun ($stateExtender, strings) { }, data: { activityStream: true, - activityStreamTarget: 'application' + activityStreamTarget: 'o_auth2_application' }, views: { '@': { @@ -110,7 +110,7 @@ function ApplicationsRun ($stateExtender, strings) { }, data: { activityStream: true, - activityStreamTarget: 'application' + activityStreamTarget: 'o_auth2_application' }, views: { 'add@applications': { @@ -132,7 +132,7 @@ function ApplicationsRun ($stateExtender, strings) { }, data: { activityStream: true, - activityStreamTarget: 'application', + activityStreamTarget: 'o_auth2_application', activityStreamId: 'application_id' }, views: { @@ -262,8 +262,7 @@ function ApplicationsRun ($stateExtender, strings) { }, data: { activityStream: true, - // TODO: double-check activity stream works - activityStreamTarget: 'application' + activityStreamTarget: 'o_auth2_application' }, views: { 'userList@applications.edit': { diff --git a/awx/ui/client/src/activity-stream/get-target-title.factory.js b/awx/ui/client/src/activity-stream/get-target-title.factory.js index f782769c5c..88d274f58f 100644 --- a/awx/ui/client/src/activity-stream/get-target-title.factory.js +++ b/awx/ui/client/src/activity-stream/get-target-title.factory.js @@ -43,7 +43,7 @@ export default function GetTargetTitle(i18n) { case 'template': rtnTitle = i18n._('TEMPLATES'); break; - case 'application': + case 'o_auth2_application': rtnTitle = i18n._('APPLICATIONS'); break; } diff --git a/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js b/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js index f6a45e6bdd..2ce145c4dc 100644 --- a/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js +++ b/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js @@ -18,7 +18,7 @@ export default function ModelToBasePathKey() { var basePathKey; switch(model) { - case 'application': + case 'o_auth2_application': basePathKey = 'applications'; break; case 'project': diff --git a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js index 601e65f1ac..e60170c310 100644 --- a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js +++ b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js @@ -21,7 +21,7 @@ export default ['templateUrl', 'i18n', function(templateUrl, i18n) { $scope.options = [ {label: i18n._('All Activity'), value: 'dashboard'}, - {label: i18n._('Applications'), value: 'application'}, + {label: i18n._('Applications'), value: 'o_auth2_application'}, {label: i18n._('Credentials'), value: 'credential'}, {label: i18n._('Hosts'), value: 'host'}, {label: i18n._('Inventories'), value: 'inventory'}, From 11560ab7bbdcd28b7337d04be9254d0f4256cedb Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 27 Apr 2018 13:05:34 -0400 Subject: [PATCH 6/6] populate org and description on app detail view --- .../add-applications.controller.js | 2 +- .../applications/applications.strings.js | 4 +++ .../edit-applications.controller.js | 32 ++++++------------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/awx/ui/client/features/applications/add-applications.controller.js b/awx/ui/client/features/applications/add-applications.controller.js index 23453734fe..486c20dffb 100644 --- a/awx/ui/client/features/applications/add-applications.controller.js +++ b/awx/ui/client/features/applications/add-applications.controller.js @@ -42,7 +42,7 @@ function AddApplicationsController (models, $state, strings) { vm.form.organization._resource = 'organization'; vm.form.organization._route = 'applications.add.organization'; vm.form.organization._model = organization; - vm.form.organization._placeholder = strings.get('SELECT AN ORGANIZATION'); + vm.form.organization._placeholder = strings.get('inputs.ORGANIZATION_PLACEHOLDER'); vm.form.name.required = true; vm.form.organization.required = true; diff --git a/awx/ui/client/features/applications/applications.strings.js b/awx/ui/client/features/applications/applications.strings.js index 18158c4d4e..0194efbf42 100644 --- a/awx/ui/client/features/applications/applications.strings.js +++ b/awx/ui/client/features/applications/applications.strings.js @@ -25,6 +25,10 @@ function ApplicationsStrings (BaseString) { ROW_ITEM_LABEL_ORGANIZATION: t.s('ORG'), ROW_ITEM_LABEL_MODIFIED: t.s('LAST MODIFIED') }; + + ns.inputs = { + ORGANIZATION_PLACEHOLDER: t.s('SELECT AN ORGANIZATION') + }; } ApplicationsStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/features/applications/edit-applications.controller.js b/awx/ui/client/features/applications/edit-applications.controller.js index 6b7e21aed4..60b08be6ba 100644 --- a/awx/ui/client/features/applications/edit-applications.controller.js +++ b/awx/ui/client/features/applications/edit-applications.controller.js @@ -52,37 +52,25 @@ function EditApplicationsController (models, $state, strings, $scope) { vm.form.disabled = !isEditable; + vm.form.name.required = true; + vm.form.redirect_uris.required = true; + const isOrgAdmin = _.some(me.get('related.admin_of_organizations.results'), (org) => org.id === organization.get('id')); const isSuperuser = me.get('is_superuser'); const isCurrentAuthor = Boolean(application.get('summary_fields.created_by.id') === me.get('id')); - - vm.form.organization = { - type: 'field', - label: 'Organization', - id: 'organization' - }; - vm.form.description = { - type: 'String', - label: 'Description', - id: 'description' - }; - - vm.form.organization._resource = 'organization'; - vm.form.organization._route = 'applications.edit.organization'; - vm.form.organization._model = organization; - vm.form.organization._placeholder = strings.get('SELECT AN ORGANIZATION'); - - // TODO: org not returned via api endpoint, check on this - vm.form.organization._value = application.get('organization'); - vm.form.organization._disabled = true; + if (isSuperuser || isOrgAdmin || (application.get('organization') === null && isCurrentAuthor)) { vm.form.organization._disabled = false; } - vm.form.name.required = true; + vm.form.organization._resource = 'organization'; + vm.form.organization._model = organization; + vm.form.organization._route = 'applications.edit.organization'; + vm.form.organization._value = application.get('summary_fields.organization.id'); + vm.form.organization._displayValue = application.get('summary_fields.organization.name'); + vm.form.organization._placeholder = strings.get('inputs.ORGANIZATION_PLACEHOLDER'); vm.form.organization.required = true; - vm.form.redirect_uris.required = true; delete vm.form.name.help_text;