From 6b004d3165bfebf1f48b065317954155e955a542 Mon Sep 17 00:00:00 2001 From: Edgar Garcia <63310723+edgarulg@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:24:44 -0600 Subject: [PATCH] feat(google): add support for partnerMetadata in GCE servergroup (#10150) * feat(google): add support for partnerMetadata in GCE servergroup * feat(google): fix prettier issues * feat(google): render JSON object as its string representation * feat(google): update tests with textarea * feat(google): fix prettier issue * feat(google): move display logic to controller * feat(google): fix tests * feat(google): expect JSON string in test * feat(google): use formatValueForDisplay in ng-model * feat(google): define json-text directive to handle view * feat(google): add space to JSON format * feat(google): remove hiddenKeys test --- packages/core/src/forms/forms.module.js | 2 + .../mapObjectEditor.component.html | 56 +++++++++ .../mapObjectEditor.component.js | 113 ++++++++++++++++++ .../mapObjectEditor.component.less | 9 ++ .../mapObjectEditor.component.spec.js | 85 +++++++++++++ .../serverGroupCommandBuilder.service.js | 11 ++ .../advancedSettings.directive.html | 11 ++ 7 files changed, 287 insertions(+) create mode 100644 packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.html create mode 100644 packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.js create mode 100644 packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.less create mode 100644 packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.spec.js diff --git a/packages/core/src/forms/forms.module.js b/packages/core/src/forms/forms.module.js index 39b128285a4..c4ff56b08da 100644 --- a/packages/core/src/forms/forms.module.js +++ b/packages/core/src/forms/forms.module.js @@ -7,6 +7,7 @@ import { CORE_FORMS_CHECKLIST_CHECKLIST_DIRECTIVE } from './checklist/checklist. import { CORE_FORMS_CHECKMAP_CHECKMAP_DIRECTIVE } from './checkmap/checkmap.directive'; import { CORE_FORMS_IGNOREEMPTYDELETE_DIRECTIVE } from './ignoreEmptyDelete.directive'; import { CORE_FORMS_MAPEDITOR_MAPEDITOR_COMPONENT } from './mapEditor/mapEditor.component'; +import { CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT } from './mapObjectEditor/mapObjectEditor.component'; import { NUMBER_LIST_COMPONENT } from './numberList/numberList.component'; import { CORE_FORMS_VALIDATEONSUBMIT_VALIDATEONSUBMIT_DIRECTIVE } from './validateOnSubmit/validateOnSubmit.directive'; @@ -18,6 +19,7 @@ module(CORE_FORMS_FORMS_MODULE, [ CORE_FORMS_CHECKMAP_CHECKMAP_DIRECTIVE, CORE_FORMS_IGNOREEMPTYDELETE_DIRECTIVE, CORE_FORMS_MAPEDITOR_MAPEDITOR_COMPONENT, + CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT, CORE_FORMS_VALIDATEONSUBMIT_VALIDATEONSUBMIT_DIRECTIVE, NUMBER_LIST_COMPONENT, ]); diff --git a/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.html b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.html new file mode 100644 index 00000000000..c0a0c6b3f58 --- /dev/null +++ b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.html @@ -0,0 +1,56 @@ +
diff --git a/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.js b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.js new file mode 100644 index 00000000000..651e0980627 --- /dev/null +++ b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.js @@ -0,0 +1,113 @@ +'use strict'; + +import * as angular from 'angular'; +import { isString } from 'lodash'; + +import { CORE_VALIDATION_VALIDATEUNIQUE_DIRECTIVE } from '../../validation/validateUnique.directive'; + +import './mapObjectEditor.component.less'; + +export const CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT = 'spinnaker.core.forms.mapObjectEditor.component'; +export const name = CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT; // for backwards compatibility +angular + .module(CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT, [CORE_VALIDATION_VALIDATEUNIQUE_DIRECTIVE]) + .directive('jsonText', function () { + return { + restrict: 'A', + require: 'ngModel', + link: function (scope, element, attr, ngModel) { + function into(input) { + return JSON.parse(input); + } + function out(data) { + return JSON.stringify(data, null, 2); + } + ngModel.$parsers.push(into); + ngModel.$formatters.push(out); + }, + }; + }) + .component('mapObjectEditor', { + bindings: { + model: '=', + keyLabel: '@', + valueLabel: '@', + addButtonLabel: '@', + allowEmpty: '=?', + onChange: '&', + labelsLeft: '', + label: '@', + hiddenKeys: '<', + }, + controller: [ + '$scope', + function ($scope) { + this.backingModel = []; + + const modelKeys = () => Object.keys(this.model); + + this.addField = () => { + this.backingModel.push({ key: '', value: {}, checkUnique: modelKeys() }); + // do not fire the onChange event, since no values have been committed to the object + }; + + this.removeField = (index) => { + this.backingModel.splice(index, 1); + this.synchronize(); + this.onChange(); + }; + + // Clears existing values from model, then replaces them + this.synchronize = () => { + if (this.isParameterized) { + return; + } + const modelStart = JSON.stringify(this.model); + const allKeys = this.backingModel.map((pair) => pair.key); + modelKeys().forEach((key) => delete this.model[key]); + this.backingModel.forEach((pair) => { + if (pair.key && (this.allowEmpty || pair.value)) { + try { + // Parse value if it is a valid JSON object + this.model[pair.key] = JSON.parse(pair.value); + } catch (e) { + // If value is not a valid JSON object, just store the raw value + this.model[pair.key] = pair.value; + } + } + // include other keys to verify no duplicates + pair.checkUnique = allKeys.filter((key) => pair.key !== key); + }); + if (modelStart !== JSON.stringify(this.model)) { + this.onChange(); + } + }; + + // In Angular 1.7 Directive bindings were removed in the constructor, default values now must be instantiated within $onInit + // See https://docs.angularjs.org/guide/migration#-compile- and https://docs.angularjs.org/guide/migration#migrate1.5to1.6-ng-services-$compile + this.$onInit = () => { + // Set default values for optional fields + this.onChange = this.onChange || angular.noop; + this.keyLabel = this.keyLabel || 'Key'; + this.valueLabel = this.valueLabel || 'Value'; + this.addButtonLabel = this.addButtonLabel || 'Add Field'; + this.allowEmpty = this.allowEmpty || false; + this.labelsLeft = this.labelsLeft || false; + this.tableClass = this.label ? '' : 'no-border-top'; + this.columnCount = this.labelsLeft ? 5 : 3; + this.model = this.model || {}; + this.isParameterized = isString(this.model); + this.hiddenKeys = this.hiddenKeys || []; + + if (this.model && !this.isParameterized) { + modelKeys().forEach((key) => { + this.backingModel.push({ key: key, value: this.model[key] }); + }); + } + }; + + $scope.$watch(() => JSON.stringify(this.backingModel), this.synchronize); + }, + ], + templateUrl: require('./mapObjectEditor.component.html'), + }); diff --git a/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.less b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.less new file mode 100644 index 00000000000..bb67f98d869 --- /dev/null +++ b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.less @@ -0,0 +1,9 @@ +map-object-editor { + .table.no-border-top { + border-top: 2px solid var(--color-white); + + .table-label { + padding: 0.8rem 0 0 1rem; + } + } +} diff --git a/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.spec.js b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.spec.js new file mode 100644 index 00000000000..41be1ae763f --- /dev/null +++ b/packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.spec.js @@ -0,0 +1,85 @@ +'use strict'; + +describe('Component: mapObjectEditor', function () { + var scope; + + beforeEach(window.module(require('./mapObjectEditor.component').name)); + + beforeEach( + window.inject(function ($rootScope, $compile) { + scope = $rootScope.$new(); + this.compile = $compile; + }), + ); + + it('initializes with provided values', function () { + scope.model = { foo: { bar: 'baz' }, bah: 11 }; + let dom = this.compile('