From 09ac71518e835f5440bf84483e0f036671f67b66 Mon Sep 17 00:00:00 2001 From: gconsidine Date: Thu, 8 Jun 2017 17:03:53 -0400 Subject: [PATCH] Add PUT support for models --- .../credentials/add-credentials.controller.js | 6 +- .../add-edit-credentials.view.html | 2 +- .../edit-credentials.controller.js | 26 +++-- awx/ui/client/features/credentials/index.js | 18 ++-- .../lib/components/input/secret.directive.js | 38 +++++++- .../lib/components/input/secret.partial.html | 6 +- .../lib/components/input/select.directive.js | 4 + .../input/textarea-secret.directive.js | 74 +++++++++------ .../input/textarea-secret.partial.html | 6 +- awx/ui/client/lib/models/Base.js | 95 +++++++++++-------- awx/ui/client/lib/models/Credential.js | 26 ++++- awx/ui/client/lib/models/CredentialType.js | 7 ++ awx/ui/client/lib/models/Me.js | 6 ++ 13 files changed, 211 insertions(+), 103 deletions(-) diff --git a/awx/ui/client/features/credentials/add-credentials.controller.js b/awx/ui/client/features/credentials/add-credentials.controller.js index b9d2c5ee7a..699cddb45a 100644 --- a/awx/ui/client/features/credentials/add-credentials.controller.js +++ b/awx/ui/client/features/credentials/add-credentials.controller.js @@ -5,6 +5,8 @@ function AddCredentialsController (models, $state) { let credential = models.credential; let credentialType = models.credentialType; + vm.panelTitle = 'New Credential'; + vm.form = credential.createFormSchema('post', { omit: ['user', 'team', 'inputs'] }); @@ -24,13 +26,13 @@ function AddCredentialsController (models, $state) { }; vm.form.save = data => { - data.user = me.get('results[0].id'); + data.user = me.getSelf().id; return credential.request('post', data); }; vm.form.onSaveSuccess = res => { - $state.go('credentials.edit', { credential: res.data }); + $state.go('credentials.edit', { id: res.data.id }, { reload: true }); }; } diff --git a/awx/ui/client/features/credentials/add-edit-credentials.view.html b/awx/ui/client/features/credentials/add-edit-credentials.view.html index 3e62f42611..44ca1d7025 100644 --- a/awx/ui/client/features/credentials/add-edit-credentials.view.html +++ b/awx/ui/client/features/credentials/add-edit-credentials.view.html @@ -1,5 +1,5 @@ - New Credential + {{ vm.panelTitle }} Details diff --git a/awx/ui/client/features/credentials/edit-credentials.controller.js b/awx/ui/client/features/credentials/edit-credentials.controller.js index 0887669f5d..22b1cb7681 100644 --- a/awx/ui/client/features/credentials/edit-credentials.controller.js +++ b/awx/ui/client/features/credentials/edit-credentials.controller.js @@ -4,35 +4,43 @@ function EditCredentialsController (models, $state) { let me = models.me; let credential = models.credential; let credentialType = models.credentialType; - let credentialOptions = models.credentialOptions; - vm.form = credentialOptions.createFormSchema('put', { - omit: ['user', 'team', 'inputs'], - models + vm.panelTitle = credential.get('name'); + + vm.form = credential.createFormSchema('put', { + omit: ['user', 'team', 'inputs'] }); vm.form.credential_type._data = credentialType.get('results'); - vm.form.credential_type._placeholder = 'SELECT A TYPE'; vm.form.credential_type._format = 'grouped-object'; vm.form.credential_type._display = 'name'; vm.form.credential_type._key = 'id'; vm.form.credential_type._exp = 'type as type.name group by type.kind for type in state._data'; + vm.form.credential_type._value = credentialType.getById(credential.get('credential_type')); vm.form.inputs = { - _get: credentialType.mergeInputProperties, + _get (type) { + let inputs = credentialType.mergeInputProperties(type); + + if (type.id === credential.get('credential_type')) { + inputs = credential.assignInputGroupValues(inputs); + } + + return inputs; + }, _source: vm.form.credential_type, _reference: 'vm.form.inputs', _key: 'inputs' }; vm.form.save = data => { - data.user = me.get('results[0].id'); + data.user = me.getSelf().id; - return credential.request('post', data); + return credential.request('put', data); }; vm.form.onSaveSuccess = res => { - $state.go('credentials.edit', { credential: res.data }); + $state.go('credentials', { reload: true }); }; } diff --git a/awx/ui/client/features/credentials/index.js b/awx/ui/client/features/credentials/index.js index c4bee3db68..1aa3e683c3 100644 --- a/awx/ui/client/features/credentials/index.js +++ b/awx/ui/client/features/credentials/index.js @@ -41,21 +41,21 @@ function config ($stateExtenderProvider, pathServiceProvider) { } }); - function CredentialsResolve ($q, params, Me, Credential, CredentialType) { + function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType) { + let id = $stateParams.id; + let promises = { - me: new Me('get') + me: new Me('get'), + credentialType: new CredentialType('get') }; - if (params.credential) { - promises.credential = new Credential('get', params.credential); - promises.credentialOptions = new Credential('options'); - promises.credentialType = new CredentialType('get', params.credential.credential_type); + if (id) { + promises.credential = new Credential(['get', 'options'], [id, id]); } else { promises.credential = new Credential('options'); - promises.credentialType = new CredentialType('get'); } - return $q.all(promises).then(models => models); + return $q.all(promises); } CredentialsResolve.$inject = [ @@ -86,7 +86,7 @@ function config ($stateExtenderProvider, pathServiceProvider) { stateExtender.addState({ name: 'credentials.edit', - route: '/edit/:credential', + route: '/edit/:id', ncyBreadcrumb: { label: N_('EDIT') }, diff --git a/awx/ui/client/lib/components/input/secret.directive.js b/awx/ui/client/lib/components/input/secret.directive.js index eb3e2f8169..a22273d31e 100644 --- a/awx/ui/client/lib/components/input/secret.directive.js +++ b/awx/ui/client/lib/components/input/secret.directive.js @@ -18,13 +18,45 @@ function AtInputSecretController (baseInputController) { baseInputController.call(vm, 'input', _scope_, element, form); scope = _scope_; - scope.type = 'password'; - scope.buttonText = 'SHOW'; + + if (!scope.state._value) { + scope.type = 'password'; + scope.buttonText = 'SHOW'; + + vm.toggle = vm.toggleAddState; + } else { + scope.type = 'password'; + scope.edit = true; + scope.replace = false; + scope.buttonText = 'REPLACE'; + + vm.toggle = vm.toggleEditState; + } vm.check(); }; - vm.toggle = () => { + vm.updateModel = value => { + if (!scope.edit || scope.replace) { + scope.state._value = scope.displayModel; + } + + vm.check(); + }; + + vm.toggleEditState = () => { + scope.displayModel = ''; + + if (scope.replace) { + scope.buttonText = 'REPLACE'; + } else { + scope.buttonText = 'REVERT'; + } + + scope.replace = !scope.replace; + }; + + vm.toggleAddState = () => { if (scope.type === 'password') { scope.type = 'text'; scope.buttonText = 'HIDE'; diff --git a/awx/ui/client/lib/components/input/secret.partial.html b/awx/ui/client/lib/components/input/secret.partial.html index 1589d8ce41..7320ad9afc 100644 --- a/awx/ui/client/lib/components/input/secret.partial.html +++ b/awx/ui/client/lib/components/input/secret.partial.html @@ -12,13 +12,13 @@ + ng-change="vm.updateModel()" + ng-disabled="(state._disabled || form.disabled) || (state._encrypted && !replace)" /> diff --git a/awx/ui/client/lib/components/input/select.directive.js b/awx/ui/client/lib/components/input/select.directive.js index 348480fc54..2ed8d2ef11 100644 --- a/awx/ui/client/lib/components/input/select.directive.js +++ b/awx/ui/client/lib/components/input/select.directive.js @@ -27,6 +27,10 @@ function AtInputSelectController (baseInputController, eventService) { vm.setListeners(); vm.check(); + + if (scope.state._value) { + vm.updateDisplayModel(); + } }; vm.setListeners = () => { diff --git a/awx/ui/client/lib/components/input/textarea-secret.directive.js b/awx/ui/client/lib/components/input/textarea-secret.directive.js index ad228174db..16b584292e 100644 --- a/awx/ui/client/lib/components/input/textarea-secret.directive.js +++ b/awx/ui/client/lib/components/input/textarea-secret.directive.js @@ -26,64 +26,78 @@ function AtInputTextareaSecretController (baseInputController, eventService) { textarea = element.find('textarea')[0]; container = element[0]; - scope.pre = {}; - scope.state._edit = true; - if (scope.state.format === 'ssh_private_key') { scope.ssh = true; - scope.state._hint = scope.state._hint || DEFAULT_HINT; input = element.find('input')[0]; - vm.setFileListeners(textarea, input); } - if (scope.state._edit) { + if (scope.state._value) { scope.edit = true; - scope.isShown = true; + scope.replace = false; scope.buttonText = 'REPLACE'; + } else { + scope.state._hint = scope.state._hint || DEFAULT_HINT; + vm.listeners = vm.setFileListeners(textarea, input); + } + + + vm.updateModel(); + }; + + vm.updateModel = (value) => { + if (!scope.edit || scope.replace) { + scope.state._value = scope.displayModel; } vm.check(); }; vm.setFileListeners = (textarea, input) => { - eventService.addListener(textarea, 'dragenter', event => { - event.stopPropagation(); - event.preventDefault(); - scope.$apply(() => scope.drag = true); - }); + return eventService.addListeners([ + [textarea, 'dragenter', event => { + event.stopPropagation(); + event.preventDefault(); + scope.$apply(() => scope.drag = true); + }], - eventService.addListener(input, 'dragleave', event => { - event.stopPropagation(); - event.preventDefault(); - scope.$apply(() => scope.drag = false); - }); + [input, 'dragleave', event => { + event.stopPropagation(); + event.preventDefault(); + scope.$apply(() => scope.drag = false); + }], - eventService.addListener(input, 'change', event => { - let reader = new FileReader(); + [input, 'change', event => { + let reader = new FileReader(); - reader.onload = () => vm.readFile(reader, event); - reader.readAsText(input.files[0]); - }); + reader.onload = () => vm.readFile(reader, event); + reader.readAsText(input.files[0]); + }] + ]); }; vm.readFile = (reader, event) => { scope.$apply(() => { - scope.state._value = reader.result; + scope.displayModel = reader.result; + vm.updateModel(); scope.drag = false + input.value = ''; }); }; vm.toggle = () => { - if (scope.isShown) { - scope.buttonText = 'REVERT'; - scope.pre.value = scope.state._value; - scope.state._value = undefined; - } else { - scope.state._value = scope.pre.value; + scope.displayModel = undefined; + + if (scope.replace) { scope.buttonText = 'REPLACE'; + scope.state._hint = ''; + eventService.remove(vm.listeners); + } else { + scope.buttonText = 'REVERT'; + scope.state._hint = scope.state._hint || DEFAULT_HINT; + vm.listeners = vm.setFileListeners(textarea, input); } - scope.isShown = !scope.isShown; + scope.replace = !scope.replace; }; } diff --git a/awx/ui/client/lib/components/input/textarea-secret.partial.html b/awx/ui/client/lib/components/input/textarea-secret.partial.html index 62d228bf0c..0b01e29323 100644 --- a/awx/ui/client/lib/components/input/textarea-secret.partial.html +++ b/awx/ui/client/lib/components/input/textarea-secret.partial.html @@ -17,13 +17,13 @@ type="file" name="files" /> diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index 594a8087d9..79bbebe801 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -1,40 +1,33 @@ let $http; let $q; -function request (method, ...args) { - this.method = method.toUpperCase(); +function request (method, resource) { + if (Array.isArray(method) && Array.isArray(resource)) { + let promises = method.map((value, i) => this.http[value](resource[i])); - if (typeof args[0] === 'object') { - this.res = null; - this.model = args[0]; - - return $q.resolve(); - } - - switch (this.method) { - case 'OPTIONS': - return this.httpOptions(...args); - case 'GET': - return this.httpGet(...args); - case 'POST': - return this.httpPost(...args); + return $q.all(promises); } + + return this.http[method](resource); } -function httpGet (id) { +function httpGet (resource) { let req = { method: 'GET', url: this.path }; - if (id) { - req.url = `${this.path}/${id}`; + if (typeof resource === 'object') { + this.model[this.method] = resource; + + return $q.resolve(); + } else if (resource) { + req.url = `${this.path}/${resource}`; } return $http(req) .then(res => { - this.res = res; - this.model = res.data; + this.model.GET = res.data; return res; }); @@ -47,33 +40,56 @@ function httpPost (data) { data }; - return $http(req) - .then(res => { - this.res = res; - this.model = res.data; - - return res; - }); + return $http(req).then(res => res); } -function httpOptions () { +function httpPut (changes) { + let model = Object.assign(this.get(), changes); + + let req = { + method: 'PUT', + url: `${this.path}${model.id}/`, + data: model + }; + + return $http(req).then(res => res); +} + +function httpOptions (resource) { let req = { method: 'OPTIONS', url: this.path }; + if (resource) { + req.url = `${this.path}/${resource}`; + } + return $http(req) .then(res => { - this.res = res; - this.model = res.data; + this.model.OPTIONS = res.data; return res; }); } -function get (_keys_) { - let keys = _keys_.split('.'); - let value = this.model; +function get (method, keys) { + let model; + + if (keys) { + model = this.model[method.toUpperCase()]; + } else { + model = this.model.GET; + keys = method; + } + + if (!keys) { + return model; + } + + keys = keys.split('.'); + + let value = model; try { keys.forEach(key => { @@ -109,14 +125,17 @@ function normalizePath (resource) { } function BaseModel (path) { + this.model = {}; this.get = get; - this.httpGet = httpGet; - this.httpOptions = httpOptions; - this.httpPost = httpPost; this.normalizePath = normalizePath; this.request = request; + this.http = { + get: httpGet.bind(this), + options: httpOptions.bind(this), + post: httpPost.bind(this), + put: httpPut.bind(this) + }; - this.model = {}; this.path = this.normalizePath(path); }; diff --git a/awx/ui/client/lib/models/Credential.js b/awx/ui/client/lib/models/Credential.js index c99c0f0099..371d2ad6e9 100644 --- a/awx/ui/client/lib/models/Credential.js +++ b/awx/ui/client/lib/models/Credential.js @@ -1,9 +1,9 @@ +const ENCRYPTED_VALUE = '$encrypted$'; + let BaseModel; function createFormSchema (method, config) { - method = method.toUpperCase(); - - let schema = Object.assign({}, this.get(`actions.${method}`)); + let schema = Object.assign({}, this.get('options', `actions.${method.toUpperCase()}`)); if (config && config.omit) { config.omit.forEach(key => { @@ -13,17 +13,33 @@ function createFormSchema (method, config) { for (let key in schema) { schema[key].id = key; + + if (method === 'put') { + schema[key]._value = this.get(key); + } } return schema; } -function CredentialModel (method, id) { +function assignInputGroupValues (inputs) { + return inputs.map(input => { + let value = this.get(`inputs.${input.id}`); + + input._value = value; + input._encrypted = value === ENCRYPTED_VALUE; + + return input; + }); +} + +function CredentialModel (method, resource) { BaseModel.call(this, 'credentials'); this.createFormSchema = createFormSchema.bind(this); + this.assignInputGroupValues = assignInputGroupValues.bind(this); - return this.request(method, id) + return this.request(method, resource) .then(() => this); } diff --git a/awx/ui/client/lib/models/CredentialType.js b/awx/ui/client/lib/models/CredentialType.js index 2679e191d7..b03a2fd1f0 100644 --- a/awx/ui/client/lib/models/CredentialType.js +++ b/awx/ui/client/lib/models/CredentialType.js @@ -26,11 +26,18 @@ function mergeInputProperties (type) { }); } +function getById (id) { + let type = this.get('results').filter(type => type.id === id); + + return type ? type[0] : undefined; +} + function CredentialTypeModel (method, id) { BaseModel.call(this, 'credential_types'); this.categorizeByKind = categorizeByKind.bind(this); this.mergeInputProperties = mergeInputProperties.bind(this); + this.getById = getById.bind(this); return this.request(method, id) .then(() => this); diff --git a/awx/ui/client/lib/models/Me.js b/awx/ui/client/lib/models/Me.js index ddb05bcb09..2a36a5b2be 100644 --- a/awx/ui/client/lib/models/Me.js +++ b/awx/ui/client/lib/models/Me.js @@ -1,8 +1,14 @@ let BaseModel; +function getSelf () { + return this.get('results[0]'); +} + function MeModel (method) { BaseModel.call(this, 'me'); + this.getSelf = getSelf.bind(this); + return this.request(method) .then(() => this); }