Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Ember deprecation - addObject, removeObject #25952

Merged
merged 19 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/app/adapters/database/role.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default ApplicationAdapter.extend({
async _updateAllowedRoles(store, { role, backend, db, type = 'add' }) {
const connection = await store.queryRecord('database/connection', { backend, id: db });
const roles = [...(connection.allowed_roles || [])];
const allowedRoles = type === 'add' ? addToArray([roles, role]) : removeFromArray([roles, role]);
const allowedRoles = type === 'add' ? addToArray(roles, role) : removeFromArray(roles, role);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I simplified the interface for the exported methods so that we don't need to wrap the params in an array

connection.allowed_roles = allowedRoles;
return connection.save();
},
Expand Down
5 changes: 3 additions & 2 deletions ui/app/adapters/ldap/role.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import NamedPathAdapter from 'vault/adapters/named-path';
import { encodePath } from 'vault/utils/path-encoding-helpers';
import { service } from '@ember/service';
import AdapterError from '@ember-data/adapter/error';
import { addManyToArray } from 'vault/helpers/add-to-array';

export default class LdapRoleAdapter extends NamedPathAdapter {
@service flashMessages;
Expand All @@ -33,7 +34,7 @@ export default class LdapRoleAdapter extends NamedPathAdapter {
async query(store, type, query, recordArray, options) {
const { showPartialError } = options.adapterOptions || {};
const { backend } = query;
const roles = [];
let roles = [];
const errors = [];

for (const roleType of ['static', 'dynamic']) {
Expand All @@ -42,7 +43,7 @@ export default class LdapRoleAdapter extends NamedPathAdapter {
const models = await this.ajax(url, 'GET', { data: { list: true } }).then((resp) => {
return resp.data.keys.map((name) => ({ id: name, name, backend, type: roleType }));
});
roles.addObjects(models);
roles = addManyToArray(roles, models);
} catch (error) {
if (error.httpStatus !== 404) {
errors.push(error);
Expand Down
11 changes: 7 additions & 4 deletions ui/app/components/auth-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import Ember from 'ember';
import { next } from '@ember/runloop';
import { service } from '@ember/service';
import { match, alias, or } from '@ember/object/computed';
import { match, or } from '@ember/object/computed';
import { dasherize } from '@ember/string';
import Component from '@ember/component';
import { computed } from '@ember/object';
Expand Down Expand Up @@ -166,9 +166,12 @@ export default Component.extend(DEFAULTS, {
return templateName;
}),

hasCSPError: alias('csp.connectionViolations'),

cspErrorText: `This is a standby Vault node but can't communicate with the active node via request forwarding. Sign in at the active node to use the Vault UI.`,
cspError: computed('csp.connectionViolations.length', function () {
if (this.csp.connectionViolations.length) {
return `This is a standby Vault node but can't communicate with the active node via request forwarding. Sign in at the active node to use the Vault UI.`;
}
return '';
}),

allSupportedMethods: computed('methodsToShow', 'hasMethodsWithPath', 'authMethods', function () {
const hasMethodsWithPath = this.hasMethodsWithPath;
Expand Down
4 changes: 3 additions & 1 deletion ui/app/components/clients/horizontal-bar-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ export default class HorizontalBarChart extends Component {
this.isLabel = false;
this.tooltipText = []; // clear stats
this.args.chartLegend.forEach(({ key, label }) => {
this.tooltipText.pushObject(`${formatNumber([data[key]])} ${label}`);
// since we're relying on D3 not ember reactivity,
// pushing directly to this.tooltipText updates the DOM
hashishaw marked this conversation as resolved.
Show resolved Hide resolved
this.tooltipText.push(`${formatNumber([data[key]])} ${label}`);
});

select(hoveredElement).style('opacity', 1);
Expand Down
4 changes: 3 additions & 1 deletion ui/app/components/clients/vertical-bar-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ export default class VerticalBarChart extends Component {
this.tooltipStats = []; // clear stats
this.args.chartLegend.forEach(({ key, label }) => {
stackedNumbers.push(data[key]);
this.tooltipStats.pushObject(`${formatNumber([data[key]])} ${label}`);
// since we're relying on D3 not ember reactivity,
// pushing directly to this.tooltipStats updates the DOM
this.tooltipStats.push(`${formatNumber([data[key]])} ${label}`);
});
this.tooltipTotal = `${formatNumber([calculateSum(stackedNumbers)])} ${
data.new_clients ? 'total' : 'new'
Expand Down
4 changes: 3 additions & 1 deletion ui/app/components/keymgmt/provider-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { task } from 'ember-concurrency';
import { waitFor } from '@ember/test-waiters';
import { removeFromArray } from 'vault/helpers/remove-from-array';

/**
* @module KeymgmtProviderEdit
Expand Down Expand Up @@ -94,8 +95,9 @@ export default class KeymgmtProviderEdit extends Component {
@action
async onDeleteKey(model) {
try {
const providerKeys = removeFromArray(this.args.model.keys, model);
await model.destroyRecord();
this.args.model.keys.removeObject(model);
this.args.model.keys = providerKeys;
} catch (error) {
this.flashMessages.danger(error.errors.join('. '));
}
Expand Down
22 changes: 16 additions & 6 deletions ui/app/components/mfa/mfa-login-enforcement-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { task } from 'ember-concurrency';
import handleHasManySelection from 'core/utils/search-select-has-many';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 👏 👏

import { addManyToArray, addToArray } from 'vault/helpers/add-to-array';
import { removeFromArray } from 'vault/helpers/remove-from-array';

/**
* @module MfaLoginEnforcementForm
Expand Down Expand Up @@ -64,7 +65,7 @@ export default class MfaLoginEnforcementForm extends Component {
for (const { label, key } of this.targetTypes) {
const targetArray = await this.args.model[key];
const targets = targetArray.map((value) => ({ label, key, value }));
this.targets.addObjects(targets);
this.targets = addManyToArray(this.targets, targets);
}
}
async resetTargetState() {
Expand Down Expand Up @@ -102,7 +103,6 @@ export default class MfaLoginEnforcementForm extends Component {

updateModelForKey(key) {
const newValue = this.targets.filter((t) => t.key === key).map((t) => t.value);
// Set the model value directly instead of using Array methods (like .addObject)
this.args.model[key] = newValue;
}

Expand All @@ -126,8 +126,18 @@ export default class MfaLoginEnforcementForm extends Component {

@action
async onMethodChange(selectedIds) {
// first make sure the async relationship is loaded
const methods = await this.args.model.mfa_methods;
handleHasManySelection(selectedIds, methods, this.store, 'mfa-method');
// then remove items that are no longer selected
const updatedList = methods.filter((model) => {
return selectedIds.includes(model.id);
});
// then add selected items that don't exist in the list already
const modelIds = updatedList.map((model) => model.id);
const toAdd = selectedIds
.filter((id) => !modelIds.includes(id))
.map((id) => this.store.peekRecord('mfa-method', id));
this.args.model.mfa_methods = addManyToArray(updatedList, toAdd);
}

@action
Expand All @@ -150,15 +160,15 @@ export default class MfaLoginEnforcementForm extends Component {
addTarget() {
const { label, key } = this.selectedTarget;
const value = this.selectedTargetValue;
this.targets.addObject({ label, value, key });
this.targets = addToArray(this.targets, { label, value, key });
// recalculate value for appropriate model property
this.updateModelForKey(key);
this.selectedTargetValue = null;
this.resetTargetState();
}
@action
removeTarget(target) {
this.targets.removeObject(target);
this.targets = removeFromArray(this.targets, target);
// recalculate value for appropriate model property
this.updateModelForKey(target.key);
}
Expand Down
1 change: 1 addition & 0 deletions ui/app/components/secret-create-or-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export default class SecretCreateOrUpdate extends Component {
if (isBlank(item.name)) {
return;
}
// secretData is a KVObject/ArrayProxy so removeObject is fine here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the comment!

data.removeObject(item);
this.checkRows();
this.handleChange();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { capitalize } from '@ember/string';
import { task } from 'ember-concurrency';
import { addToArray } from 'vault/helpers/add-to-array';

export default class MfaMethodCreateController extends Controller {
@service store;
Expand Down Expand Up @@ -95,7 +96,8 @@ export default class MfaMethodCreateController extends Controller {
// first save method
yield this.method.save();
if (this.enforcement) {
this.enforcement.mfa_methods.addObject(this.method);
// mfa_methods is type PromiseManyArray so slice in necessary to convert it to an Array
this.enforcement.mfa_methods = addToArray(this.enforcement.mfa_methods.slice(), this.method);
hashishaw marked this conversation as resolved.
Show resolved Hide resolved
try {
// now save enforcement and catch error separately
yield this.enforcement.save();
Expand Down
15 changes: 13 additions & 2 deletions ui/app/helpers/add-to-array.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if these helpers should live in the addon engine, it seems like something we may want access to in the wider app? It also seems like this should be a util as I don't see it used in any templates, which would simplify the extra logic needed to build the helper, but that may be too much scope creep for this PR to relocate. 😶‍🌫️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea for a follow-on! I think it will be easier to track the changes outside of this PR

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ function dedupe(items) {
return items.filter((v, i) => items.indexOf(v) === i);
}

export function addToArray([array, string]) {
export function addManyToArray(array, otherArray) {
assert(`Both values must be an array`, Array.isArray(array) && Array.isArray(otherArray));
const newArray = [...array].concat(otherArray);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity - is there a reason you used concat() here instead of spreading otherArray?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there was, I don't remember it 😂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL: concat will add otherArray as a single item if it isn't an array, which is nice. https://stackoverflow.com/questions/48865710/spread-operator-vs-array-concat

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh neat! 💡

return dedupe(newArray);
}

export function addToArray(array, string) {
if (!Array.isArray(array)) {
assert(`Value provided is not an array`, false);
}
Expand All @@ -19,4 +25,9 @@ export function addToArray([array, string]) {
return dedupe(newArray);
}

export default buildHelper(addToArray);
export default buildHelper(function ([array, string]) {
if (Array.isArray(string)) {
return addManyToArray(array, string);
}
return addToArray(array, string);
});
19 changes: 14 additions & 5 deletions ui/app/helpers/remove-from-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ function dedupe(items) {
return items.filter((v, i) => items.indexOf(v) === i);
}

export function removeFromArray([array, string]) {
if (!Array.isArray(array)) {
assert(`Value provided is not an array`, false);
}
export function removeManyFromArray(array, toRemove) {
assert(`Both values must be an array`, Array.isArray(array) && Array.isArray(toRemove));
const a = [...(array || [])];
return a.filter((v) => !toRemove.includes(v));
}

export function removeFromArray(array, string) {
assert(`Value provided is not an array`, Array.isArray(array));
const newArray = [...array];
const idx = newArray.indexOf(string);
if (idx >= 0) {
Expand All @@ -22,4 +26,9 @@ export function removeFromArray([array, string]) {
return dedupe(newArray);
}

export default buildHelper(removeFromArray);
export default buildHelper(function ([array, string]) {
if (Array.isArray(string)) {
return removeManyFromArray(array, string);
}
return removeFromArray(array, string);
});
3 changes: 2 additions & 1 deletion ui/app/lib/console-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ export function shiftCommandIndex(keyCode: number, history: CommandLog[], index:
}

if (newInputValue !== '') {
newInputValue = history.objectAt(index)?.content;
const objAt = history[index];
newInputValue = objAt?.content;
}

return [index, newInputValue];
Expand Down
15 changes: 9 additions & 6 deletions ui/app/models/kmip/role.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { computed } from '@ember/object';
import fieldToAttrs, { expandAttributeMeta } from 'vault/utils/field-to-attrs';
import apiPath from 'vault/utils/api-path';
import lazyCapabilities from 'vault/macros/lazy-capabilities';
import { removeManyFromArray } from 'vault/helpers/remove-from-array';

export const COMPUTEDS = {
operationFields: computed('newFields', function () {
return this.newFields.filter((key) => key.startsWith('operation'));
}),

operationFieldsWithoutSpecial: computed('operationFields', function () {
return this.operationFields.slice().removeObjects(['operationAll', 'operationNone']);
return removeManyFromArray(this.operationFields, ['operationAll', 'operationNone']);
}),

tlsFields: computed(function () {
Expand All @@ -25,12 +26,12 @@ export const COMPUTEDS = {
// For rendering on the create/edit pages
defaultFields: computed('newFields', 'operationFields', 'tlsFields', function () {
const excludeFields = ['role'].concat(this.operationFields, this.tlsFields);
return this.newFields.slice().removeObjects(excludeFields);
return removeManyFromArray(this.newFields, excludeFields);
}),

// For adapter/serializer
nonOperationFields: computed('newFields', 'operationFields', function () {
return this.newFields.slice().removeObjects(this.operationFields);
return removeManyFromArray(this.newFields, this.operationFields);
}),
};

Expand Down Expand Up @@ -64,9 +65,11 @@ export default Model.extend(COMPUTEDS, {

const attributes = ['operationAddAttribute', 'operationGetAttributes'];
const server = ['operationDiscoverVersions'];
const others = this.operationFieldsWithoutSpecial
.slice()
.removeObjects(objects.concat(attributes, server));
const others = removeManyFromArray(this.operationFieldsWithoutSpecial, [
...objects,
...attributes,
...server,
]);
const groups = [
{ 'Managed Cryptographic Objects': objects },
{ 'Object Attributes': attributes },
Expand Down
10 changes: 6 additions & 4 deletions ui/app/models/mfa-login-enforcement.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { methods } from 'vault/helpers/mountable-auth-methods';
import { withModelValidations } from 'vault/decorators/model-validations';
import { isPresent } from '@ember/utils';
import { service } from '@ember/service';
import { addManyToArray, addToArray } from 'vault/helpers/add-to-array';

const validations = {
name: [{ type: 'presence', message: 'Name is required' }],
Expand Down Expand Up @@ -52,7 +53,7 @@ export default class MfaLoginEnforcementModel extends Model {

async prepareTargets() {
let authMethods;
const targets = [];
let targets = [];

if (this.auth_method_accessors.length || this.auth_method_types.length) {
// fetch all auth methods and lookup by accessor to get mount path and type
Expand All @@ -68,7 +69,8 @@ export default class MfaLoginEnforcementModel extends Model {
const selectedAuthMethods = authMethods.filter((model) => {
return this.auth_method_accessors.includes(model.accessor);
});
targets.addObjects(
targets = addManyToArray(
targets,
selectedAuthMethods.map((method) => ({
icon: this.iconForMount(method.type),
link: 'vault.cluster.access.method',
Expand All @@ -82,7 +84,7 @@ export default class MfaLoginEnforcementModel extends Model {
this.auth_method_types.forEach((type) => {
const icon = this.iconForMount(type);
const mountCount = authMethods.filterBy('type', type).length;
targets.addObject({
targets = addToArray(targets, {
key: 'auth_method_types',
icon,
title: type,
Expand All @@ -92,7 +94,7 @@ export default class MfaLoginEnforcementModel extends Model {

for (const key of ['identity_entities', 'identity_groups']) {
(await this[key]).forEach((model) => {
targets.addObject({
targets = addToArray(targets, {
key,
icon: 'user',
link: 'vault.cluster.access.identity.show',
Expand Down
2 changes: 1 addition & 1 deletion ui/app/serializers/sync/destination.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default class SyncDestinationSerializer extends ApplicationSerializer {
const type = key.replace(/\/$/, '');
const id = `${type}/${name}`;
// create object with destination's id and attributes, add to payload
transformedPayload.pushObject({ id, name, type });
transformedPayload.push({ id, name, type });
});
}
return transformedPayload;
Expand Down
5 changes: 2 additions & 3 deletions ui/app/services/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { resolve, reject } from 'rsvp';
import getStorage from 'vault/lib/token-storage';
import ENV from 'vault/config/environment';
import { allSupportedAuthBackends } from 'vault/helpers/supported-auth-backends';
import { addToArray } from 'vault/helpers/add-to-array';

const TOKEN_SEPARATOR = '☃';
const TOKEN_PREFIX = 'vault-';
Expand Down Expand Up @@ -250,7 +251,6 @@ export default Service.extend({

persistAuthData() {
const [firstArg, resp] = arguments;
const tokens = this.tokens;
const currentNamespace = this.namespaceService.path || '';
// dropdown vs tab format
const mountPath = firstArg?.data?.path || firstArg?.selectedAuth;
Expand Down Expand Up @@ -303,8 +303,7 @@ export default Service.extend({
if (!data.displayName) {
data.displayName = (this.getTokenData(tokenName) || {}).displayName;
}
tokens.addObject(tokenName);
this.set('tokens', tokens);
this.set('tokens', addToArray(this.tokens, tokenName));
this.set('allowExpiration', false);
this.setTokenData(tokenName, data);
return resolve({
Expand Down
Loading
Loading