mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	UI - make engine list more consistent with the auth method list (#4598)
* remove expanding behavior from engines list and add a configuration route * use page header component, secret tab component for the template on the secret engine configuration route * move abstraction to secret-list-header and remove secret-tabs * add attrs to secret engine model and adjust mount controller code to support that * fix top level nav so that we can use the back button properly * fix tests
This commit is contained in:
		
							
								
								
									
										5
									
								
								ui/app/components/page-header-level-left.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ui/app/components/page-header-level-left.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | import Ember from 'ember'; | ||||||
|  |  | ||||||
|  | export default Ember.Component.extend({ | ||||||
|  |   tagName: '', | ||||||
|  | }); | ||||||
							
								
								
									
										5
									
								
								ui/app/components/page-header-level-right.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ui/app/components/page-header-level-right.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | import Ember from 'ember'; | ||||||
|  |  | ||||||
|  | export default Ember.Component.extend({ | ||||||
|  |   tagName: '', | ||||||
|  | }); | ||||||
							
								
								
									
										5
									
								
								ui/app/components/page-header-top.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ui/app/components/page-header-top.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | import Ember from 'ember'; | ||||||
|  |  | ||||||
|  | export default Ember.Component.extend({ | ||||||
|  |   tagName: '', | ||||||
|  | }); | ||||||
							
								
								
									
										6
									
								
								ui/app/components/page-header.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ui/app/components/page-header.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | import Ember from 'ember'; | ||||||
|  |  | ||||||
|  | export default Ember.Component.extend({ | ||||||
|  |   tagName: '', | ||||||
|  |   hasLevel: true, | ||||||
|  | }); | ||||||
							
								
								
									
										14
									
								
								ui/app/components/secret-list-header.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ui/app/components/secret-list-header.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | import Ember from 'ember'; | ||||||
|  |  | ||||||
|  | export default Ember.Component.extend({ | ||||||
|  |   tagName: '', | ||||||
|  |  | ||||||
|  |   // api | ||||||
|  |   isCertTab: false, | ||||||
|  |   isConfigure: false, | ||||||
|  |   baseKey: null, | ||||||
|  |   backendCrumb: null, | ||||||
|  |   model: null, | ||||||
|  |  | ||||||
|  |  | ||||||
|  | }); | ||||||
| @@ -30,7 +30,6 @@ export default Ember.Controller.extend({ | |||||||
|   description: null, |   description: null, | ||||||
|   default_lease_ttl: null, |   default_lease_ttl: null, | ||||||
|   max_lease_ttl: null, |   max_lease_ttl: null, | ||||||
|   force_no_cache: null, |  | ||||||
|   showConfig: false, |   showConfig: false, | ||||||
|   local: false, |   local: false, | ||||||
|   sealWrap: false, |   sealWrap: false, | ||||||
| @@ -50,7 +49,6 @@ export default Ember.Controller.extend({ | |||||||
|       description: null, |       description: null, | ||||||
|       default_lease_ttl: null, |       default_lease_ttl: null, | ||||||
|       max_lease_ttl: null, |       max_lease_ttl: null, | ||||||
|       force_no_cache: null, |  | ||||||
|       local: false, |       local: false, | ||||||
|       showConfig: false, |       showConfig: false, | ||||||
|       sealWrap: false, |       sealWrap: false, | ||||||
| @@ -82,7 +80,6 @@ export default Ember.Controller.extend({ | |||||||
|         selectedType: type, |         selectedType: type, | ||||||
|         description, |         description, | ||||||
|         default_lease_ttl, |         default_lease_ttl, | ||||||
|         force_no_cache, |  | ||||||
|         local, |         local, | ||||||
|         max_lease_ttl, |         max_lease_ttl, | ||||||
|         sealWrap, |         sealWrap, | ||||||
| @@ -92,7 +89,6 @@ export default Ember.Controller.extend({ | |||||||
|         'selectedType', |         'selectedType', | ||||||
|         'description', |         'description', | ||||||
|         'default_lease_ttl', |         'default_lease_ttl', | ||||||
|         'force_no_cache', |  | ||||||
|         'local', |         'local', | ||||||
|         'max_lease_ttl', |         'max_lease_ttl', | ||||||
|         'sealWrap', |         'sealWrap', | ||||||
| @@ -112,9 +108,8 @@ export default Ember.Controller.extend({ | |||||||
|  |  | ||||||
|       if (this.get('showConfig')) { |       if (this.get('showConfig')) { | ||||||
|         attrs.config = { |         attrs.config = { | ||||||
|           default_lease_ttl, |           defaultLeaseTtl: default_lease_ttl, | ||||||
|           max_lease_ttl, |           maxLeaseTtl: max_lease_ttl, | ||||||
|           force_no_cache, |  | ||||||
|         }; |         }; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,5 +2,7 @@ import attr from 'ember-data/attr'; | |||||||
| import Fragment from 'ember-data-model-fragments/fragment'; | import Fragment from 'ember-data-model-fragments/fragment'; | ||||||
|  |  | ||||||
| export default Fragment.extend({ | export default Fragment.extend({ | ||||||
|   version: attr('number'), |   version: attr('number', { | ||||||
|  |     label: 'Version', | ||||||
|  |   }), | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ import DS from 'ember-data'; | |||||||
| import { queryRecord } from 'ember-computed-query'; | import { queryRecord } from 'ember-computed-query'; | ||||||
| import { fragment } from 'ember-data-model-fragments/attributes'; | import { fragment } from 'ember-data-model-fragments/attributes'; | ||||||
|  |  | ||||||
|  | import { expandAttributeMeta } from 'vault/utils/field-to-attrs'; | ||||||
|  |  | ||||||
| const { attr } = DS; | const { attr } = DS; | ||||||
| const { computed } = Ember; | const { computed } = Ember; | ||||||
|  |  | ||||||
| @@ -16,11 +18,26 @@ export default DS.Model.extend({ | |||||||
|   name: attr('string'), |   name: attr('string'), | ||||||
|   type: attr('string'), |   type: attr('string'), | ||||||
|   description: attr('string'), |   description: attr('string'), | ||||||
|   config: attr('object'), |   config: fragment('mount-config', { defaultValue: {} }), | ||||||
|   options: fragment('mount-options'), |   options: fragment('mount-options', { defaultValue: {} }), | ||||||
|   local: attr('boolean'), |   local: attr('boolean'), | ||||||
|   sealWrap: attr('boolean'), |   sealWrap: attr('boolean'), | ||||||
|  |  | ||||||
|  |   formFields: [ | ||||||
|  |     'type', | ||||||
|  |     'path', | ||||||
|  |     'description', | ||||||
|  |     'accessor', | ||||||
|  |     'local', | ||||||
|  |     'sealWrap', | ||||||
|  |     'config.{defaultLeaseTtl,maxLeaseTtl}', | ||||||
|  |     'options.{version}', | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   attrs: computed('formFields', function() { | ||||||
|  |     return expandAttributeMeta(this, this.get('formFields')); | ||||||
|  |   }), | ||||||
|  |  | ||||||
|   shouldIncludeInList: computed('type', function() { |   shouldIncludeInList: computed('type', function() { | ||||||
|     return !LIST_EXCLUDED_BACKENDS.includes(this.get('type')); |     return !LIST_EXCLUDED_BACKENDS.includes(this.get('type')); | ||||||
|   }), |   }), | ||||||
|   | |||||||
| @@ -68,6 +68,7 @@ Router.map(function() { | |||||||
|         this.route('backends', { path: '/' }); |         this.route('backends', { path: '/' }); | ||||||
|         this.route('backend', { path: '/:backend' }, function() { |         this.route('backend', { path: '/:backend' }, function() { | ||||||
|           this.route('index', { path: '/' }); |           this.route('index', { path: '/' }); | ||||||
|  |           this.route('configuration'); | ||||||
|           // because globs / params can't be empty, |           // because globs / params can't be empty, | ||||||
|           // we have to special-case ids of '' with thier own routes |           // we have to special-case ids of '' with thier own routes | ||||||
|           this.route('list-root', { path: '/list/' }); |           this.route('list-root', { path: '/list/' }); | ||||||
|   | |||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | import Ember from 'ember'; | ||||||
|  |  | ||||||
|  | export default Ember.Route.extend({ | ||||||
|  |   model() { | ||||||
|  |     return this.modelFor('vault.cluster.secrets.backend'); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -20,8 +20,7 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
| .popup-menu-trigger { | .popup-menu-trigger { | ||||||
|   width: 3rem; |   min-width: auto; | ||||||
|   height: 2rem; |  | ||||||
| } | } | ||||||
| .popup-menu-trigger.is-active { | .popup-menu-trigger.is-active { | ||||||
|   &, |   &, | ||||||
|   | |||||||
| @@ -146,7 +146,6 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12); | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   &.is-more-icon, |  | ||||||
|   &.tool-tip-trigger { |   &.tool-tip-trigger { | ||||||
|     color: $black; |     color: $black; | ||||||
|     min-width: auto; |     min-width: auto; | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ | |||||||
|           {{/if}} |           {{/if}} | ||||||
|         </li> |         </li> | ||||||
|         <li class="{{if (is-active-route (array 'vault.cluster.policies' 'vault.cluster.policy')) 'is-active'}}"> |         <li class="{{if (is-active-route (array 'vault.cluster.policies' 'vault.cluster.policy')) 'is-active'}}"> | ||||||
|           <a href="{{href-to "vault.cluster.policies" activeClusterName current-when='vault.cluster.policies vault.cluster.policy'}}"> |           <a href="{{href-to "vault.cluster.policies" "acl" current-when='vault.cluster.policies vault.cluster.policy'}}"> | ||||||
|             Policies |             Policies | ||||||
|           </a> |           </a> | ||||||
|         </li> |         </li> | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| <ul> | <ul> | ||||||
|  |   {{yield}} | ||||||
|   {{#each secretPath as |path index|}} |   {{#each secretPath as |path index|}} | ||||||
|     <li class="{{if (is-active-route path.path path.model isExact=true) 'is-active'}}"> |     <li class="{{if (is-active-route path.path path.model isExact=true) 'is-active'}}"> | ||||||
|       <span class="sep">/</span> |       <span class="sep">/</span> | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								ui/app/templates/components/page-header-level-left.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ui/app/templates/components/page-header-level-left.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | {{yield}} | ||||||
							
								
								
									
										1
									
								
								ui/app/templates/components/page-header-level-right.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ui/app/templates/components/page-header-level-right.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | {{yield}} | ||||||
							
								
								
									
										15
									
								
								ui/app/templates/components/page-header.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								ui/app/templates/components/page-header.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | <header class="page-header"> | ||||||
|  |   {{#if hasLevel}} | ||||||
|  |     {{yield (hash top=(component 'page-header-top'))}} | ||||||
|  |     <div class="level"> | ||||||
|  |       <div class="level-left"> | ||||||
|  |         {{yield (hash levelLeft=(component 'page-header-level-left'))}} | ||||||
|  |       </div> | ||||||
|  |       <div class="level-right field is-grouped"> | ||||||
|  |         {{yield (hash levelRight=(component 'page-header-level-right'))}} | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   {{else}} | ||||||
|  |     {{yield (hash top=(component 'page-header-top'))}} | ||||||
|  |   {{/if}} | ||||||
|  | </header> | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| {{#basic-dropdown class="popup-menu" horizontalPosition="auto-right" verticalPosition="below" onOpen=onOpen as |d|}} | {{#basic-dropdown class="popup-menu" horizontalPosition="auto-right" verticalPosition="below" onOpen=onOpen as |d|}} | ||||||
|   {{#d.trigger tagName="button" class=(concat "popup-menu-trigger button is-transparent " (if d.isOpen "is-active")) data-test-popup-menu-trigger=true}} |   {{#d.trigger tagName="button" class=(concat "popup-menu-trigger button is-ghost " (if d.isOpen "is-active")) data-test-popup-menu-trigger=true}} | ||||||
|     {{i-con |     {{i-con | ||||||
|       glyph="more" |       glyph="more" | ||||||
|       class="has-text-black auto-width" |       class="has-text-black auto-width" | ||||||
|   | |||||||
							
								
								
									
										108
									
								
								ui/app/templates/components/secret-list-header.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								ui/app/templates/components/secret-list-header.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | {{#with (options-for-backend model.type) as |options|}} | ||||||
|  |   {{#page-header as |p|}} | ||||||
|  |     {{#p.top}} | ||||||
|  |       {{#key-value-header | ||||||
|  |         baseKey=baseKey | ||||||
|  |         path="vault.cluster.secrets.backend.list" | ||||||
|  |         root=backendCrumb | ||||||
|  |       }} | ||||||
|  |         <li> | ||||||
|  |           <span class="sep">/</span> | ||||||
|  |           <a href={{href-to "vault.cluster.secrets"}}> | ||||||
|  |             secrets | ||||||
|  |           </a> | ||||||
|  |         </li> | ||||||
|  |       {{/key-value-header}} | ||||||
|  |     {{/p.top}} | ||||||
|  |     {{#p.levelLeft}} | ||||||
|  |       <h1 class="title is-3"> | ||||||
|  |         {{model.id}} | ||||||
|  |         <span class="tag is-outlined is-inverted has-text-grey-dark is-font-mono"> | ||||||
|  |           {{or options.displayName (capitalize model.type)}} | ||||||
|  |         </span> | ||||||
|  |         {{#if (eq model.options.version 2)}} | ||||||
|  |           <span class="has-text-grey-dark has-text-weight-normal is-size-6"> | ||||||
|  |             Version 2 | ||||||
|  |           </span> | ||||||
|  |         {{/if}} | ||||||
|  |       </h1> | ||||||
|  |     {{/p.levelLeft}} | ||||||
|  |     {{#p.levelRight}} | ||||||
|  |        {{#unless (or isCertTab isConfigure)}} | ||||||
|  |           <div class="control"> | ||||||
|  |             {{#secret-link | ||||||
|  |               mode="create" | ||||||
|  |               secret=(or baseKey.id '') | ||||||
|  |               queryParams=(query-params initialKey='') | ||||||
|  |               class="button has-icon-right is-ghost is-compact" | ||||||
|  |               data-test-secret-create=true | ||||||
|  |             }} | ||||||
|  |               {{options.create}} | ||||||
|  |               {{i-con glyph="chevron-right" size=11}} | ||||||
|  |             {{/secret-link}} | ||||||
|  |           </div> | ||||||
|  |         {{/unless}} | ||||||
|  |       {{#if (or (eq model.type "aws") (eq model.type "ssh") (eq model.type "pki"))}} | ||||||
|  |         <div class="control"> | ||||||
|  |           <a href={{href-to | ||||||
|  |               "vault.cluster.settings.configure-secret-backend" | ||||||
|  |               model.id | ||||||
|  |             }} | ||||||
|  |             class="button has-icon-right is-ghost is-compact" | ||||||
|  |             data-test-secret-backend-configure=true | ||||||
|  |             > | ||||||
|  |               Configure {{i-con glyph="chevron-right" size=11}} | ||||||
|  |           </a> | ||||||
|  |         </div> | ||||||
|  |       {{/if}} | ||||||
|  |     {{/p.levelRight}} | ||||||
|  |   {{/page-header}} | ||||||
|  |   {{#if options.tabs}} | ||||||
|  |     <div class="box is-bottomless is-marginless is-fullwidth is-paddingless"> | ||||||
|  |       <nav class="tabs sub-nav"> | ||||||
|  |         <ul> | ||||||
|  |           {{#each options.tabs as |oTab|}} | ||||||
|  |             {{#if oTab.tab}} | ||||||
|  |               {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab=oTab.tab) tagName="li" activeClass="is-active" data-test-tab=oTab.label}} | ||||||
|  |                 {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab=oTab.tab)}} | ||||||
|  |                   {{oTab.label}} | ||||||
|  |                 {{/link-to}} | ||||||
|  |               {{/link-to}} | ||||||
|  |             {{else}} | ||||||
|  |               {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab='') tagName="li" activeClass="is-active" data-test-tab=oTab.label}} | ||||||
|  |                 {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab='')}} | ||||||
|  |                   {{oTab.label}} | ||||||
|  |                 {{/link-to}} | ||||||
|  |               {{/link-to}} | ||||||
|  |             {{/if}} | ||||||
|  |           {{/each}} | ||||||
|  |           {{#link-to 'vault.cluster.secrets.backend.configuration' tagName="li" activeClass="is-active"}} | ||||||
|  |             {{#link-to 'vault.cluster.secrets.backend.configuration' }} | ||||||
|  |               Configuration | ||||||
|  |             {{/link-to}} | ||||||
|  |           {{/link-to}} | ||||||
|  |         </ul> | ||||||
|  |       </nav> | ||||||
|  |     </div> | ||||||
|  |   {{else}} | ||||||
|  |     {{!-- if there are no tabs in the options, we'll hardcode them here --}} | ||||||
|  |     <div class="box is-bottomless is-marginless is-fullwidth is-paddingless"> | ||||||
|  |       <nav class="tabs sub-nav"> | ||||||
|  |         <ul> | ||||||
|  |           {{#if (contains model.type (supported-secret-backends))}} | ||||||
|  |             {{#link-to 'vault.cluster.secrets.backend.list-root' tagName="li" activeClass="is-active" current-when="vault.cluster.secrets.backend.list-root vault.cluster.secrets.backend.list"}} | ||||||
|  |               {{#link-to 'vault.cluster.secrets.backend.list-root'}} | ||||||
|  |                 {{capitalize (pluralize options.item)}} | ||||||
|  |               {{/link-to}} | ||||||
|  |             {{/link-to}} | ||||||
|  |           {{/if}} | ||||||
|  |           {{#link-to 'vault.cluster.secrets.backend.configuration' tagName="li" activeClass="is-active"}} | ||||||
|  |             {{#link-to 'vault.cluster.secrets.backend.configuration' }} | ||||||
|  |               Configuration | ||||||
|  |             {{/link-to}} | ||||||
|  |           {{/link-to}} | ||||||
|  |         </ul> | ||||||
|  |       </nav> | ||||||
|  |     </div> | ||||||
|  |   {{/if}} | ||||||
|  | {{/with}} | ||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | {{secret-list-header model=model isConfigure=true backendCrumb=(hash label=model.id text=model.id path="vault.cluster.secrets.backend.list-root" model=model.id)}} | ||||||
|  |  | ||||||
|  | <div class="box is-fullwidth is-sideless is-paddingless is-marginless"> | ||||||
|  |   {{#each model.attrs as |attr|}} | ||||||
|  |     {{#if (eq attr.type "object")}} | ||||||
|  |       {{info-table-row | ||||||
|  |         alwaysRender=true | ||||||
|  |         label=(or attr.options.label (to-label attr.name))  | ||||||
|  |         value=(stringify (get model attr.name)) | ||||||
|  |       }} | ||||||
|  |     {{else}} | ||||||
|  |       {{info-table-row | ||||||
|  |         alwaysRender=(not-eq attr.name 'options.version') | ||||||
|  |         label=(or attr.options.label (to-label attr.name)) | ||||||
|  |         value=(get model attr.name) | ||||||
|  |       }} | ||||||
|  |     {{/if}} | ||||||
|  |   {{/each}} | ||||||
|  | </div> | ||||||
| @@ -1,79 +1,6 @@ | |||||||
|  | {{secret-list-header isCertTab=(eq tab "certs") model=backendModel baseKey=baseKey backendCrumb=backendCrumb}} | ||||||
|  |  | ||||||
| {{#with (options-for-backend backendType tab) as |options|}} | {{#with (options-for-backend backendType tab) as |options|}} | ||||||
|   <header class="page-header"> |  | ||||||
|     {{key-value-header |  | ||||||
|       baseKey=baseKey |  | ||||||
|       path="vault.cluster.secrets.backend.list" |  | ||||||
|       root=backendCrumb |  | ||||||
|     }} |  | ||||||
|     <div class="level"> |  | ||||||
|       <div class="level-left"> |  | ||||||
|         <h1 class="title is-3"> |  | ||||||
|           {{backend}} |  | ||||||
|           <span class="tag is-outlined is-inverted has-text-grey-dark is-font-mono"> |  | ||||||
|             {{or options.displayName (capitalize backendType)}} |  | ||||||
|           </span> |  | ||||||
|           {{#if (eq backendModel.options.version 2)}} |  | ||||||
|             <span class="has-text-grey-dark has-text-weight-normal is-size-6"> |  | ||||||
|               Version 2 |  | ||||||
|             </span> |  | ||||||
|           {{/if}} |  | ||||||
|         </h1> |  | ||||||
|       </div> |  | ||||||
|       <div class="level-right field is-grouped"> |  | ||||||
|         {{#if (not-eq tab "certs")}} |  | ||||||
|           <div class="control"> |  | ||||||
|             {{#secret-link |  | ||||||
|               mode="create" |  | ||||||
|               secret=(or baseKey.id '') |  | ||||||
|               queryParams=(query-params initialKey='') |  | ||||||
|               class="button has-icon-right is-ghost is-compact" |  | ||||||
|               data-test-secret-create=true |  | ||||||
|             }} |  | ||||||
|               {{options.create}} |  | ||||||
|               {{i-con glyph="chevron-right" size=11}} |  | ||||||
|             {{/secret-link}} |  | ||||||
|           </div> |  | ||||||
|         {{/if}} |  | ||||||
|         {{#if (or (eq backendType "aws") (eq backendType "ssh") (eq backendType "pki"))}} |  | ||||||
|           <div class="control"> |  | ||||||
|             <a href={{href-to |  | ||||||
|               "vault.cluster.settings.configure-secret-backend" |  | ||||||
|               backend |  | ||||||
|               }} |  | ||||||
|               class="button has-icon-right is-ghost is-compact" |  | ||||||
|               data-test-secret-backend-configure=true |  | ||||||
|               > |  | ||||||
|               Configure |  | ||||||
|               {{i-con glyph="chevron-right" size=11}} |  | ||||||
|             </a> |  | ||||||
|           </div> |  | ||||||
|         {{/if}} |  | ||||||
|       </div> |  | ||||||
|     </div> |  | ||||||
|   </header> |  | ||||||
|   {{#if options.tabs}} |  | ||||||
|     <div class="box is-bottomless is-marginless is-fullwidth is-paddingless"> |  | ||||||
|       <nav class="tabs sub-nav"> |  | ||||||
|         <ul> |  | ||||||
|           {{#each options.tabs as |oTab|}} |  | ||||||
|             {{#if oTab.tab}} |  | ||||||
|               {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab=oTab.tab) tagName="li" activeClass="is-active" data-test-tab=oTab.label}} |  | ||||||
|                 {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab=oTab.tab)}} |  | ||||||
|                   {{oTab.label}} |  | ||||||
|                 {{/link-to}} |  | ||||||
|               {{/link-to}} |  | ||||||
|             {{else}} |  | ||||||
|               {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab='') tagName="li" activeClass="is-active" data-test-tab=oTab.label}} |  | ||||||
|                 {{#link-to 'vault.cluster.secrets.backend.list-root' (query-params tab='')}} |  | ||||||
|                   {{oTab.label}} |  | ||||||
|                 {{/link-to}} |  | ||||||
|               {{/link-to}} |  | ||||||
|             {{/if}} |  | ||||||
|           {{/each}} |  | ||||||
|         </ul> |  | ||||||
|       </nav> |  | ||||||
|     </div> |  | ||||||
|   {{/if}} |  | ||||||
|   <div class="box is-sideless has-background-grey-lighter has-short-padding is-marginless"> |   <div class="box is-sideless has-background-grey-lighter has-short-padding is-marginless"> | ||||||
|     <div class="level"> |     <div class="level"> | ||||||
|       <div class="level-left"> |       <div class="level-left"> | ||||||
| @@ -90,17 +17,15 @@ | |||||||
|            mode=(if (eq tab 'certs') 'secrets-cert' 'secrets') |            mode=(if (eq tab 'certs') 'secrets-cert' 'secrets') | ||||||
|          }} |          }} | ||||||
|          {{#if filterFocused}} |          {{#if filterFocused}} | ||||||
|              |  | ||||||
|              |  | ||||||
|           {{#if filterMatchesKey}} |           {{#if filterMatchesKey}} | ||||||
|             {{#unless filterIsFolder}} |             {{#unless filterIsFolder}} | ||||||
|               <p class="help has-text-grey is-size-8"> |               <p class="input-hint"> | ||||||
|                 <kbd>Enter</kbd> to view {{filter}} |                 <kbd>Enter</kbd> to view {{filter}} | ||||||
|               </p> |               </p> | ||||||
|             {{/unless}} |             {{/unless}} | ||||||
|           {{/if}} |           {{/if}} | ||||||
|           {{#if firstPartialMatch}} |           {{#if firstPartialMatch}} | ||||||
|             <p class="help has-text-grey is-size-8"> |             <p class="input-hint"> | ||||||
|               <kbd>Tab</kbd> to autocomplete |               <kbd>Tab</kbd> to autocomplete | ||||||
|             </p> |             </p> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|   | |||||||
| @@ -18,11 +18,8 @@ | |||||||
|   {{#linked-block |   {{#linked-block | ||||||
|     "vault.cluster.secrets.backend.list-root" |     "vault.cluster.secrets.backend.list-root" | ||||||
|     backend.id |     backend.id | ||||||
|     class=(concat |     class="box is-sideless is-marginless has-pointer" | ||||||
|       'box is-sideless is-marginless has-pointer ' |     data-test-secret-backend-row=backend.id | ||||||
|       (if (get this (concat backend.accessor '-open')) 'has-background-white-bis') |  | ||||||
|     ) |  | ||||||
|     data-test-secret-backend-link=backend.id |  | ||||||
|   }} |   }} | ||||||
|     <div class="level is-mobile"> |     <div class="level is-mobile"> | ||||||
|       <div class="level-left"> |       <div class="level-left"> | ||||||
| @@ -53,25 +50,35 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="level-right is-flex is-paddingless is-marginless"> |       <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|         <div class="level-item"> |         <div class="level-item"> | ||||||
|           <button class="button is-more-icon is-ghost" data-test-secret-backend-detail=true type="button" {{action (toggle (concat backend.accessor '-open') this)}}> |           {{#popup-menu name="engine-menu"}} | ||||||
|             {{i-con glyph="more" size=16 aria-label=(concat backend.path ' details')}} |              <nav class="menu"> | ||||||
|           </button> |                <ul class="menu-list"> | ||||||
|  |                  <li class="action"> | ||||||
|  |                    <a href="{{href-to 'vault.cluster.secrets.backend.configuration' backend.id}}"> | ||||||
|  |                      View Configuration | ||||||
|  |                    </a> | ||||||
|  |                   </li> | ||||||
|  |                  {{#if item.updatePath.isPending}} | ||||||
|  |                    <li class="action"> | ||||||
|  |                      <button disabled=true type="button" class="link button is-loading is-transparent"></button> | ||||||
|  |                     </li> | ||||||
|  |                  {{/if}} | ||||||
|  |               </ul> | ||||||
|  |             </nav> | ||||||
|  |           {{/popup-menu}} | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     {{#if (get this (concat backend.accessor '-open'))}} |  | ||||||
|       {{partial "partials/backend-details"}} |  | ||||||
|     {{/if}} |  | ||||||
|   {{/linked-block}} |   {{/linked-block}} | ||||||
| {{/each}} | {{/each}} | ||||||
| {{#each unsupportedBackends as |backend|}} | {{#each unsupportedBackends as |backend|}} | ||||||
|   <div class="box is-sideless is-marginless has-background-transition {{if (get this (concat backend.accessor '-open')) 'has-background-white-bis'}}" |   <div class="box is-sideless is-marginless" | ||||||
|        data-test-secret-backend-row={{backend.id}} |        data-test-secret-backend-row={{backend.id}} | ||||||
|     > |     > | ||||||
|     <div class="level is-mobile"> |     <div class="level is-mobile"> | ||||||
|       <div class="level-left"> |       <div class="level-left"> | ||||||
|         <div> |         <div> | ||||||
|           <div class="has-text-weight-semibold"> |           <div data-test-secret-path class="has-text-weight-semibold"> | ||||||
|             {{i-con glyph="folder" size=14 class="has-text-grey-light"}} {{backend.path}} |             {{i-con glyph="folder" size=14 class="has-text-grey-light"}} {{backend.path}} | ||||||
|           </div> |           </div> | ||||||
|           <span class="tag"> |           <span class="tag"> | ||||||
| @@ -90,14 +97,19 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="level-right is-flex is-paddingless is-marginless"> |       <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|         <div class="level-item"> |         <div class="level-item"> | ||||||
|           <button class="button is-more-icon is-ghost" data-test-secret-backend-detail=true type="button" {{action (toggle (concat backend.accessor '-open') this)}}> |           {{#popup-menu name="engine-menu"}} | ||||||
|             {{i-con glyph="more" size=16 aria-label=(concat backend.path ' details')}} |              <nav class="menu"> | ||||||
|           </button> |                <ul class="menu-list"> | ||||||
|  |                  <li class="action"> | ||||||
|  |                    <a href="{{href-to 'vault.cluster.secrets.backend.configuration' backend.id}}" data-test-engine-config> | ||||||
|  |                      View Configuration | ||||||
|  |                    </a> | ||||||
|  |                  </li> | ||||||
|  |                </ul> | ||||||
|  |             </nav> | ||||||
|  |           {{/popup-menu}} | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     {{#if (get this (concat backend.accessor '-open'))}} |  | ||||||
|       {{partial "partials/backend-details"}} |  | ||||||
|     {{/if}} |  | ||||||
|   </div> |   </div> | ||||||
| {{/each}} | {{/each}} | ||||||
|   | |||||||
| @@ -42,11 +42,12 @@ export const expandAttributeMeta = function(modelClass, attributeNames, namePref | |||||||
|   let attributeMap = map || new Map(); |   let attributeMap = map || new Map(); | ||||||
|   modelClass.eachAttribute((name, meta) => { |   modelClass.eachAttribute((name, meta) => { | ||||||
|     let fieldName = namePrefix ? namePrefix + name : name; |     let fieldName = namePrefix ? namePrefix + name : name; | ||||||
|     if (meta.isFragment) { |     let maybeFragment = Ember.get(modelClass, fieldName); | ||||||
|  |     if (meta.isFragment && maybeFragment) { | ||||||
|       // pass the fragment and all fields that start with |       // pass the fragment and all fields that start with | ||||||
|       // the fragment name down to get extracted from the Fragment |       // the fragment name down to get extracted from the Fragment | ||||||
|       expandAttributeMeta( |       expandAttributeMeta( | ||||||
|         Ember.get(modelClass, fieldName), |         maybeFragment, | ||||||
|         fields.filter(f => f.startsWith(fieldName)), |         fields.filter(f => f.startsWith(fieldName)), | ||||||
|         fieldName + '.', |         fieldName + '.', | ||||||
|         attributeMap |         attributeMap | ||||||
| @@ -60,13 +61,15 @@ export const expandAttributeMeta = function(modelClass, attributeNames, namePref | |||||||
|   // so we'll replace each key in `fields` with the expanded meta |   // so we'll replace each key in `fields` with the expanded meta | ||||||
|   fields = fields.map(field => { |   fields = fields.map(field => { | ||||||
|     let meta = attributeMap.get(field); |     let meta = attributeMap.get(field); | ||||||
|     const { type, options } = meta; |     if (meta) { | ||||||
|  |       var { type, options } = meta; | ||||||
|  |     } | ||||||
|     return { |     return { | ||||||
|       // using field name here because it is the full path, |       // using field name here because it is the full path, | ||||||
|       // name on the attribute meta will be relative to the fragment it's on |       // name on the attribute meta will be relative to the fragment it's on | ||||||
|       name: field, |       name: field, | ||||||
|       type, |       type: type, | ||||||
|       options, |       options: options, | ||||||
|     }; |     }; | ||||||
|   }); |   }); | ||||||
|   return fields; |   return fields; | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import { test } from 'qunit'; | import { test } from 'qunit'; | ||||||
| import moduleForAcceptance from 'vault/tests/helpers/module-for-acceptance'; | import moduleForAcceptance from 'vault/tests/helpers/module-for-acceptance'; | ||||||
|  | import backendListPage from 'vault/tests/pages/secrets/backends'; | ||||||
|  |  | ||||||
| moduleForAcceptance('Acceptance | settings', { | moduleForAcceptance('Acceptance | settings', { | ||||||
|   beforeEach() { |   beforeEach() { | ||||||
| @@ -36,14 +37,19 @@ test('settings', function(assert) { | |||||||
|       find('[data-test-flash-message]').text().trim(), |       find('[data-test-flash-message]').text().trim(), | ||||||
|       `Successfully mounted '${type}' at '${path}'!` |       `Successfully mounted '${type}' at '${path}'!` | ||||||
|     ); |     ); | ||||||
|  |     let row = backendListPage.rows().findByPath(path); | ||||||
|  |     row.menu(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   andThen(() => { | ||||||
|  |     backendListPage.configLink(); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   //show mount details |  | ||||||
|   click(`[data-test-secret-backend-row="${path}"] [data-test-secret-backend-detail]`); |  | ||||||
|   andThen(() => { |   andThen(() => { | ||||||
|     assert.ok( |     assert.ok( | ||||||
|       find('[data-test-secret-backend-details="default-ttl"]').text().match(/100/), |       currentURL(), | ||||||
|       'displays the input ttl' |       '/vault/secrets/${path}/configuration', | ||||||
|  |       'navigates to the config page' | ||||||
|     ); |     ); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import { test } from 'qunit'; | import { test } from 'qunit'; | ||||||
| import moduleForAcceptance from 'vault/tests/helpers/module-for-acceptance'; | import moduleForAcceptance from 'vault/tests/helpers/module-for-acceptance'; | ||||||
| import page from 'vault/tests/pages/settings/mount-secret-backend'; | import page from 'vault/tests/pages/settings/mount-secret-backend'; | ||||||
| import listPage from 'vault/tests/pages/secrets/backends'; | import configPage from 'vault/tests/pages/secrets/backend/configuration'; | ||||||
|  |  | ||||||
| moduleForAcceptance('Acceptance | settings/mount-secret-backend', { | moduleForAcceptance('Acceptance | settings/mount-secret-backend', { | ||||||
|   beforeEach() { |   beforeEach() { | ||||||
| @@ -30,13 +30,9 @@ test('it sets the ttl corrects when mounting', function(assert) { | |||||||
|     .maxTTLUnit('h') |     .maxTTLUnit('h') | ||||||
|     .submit(); |     .submit(); | ||||||
|  |  | ||||||
|   listPage.visit(); |   configPage.visit({backend: path}); | ||||||
|   andThen(() => { |   andThen(() => { | ||||||
|     listPage.links().findByPath(path).toggleDetails(); |     assert.equal(configPage.defaultTTL, defaultTTLSeconds, 'shows the proper TTL'); | ||||||
|   }); |     assert.equal(configPage.maxTTL, maxTTLSeconds, 'shows the proper max TTL'); | ||||||
|   andThen(() => { |  | ||||||
|     const details = listPage.links().findByPath(path); |  | ||||||
|     assert.equal(details.defaultTTL, defaultTTLSeconds, 'shows the proper TTL'); |  | ||||||
|     assert.equal(details.maxTTL, maxTTLSeconds, 'shows the proper max TTL'); |  | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								ui/tests/pages/secrets/backend/configuration.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								ui/tests/pages/secrets/backend/configuration.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | import { create, visitable, text } from 'ember-cli-page-object'; | ||||||
|  |  | ||||||
|  | export default create({ | ||||||
|  |   visit: visitable('/vault/secrets/:backend/configuration'), | ||||||
|  |   defaultTTL: text('[data-test-row-value="Default Lease TTL"]'), | ||||||
|  |   maxTTL: text('[data-test-row-value="Max Lease TTL"]'), | ||||||
|  | }); | ||||||
| @@ -1,17 +1,16 @@ | |||||||
| import { create, visitable, collection, text, clickable } from 'ember-cli-page-object'; | import { create, visitable, collection, clickable, text } from 'ember-cli-page-object'; | ||||||
|  |  | ||||||
| export default create({ | export default create({ | ||||||
|   visit: visitable('/vault/secrets'), |   visit: visitable('/vault/secrets'), | ||||||
|   links: collection({ |   rows: collection({ | ||||||
|     itemScope: '[data-test-secret-backend-link]', |     itemScope: '[data-test-secret-backend-row]', | ||||||
|     item: { |     item: { | ||||||
|       path: text('[data-test-secret-path]'), |       path: text('[data-test-secret-path]'), | ||||||
|       toggleDetails: clickable('[data-test-secret-backend-detail]'), |       menu: clickable('[data-test-popup-menu-trigger]'), | ||||||
|       defaultTTL: text('[data-test-secret-backend-details="default-ttl"]'), |  | ||||||
|       maxTTL: text('[data-test-secret-backend-details="max-ttl"]'), |  | ||||||
|     }, |     }, | ||||||
|     findByPath(path) { |     findByPath(path) { | ||||||
|       return this.toArray().findBy('path', path + '/'); |       return this.toArray().findBy('path', path + '/'); | ||||||
|     }, |     }, | ||||||
|   }), |   }), | ||||||
|  |   configLink: clickable('[data-test-engine-config]'), | ||||||
| }); | }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Matthew Irish
					Matthew Irish