UI/utils cleanup (#21863)

* key-utils: create core addon and tests

* key-util: remove lib/key-utils and replace all imports with core util
This commit is contained in:
Chelsea Shaw
2023-07-14 14:45:57 -05:00
committed by GitHub
parent cc366f6cba
commit 88ca498fb4
14 changed files with 119 additions and 58 deletions

View File

@@ -7,11 +7,10 @@ import { inject as service } from '@ember/service';
import { alias, gt } from '@ember/object/computed'; import { alias, gt } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import keyUtils from 'vault/lib/key-utils';
import pathToTree from 'vault/lib/path-to-tree';
import { task, timeout } from 'ember-concurrency'; import { task, timeout } from 'ember-concurrency';
import pathToTree from 'vault/lib/path-to-tree';
import { ancestorKeysForKey } from 'core/utils/key-utils';
const { ancestorKeysForKey } = keyUtils;
const DOT_REPLACEMENT = '☃'; const DOT_REPLACEMENT = '☃';
const ANIMATION_DURATION = 250; const ANIMATION_DURATION = 250;

View File

@@ -6,8 +6,8 @@
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import Controller, { inject as controller } from '@ember/controller'; import Controller, { inject as controller } from '@ember/controller';
import utils from 'vault/lib/key-utils';
import ListController from 'core/mixins/list-controller'; import ListController from 'core/mixins/list-controller';
import { keyIsFolder } from 'core/utils/key-utils';
export default Controller.extend(ListController, { export default Controller.extend(ListController, {
flashMessages: service(), flashMessages: service(),
@@ -26,7 +26,7 @@ export default Controller.extend(ListController, {
isLoading: false, isLoading: false,
filterIsFolder: computed('filter', function () { filterIsFolder: computed('filter', function () {
return !!utils.keyIsFolder(this.filter); return !!keyIsFolder(this.filter);
}), }),
emptyTitle: computed('baseKey.id', 'filter', 'filterIsFolder', function () { emptyTitle: computed('baseKey.id', 'filter', 'filterIsFolder', function () {

View File

@@ -7,10 +7,10 @@ import { or } from '@ember/object/computed';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import utils from 'vault/lib/key-utils';
import BackendCrumbMixin from 'vault/mixins/backend-crumb'; import BackendCrumbMixin from 'vault/mixins/backend-crumb';
import WithNavToNearestAncestor from 'vault/mixins/with-nav-to-nearest-ancestor'; import WithNavToNearestAncestor from 'vault/mixins/with-nav-to-nearest-ancestor';
import ListController from 'core/mixins/list-controller'; import ListController from 'core/mixins/list-controller';
import { keyIsFolder } from 'core/utils/key-utils';
export default Controller.extend(ListController, BackendCrumbMixin, WithNavToNearestAncestor, { export default Controller.extend(ListController, BackendCrumbMixin, WithNavToNearestAncestor, {
flashMessages: service(), flashMessages: service(),
@@ -19,7 +19,7 @@ export default Controller.extend(ListController, BackendCrumbMixin, WithNavToNea
tab: '', tab: '',
filterIsFolder: computed('filter', function () { filterIsFolder: computed('filter', function () {
return !!utils.keyIsFolder(this.filter); return !!keyIsFolder(this.filter);
}), }),
isConfigurableTab: or('isCertTab', 'isConfigure'), isConfigurableTab: or('isCertTab', 'isConfigure'),

View File

@@ -5,7 +5,7 @@
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import Mixin from '@ember/object/mixin'; import Mixin from '@ember/object/mixin';
import utils from 'vault/lib/key-utils'; import { keyIsFolder, keyPartsForKey, parentKeyForKey } from 'core/utils/key-utils';
export default Mixin.create({ export default Mixin.create({
// what attribute has the path for the key // what attribute has the path for the key
@@ -26,16 +26,16 @@ export default Mixin.create({
// rather than using defineProperty for all of these, // rather than using defineProperty for all of these,
// we're just going to hardcode the known keys for the path ('id' and 'path') // we're just going to hardcode the known keys for the path ('id' and 'path')
isFolder: computed('id', 'path', function () { isFolder: computed('id', 'path', function () {
return utils.keyIsFolder(this.pathVal()); return keyIsFolder(this.pathVal());
}), }),
keyParts: computed('id', 'path', function () { keyParts: computed('id', 'path', function () {
return utils.keyPartsForKey(this.pathVal()); return keyPartsForKey(this.pathVal());
}), }),
parentKey: computed('id', 'path', 'isCreating', { parentKey: computed('id', 'path', 'isCreating', {
get: function () { get: function () {
return this.isCreating ? this.initialParentKey : utils.parentKeyForKey(this.pathVal()); return this.isCreating ? this.initialParentKey : parentKeyForKey(this.pathVal());
}, },
set: function (_, value) { set: function (_, value) {
return value; return value;

View File

@@ -4,8 +4,8 @@
*/ */
import Mixin from '@ember/object/mixin'; import Mixin from '@ember/object/mixin';
import utils from 'vault/lib/key-utils';
import { task } from 'ember-concurrency'; import { task } from 'ember-concurrency';
import { ancestorKeysForKey } from 'core/utils/key-utils';
// This mixin is currently used in a controller and a component, but we // This mixin is currently used in a controller and a component, but we
// don't see cancellation of the task as the while loop runs in either // don't see cancellation of the task as the while loop runs in either
@@ -18,7 +18,7 @@ import { task } from 'ember-concurrency';
// the ancestors array and transitions to the root // the ancestors array and transitions to the root
export default Mixin.create({ export default Mixin.create({
navToNearestAncestor: task(function* (key) { navToNearestAncestor: task(function* (key) {
const ancestors = utils.ancestorKeysForKey(key); const ancestors = ancestorKeysForKey(key);
let errored = false; let errored = false;
let nearest = ancestors.pop(); let nearest = ancestors.pop();
while (nearest) { while (nearest) {

View File

@@ -3,20 +3,20 @@
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
import { set } from '@ember/object';
import { hash } from 'rsvp'; import { hash } from 'rsvp';
import { set } from '@ember/object';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import UnloadModelRoute from 'vault/mixins/unload-model-route';
import utils from 'vault/lib/key-utils';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { keyIsFolder, parentKeyForKey } from 'core/utils/key-utils';
import UnloadModelRoute from 'vault/mixins/unload-model-route';
export default Route.extend(UnloadModelRoute, { export default Route.extend(UnloadModelRoute, {
store: service(), store: service(),
beforeModel() { beforeModel() {
const { lease_id: leaseId } = this.paramsFor(this.routeName); const { lease_id: leaseId } = this.paramsFor(this.routeName);
const parentKey = utils.parentKeyForKey(leaseId); const parentKey = parentKeyForKey(leaseId);
if (utils.keyIsFolder(leaseId)) { if (keyIsFolder(leaseId)) {
if (parentKey) { if (parentKey) {
return this.transitionTo('vault.cluster.access.leases.list', parentKey); return this.transitionTo('vault.cluster.access.leases.list', parentKey);
} else { } else {

View File

@@ -3,8 +3,8 @@
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
import { parentKeyForKey } from 'core/utils/key-utils';
import EditBase from './secret-edit'; import EditBase from './secret-edit';
import utils from 'vault/lib/key-utils';
export default EditBase.extend({ export default EditBase.extend({
queryParams: { queryParams: {
@@ -17,7 +17,7 @@ export default EditBase.extend({
beforeModel() { beforeModel() {
const { secret } = this.paramsFor(this.routeName); const { secret } = this.paramsFor(this.routeName);
const parentKey = utils.parentKeyForKey(secret); const parentKey = parentKeyForKey(secret);
const { backend } = this.paramsFor('vault.cluster.secrets.backend'); const { backend } = this.paramsFor('vault.cluster.secrets.backend');
if (this.backendType(backend) !== 'transit') { if (this.backendType(backend) !== 'transit') {
if (parentKey) { if (parentKey) {

View File

@@ -8,9 +8,9 @@ import { set } from '@ember/object';
import { resolve } from 'rsvp'; import { resolve } from 'rsvp';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import utils from 'vault/lib/key-utils';
import UnloadModelRoute from 'vault/mixins/unload-model-route'; import UnloadModelRoute from 'vault/mixins/unload-model-route';
import { encodePath, normalizePath } from 'vault/utils/path-encoding-helpers'; import { encodePath, normalizePath } from 'vault/utils/path-encoding-helpers';
import { keyIsFolder, parentKeyForKey } from 'core/utils/key-utils';
export default Route.extend(UnloadModelRoute, { export default Route.extend(UnloadModelRoute, {
store: service(), store: service(),
@@ -79,9 +79,9 @@ export default Route.extend(UnloadModelRoute, {
beforeModel({ to: { queryParams } }) { beforeModel({ to: { queryParams } }) {
const secret = this.secretParam(); const secret = this.secretParam();
return this.buildModel(secret, queryParams).then(() => { return this.buildModel(secret, queryParams).then(() => {
const parentKey = utils.parentKeyForKey(secret); const parentKey = parentKeyForKey(secret);
const mode = this.routeName.split('.').pop(); const mode = this.routeName.split('.').pop();
if (mode === 'edit' && utils.keyIsFolder(secret)) { if (mode === 'edit' && keyIsFolder(secret)) {
if (parentKey) { if (parentKey) {
return this.transitionTo('vault.cluster.secrets.backend.list', encodePath(parentKey)); return this.transitionTo('vault.cluster.secrets.backend.list', encodePath(parentKey));
} else { } else {

View File

@@ -4,10 +4,10 @@
*/ */
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import utils from 'vault/lib/key-utils';
import UnloadModelRoute from 'vault/mixins/unload-model-route'; import UnloadModelRoute from 'vault/mixins/unload-model-route';
import { normalizePath } from 'vault/utils/path-encoding-helpers'; import { normalizePath } from 'vault/utils/path-encoding-helpers';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { parentKeyForKey } from 'core/utils/key-utils';
export default Route.extend(UnloadModelRoute, { export default Route.extend(UnloadModelRoute, {
store: service(), store: service(),
@@ -16,7 +16,7 @@ export default Route.extend(UnloadModelRoute, {
beforeModel() { beforeModel() {
const backendModel = this.modelFor('vault.cluster.secrets.backend'); const backendModel = this.modelFor('vault.cluster.secrets.backend');
const { secret } = this.paramsFor(this.routeName); const { secret } = this.paramsFor(this.routeName);
const parentKey = utils.parentKeyForKey(secret); const parentKey = parentKeyForKey(secret);
if (backendModel.get('isV2KV')) { if (backendModel.get('isV2KV')) {
return; return;
} }

View File

@@ -4,7 +4,7 @@
*/ */
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import utils from 'vault/lib/key-utils'; import { ancestorKeysForKey, keyPartsForKey, keyWithoutParentKey } from 'core/utils/key-utils';
import { encodePath } from 'vault/utils/path-encoding-helpers'; import { encodePath } from 'vault/utils/path-encoding-helpers';
/** /**
@@ -60,8 +60,8 @@ export default class KeyValueHeader extends Component {
const path = this.args.path; const path = this.args.path;
const currentPath = this.currentPath; const currentPath = this.currentPath;
const showCurrent = this.showCurrent; const showCurrent = this.showCurrent;
const ancestors = utils.ancestorKeysForKey(baseKey); const ancestors = ancestorKeysForKey(baseKey);
const parts = utils.keyPartsForKey(baseKey); const parts = keyPartsForKey(baseKey);
if (ancestors.length === 0) { if (ancestors.length === 0) {
crumbs.push({ crumbs.push({
label: baseKey, label: baseKey,
@@ -87,8 +87,8 @@ export default class KeyValueHeader extends Component {
}); });
crumbs.push({ crumbs.push({
label: utils.keyWithoutParentKey(baseKey), label: keyWithoutParentKey(baseKey),
text: this.stripTrailingSlash(utils.keyWithoutParentKey(baseKey)), text: this.stripTrailingSlash(keyWithoutParentKey(baseKey)),
path: currentPath, path: currentPath,
model: baseKeyModel, model: baseKeyModel,
}); });

View File

@@ -3,17 +3,17 @@
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
import Ember from 'ember';
import { debounce, later } from '@ember/runloop'; import { debounce, later } from '@ember/runloop';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { action } from '@ember/object'; import { action } from '@ember/object';
import { guidFor } from '@ember/object/internals'; import { guidFor } from '@ember/object/internals';
import Component from '@glimmer/component'; import Component from '@glimmer/component';
// TODO MOVE THESE TO THE ADDON
import utils from 'vault/lib/key-utils';
import keys from 'vault/lib/keycodes';
import { encodePath } from 'vault/utils/path-encoding-helpers'; import { encodePath } from 'vault/utils/path-encoding-helpers';
import Ember from 'ember'; import { keyIsFolder, parentKeyForKey } from 'core/utils/key-utils';
// TODO MOVE THESE TO THE ADDON
import keys from 'vault/lib/keycodes';
/** /**
* @module NavigateInput * @module NavigateInput
@@ -94,7 +94,7 @@ export default class NavigateInput extends Component {
if (mode.startsWith('secrets') && (!val || val === baseKey)) { if (mode.startsWith('secrets') && (!val || val === baseKey)) {
return; return;
} }
if (this.args.filterMatchesKey && !utils.keyIsFolder(val)) { if (this.args.filterMatchesKey && !keyIsFolder(val)) {
const params = [routeFor('show', mode, this.args.urls), extraParams, this.keyForNav(val)].compact(); const params = [routeFor('show', mode, this.args.urls), extraParams, this.keyForNav(val)].compact();
this.transitionToRoute(...params); this.transitionToRoute(...params);
} else { } else {
@@ -126,7 +126,7 @@ export default class NavigateInput extends Component {
// pop to the nearest parentKey or to the root // pop to the nearest parentKey or to the root
onEscape(val) { onEscape(val) {
const key = utils.parentKeyForKey(val) || ''; const key = parentKeyForKey(val) || '';
this.args.filterDidChange(key); this.args.filterDidChange(key);
this.filterUpdated(key); this.filterUpdated(key);
} }
@@ -150,11 +150,11 @@ export default class NavigateInput extends Component {
} }
// select the key to nav to, assumed to be a folder // select the key to nav to, assumed to be a folder
let key = val ? val.trim() : ''; let key = val ? val.trim() : '';
const isFolder = utils.keyIsFolder(key); const isFolder = keyIsFolder(key);
if (!isFolder) { if (!isFolder) {
// nav to the closest parentKey (or the root) // nav to the closest parentKey (or the root)
key = utils.parentKeyForKey(val) || ''; key = parentKeyForKey(val) || '';
} }
const pageFilter = val.replace(key, ''); const pageFilter = val.replace(key, '');
@@ -167,7 +167,7 @@ export default class NavigateInput extends Component {
if (key) { if (key) {
args.push(key); args.push(key);
} }
if (pageFilter && !utils.keyIsFolder(pageFilter)) { if (pageFilter && !keyIsFolder(pageFilter)) {
args.push({ args.push({
queryParams: { queryParams: {
page: 1, page: 1,

View File

@@ -3,37 +3,38 @@
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
function keyIsFolder(key) { export function keyIsFolder(key: string) {
return key ? !!key.match(/\/$/) : false; return key ? !!key.match(/\/$/) : false;
} }
function keyPartsForKey(key) { export function keyPartsForKey(key: string) {
if (!key) { if (!key) {
return null; return null;
} }
var isFolder = keyIsFolder(key); const isFolder = keyIsFolder(key);
var parts = key.split('/'); const parts = key.split('/');
if (isFolder) { if (isFolder) {
// remove last item which is empty
parts.pop(); parts.pop();
} }
return parts.length > 1 ? parts : null; return parts.length > 1 ? parts : null;
} }
function parentKeyForKey(key) { export function parentKeyForKey(key: string) {
var parts = keyPartsForKey(key); const parts = keyPartsForKey(key);
if (!parts) { if (!parts) {
return null; return '';
} }
return parts.slice(0, -1).join('/') + '/'; return parts.slice(0, -1).join('/') + '/';
} }
function keyWithoutParentKey(key) { export function keyWithoutParentKey(key: string) {
return key ? key.replace(parentKeyForKey(key), '') : null; return key ? key.replace(parentKeyForKey(key), '') : null;
} }
function ancestorKeysForKey(key) { export function ancestorKeysForKey(key: string) {
var ancestors = [], const ancestors = [];
parentKey = parentKeyForKey(key); let parentKey = parentKeyForKey(key);
while (parentKey) { while (parentKey) {
ancestors.unshift(parentKey); ancestors.unshift(parentKey);
@@ -42,11 +43,3 @@ function ancestorKeysForKey(key) {
return ancestors; return ancestors;
} }
export default {
keyIsFolder,
keyPartsForKey,
parentKeyForKey,
keyWithoutParentKey,
ancestorKeysForKey,
};

View File

@@ -0,0 +1,7 @@
export {
keyIsFolder,
keyPartsForKey,
parentKeyForKey,
keyWithoutParentKey,
ancestorKeysForKey,
} from 'core/utils/key-utils';

View File

@@ -0,0 +1,62 @@
import {
ancestorKeysForKey,
keyIsFolder,
keyPartsForKey,
keyWithoutParentKey,
parentKeyForKey,
} from 'vault/utils/key-utils';
import { module, test } from 'qunit';
module('Unit | Utility | key-utils', function () {
test('keyIsFolder', function (assert) {
let result = keyIsFolder('foo');
assert.false(result, 'not folder');
result = keyIsFolder('foo/');
assert.true(result, 'is folder');
result = keyIsFolder('foo/bar');
assert.false(result, 'not folder');
});
test('keyPartsForKey', function (assert) {
let result = keyPartsForKey('');
assert.strictEqual(result, null, 'falsy value returns null');
result = keyPartsForKey('foo');
assert.strictEqual(result, null, 'returns null if not a folder');
result = keyPartsForKey('foo/bar');
assert.deepEqual(result, ['foo', 'bar'], 'returns parts of key');
result = keyPartsForKey('foo/bar/');
assert.deepEqual(result, ['foo', 'bar'], 'returns parts of key when ends in slash');
});
test('parentKeyForKey', function (assert) {
let result = parentKeyForKey('my/very/nested/secret/path');
assert.strictEqual(result, 'my/very/nested/secret/', 'returns parent path for key');
result = parentKeyForKey('my/nested/secret/');
assert.strictEqual(result, 'my/nested/', 'returns correct parents');
result = parentKeyForKey('my-secret');
assert.strictEqual(result, '', 'returns empty string when no parents');
});
test('keyWithoutParentKey', function (assert) {
let result = keyWithoutParentKey('my/very/nested/secret/path');
assert.strictEqual(result, 'path', 'returns key without parent key');
result = keyWithoutParentKey('my-secret');
assert.strictEqual(result, 'my-secret', 'returns path when no parent');
result = keyWithoutParentKey('folder/');
assert.strictEqual(result, 'folder/', 'returns path as-is when folder without parent');
});
test('ancestorKeysForKey', function (assert) {
const expected = ['my/', 'my/very/', 'my/very/nested/', 'my/very/nested/secret/'];
let result = ancestorKeysForKey('my/very/nested/secret/path');
assert.deepEqual(result, expected, 'returns array of ancestor paths');
result = ancestorKeysForKey('foobar');
assert.deepEqual(result, [], 'returns empty array for root path');
});
});