diff --git a/components/inspectit-ocelot-configurationserver-ui/src/components/editor/TreeTableEditor.js b/components/inspectit-ocelot-configurationserver-ui/src/components/editor/TreeTableEditor.js index 452a9d221e..8e1ac49060 100644 --- a/components/inspectit-ocelot-configurationserver-ui/src/components/editor/TreeTableEditor.js +++ b/components/inspectit-ocelot-configurationserver-ui/src/components/editor/TreeTableEditor.js @@ -7,8 +7,10 @@ import { Menubar } from 'primereact/menubar'; import { Message } from 'primereact/message'; import { Row } from 'primereact/row'; import { TreeTable } from 'primereact/treetable'; +import { Dropdown } from 'primereact/dropdown'; import PropTypes from 'prop-types'; import React from 'react'; +import axios from '../../lib/axios-api'; // helper for a schema property type constants const schemaType = { @@ -49,6 +51,8 @@ class TreeTableEditor extends React.Component { data: undefined, expandedKeys: DEFAULT_EXPANDED_KEYS, showAll: true, + enumOptions: [], + oldNode: '', }; } @@ -329,6 +333,21 @@ class TreeTableEditor extends React.Component { wrapWithExtras={this.wrapWithExtras} /> ); + case schemaType.ENUM: + if (this.state.oldNode.key != node.key) { + this.fetchDropdownOptions(node.key, type); + this.setState({ oldNode: node, enumOptions: [] }); + } + return ( + + ); case schemaType.INTEGER: return ( { + if (schemaType == 'ENUM') { + axios + .post('/autocomplete', { + path: configPath, + }) + .then((response) => { + const suggestions = response.data; + if (suggestions) { + const result = suggestions.map((suggestion) => { + return { + key: suggestion, + value: suggestion, + }; + }); + + this.setState({ enumOptions: result }); + } + }) + .catch((error) => { + console.warn('Could not fetch autocompletion results.', error); + }); + } + }; + render() { const { loading } = this.props; @@ -464,6 +508,55 @@ var StringEditor = ({ node, onPropValueChange, onPropValueRemove, wrapWithExtras } }; +/** Editor for Enums */ +var EnumEditor = ({ node, options, loadingMessage, onPropValueChange, onPropValueRemove, wrapWithExtras }) => { + const defaultValue = loadingMessage; + let disable = !options.length; + let currentValue = node.value; + if (disable) { + options.push({ + key: loadingMessage, + value: loadingMessage, + }); + currentValue = loadingMessage; + } + + const onChange = (e) => { + onPropValueChange(node.key, e.value); + }; + // component to render + const component = () => ( + + ); + + if (!wrapWithExtras) { + return component(); + } else { + return wrapWithExtras(component, { + node, + defaultSupplier: () => (defaultValue && defaultValue) || 0, + onPropValueChange, + onPropValueRemove, + }); + } +}; + /** Editor for numbers */ var NumberEditor = ({ node, integer, onPropValueChange, onPropValueRemove, wrapWithExtras }) => { const defaultValue = node.value; diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleter.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleter.java index 4319c804e0..b209565275 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleter.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleter.java @@ -33,6 +33,7 @@ public List getSuggestions(List path) { * Returns the names of the properties in a given path * * @param propertyPath The path to a property one wants to recieve the properties of + * * @return The names of the properties of the given path as list */ private List collectProperties(List propertyPath) { @@ -40,6 +41,11 @@ private List collectProperties(List propertyPath) { if (CollectionUtils.isEmpty(propertyPath) || ((propertyPath.size() == 1) && propertyPath.get(0).equals(""))) { return getProperties(InspectitConfig.class); } + if (endType instanceof Class && ((Class) endType).isEnum()) { + return Arrays.stream(((Class) endType).getEnumConstants()) + .map(Object::toString) + .collect(Collectors.toList()); + } if (endType == null || PropertyPathHelper.isTerminal(endType) || PropertyPathHelper.isListOfTerminalTypes(endType) || !(endType instanceof Class)) { return Collections.emptyList(); } @@ -50,6 +56,7 @@ private List collectProperties(List propertyPath) { * Return the properties of a given class * * @param beanClass the class one wants the properties of + * * @return the properties of the given class */ @VisibleForTesting diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java index 7ed39d1c4e..a0d22cf595 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java @@ -28,16 +28,7 @@ void checkFirstLevel() { List result = completer.getSuggestions(input); - assertThat(result).containsExactlyInAnyOrder( - "actions", - "data", - "exclude-lambdas", - "ignored-bootstrap-packages", - "ignored-packages", - "internal", - "rules", - "scopes", - "special"); + assertThat(result).containsExactlyInAnyOrder("actions", "data", "exclude-lambdas", "ignored-bootstrap-packages", "ignored-packages", "internal", "rules", "scopes", "special"); } @Test @@ -61,21 +52,7 @@ void pastList() { List result = completer.getSuggestions(input); - assertThat(result).containsExactlyInAnyOrder( - "config", - "env", - "exporters", - "instrumentation", - "logging", - "metrics", - "plugins", - "privacy", - "publish-open-census-to-bootstrap", - "self-monitoring", - "service-name", - "tags", - "thread-pool-size", - "tracing"); + assertThat(result).containsExactlyInAnyOrder("config", "env", "exporters", "instrumentation", "logging", "metrics", "plugins", "privacy", "publish-open-census-to-bootstrap", "self-monitoring", "service-name", "tags", "thread-pool-size", "tracing"); } @Test @@ -87,6 +64,19 @@ void endsInWildcard() { assertThat(result).isEmpty(); } + @Test + void endsInEnum() { + List input = Arrays.asList("inspectit", "tracing", "add-common-tags"); + + List result = completer.getSuggestions(input); + + assertThat(result).hasSize(4); + assertThat(result).contains("NEVER"); + assertThat(result).contains("ON_GLOBAL_ROOT"); + assertThat(result).contains("ON_LOCAL_ROOT"); + assertThat(result).contains("ALWAYS"); + } + @Test void propertyIsPresentAndReadMethodIsNull() { List input = Arrays.asList("inspectit", "instrumentation", "data", "method_duration", "is-tag"); @@ -149,21 +139,7 @@ public class GetProperties { void getPropertiesInspectit() { List result = completer.getProperties(InspectitConfig.class); - assertThat(result).containsExactlyInAnyOrder( - "config", - "env", - "exporters", - "instrumentation", - "logging", - "metrics", - "plugins", - "privacy", - "publish-open-census-to-bootstrap", - "self-monitoring", - "service-name", - "tags", - "thread-pool-size", - "tracing"); + assertThat(result).containsExactlyInAnyOrder("config", "env", "exporters", "instrumentation", "logging", "metrics", "plugins", "privacy", "publish-open-census-to-bootstrap", "self-monitoring", "service-name", "tags", "thread-pool-size", "tracing"); } } } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/validation/PropertyPathHelper.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/validation/PropertyPathHelper.java index dd48ecdf36..6ccefdd26d 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/validation/PropertyPathHelper.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/validation/PropertyPathHelper.java @@ -22,15 +22,14 @@ public class PropertyPathHelper { * A HashSet of classes which are used as wildcards in the search for properties. If a found class matches one of these * classes, the end of the property path is reached. Mainly used in the search of maps */ - private static final HashSet> TERMINAL_TYPES = new HashSet<>(Arrays.asList(Object.class, String.class, Integer.class, Long.class, - Float.class, Double.class, Character.class, Void.class, - Boolean.class, Byte.class, Short.class, Duration.class, Path.class, URL.class, FileSystemResource.class)); + private static final HashSet> TERMINAL_TYPES = new HashSet<>(Arrays.asList(Object.class, String.class, Integer.class, Long.class, Float.class, Double.class, Character.class, Void.class, Boolean.class, Byte.class, Short.class, Duration.class, Path.class, URL.class, FileSystemResource.class)); /** * Returns the type which can be found at the end of the path. Returns null if the path does not exist * * @param propertyNames The list of properties one wants to check * @param type The type in which the current top-level properties should be found + * * @return The type which can be found at the end of the path. Returns null if the path does not exist */ public Type getPathEndType(List propertyNames, Type type) { @@ -57,6 +56,7 @@ public Type getPathEndType(List propertyNames, Type type) { * * @param propertyNames List of property names * @param mapValueType The type which is given as value type of a map + * * @return True: The type exists
False: the type does not exists */ Type getTypeInMap(List propertyNames, Type mapValueType) { @@ -72,6 +72,7 @@ Type getTypeInMap(List propertyNames, Type mapValueType) { * * @param propertyNames List of property names * @param listValueType The type which is given as value type of a list + * * @return True: The type exists
False: the type does not exists */ Type getTypeInList(List propertyNames, Type listValueType) { @@ -83,14 +84,14 @@ Type getTypeInList(List propertyNames, Type listValueType) { * * @param propertyNames List of property names * @param beanType The bean through which should be searched + * * @return True: the property and all other properties exists
False: At least one of the properties does not exist */ private Type getTypeInBean(List propertyNames, Class beanType) { String propertyName = CaseUtils.kebabCaseToCamelCase(propertyNames.get(0)); - Optional foundProperty = - Arrays.stream(BeanUtils.getPropertyDescriptors(beanType)) - .filter(descriptor -> CaseUtils.compareIgnoreCamelOrKebabCase(propertyName, descriptor.getName())) - .findFirst(); + Optional foundProperty = Arrays.stream(BeanUtils.getPropertyDescriptors(beanType)) + .filter(descriptor -> CaseUtils.compareIgnoreCamelOrKebabCase(propertyName, descriptor.getName())) + .findFirst(); if (foundProperty.isPresent()) { Type propertyType; Method writeMethod = foundProperty.get().getWriteMethod(); @@ -108,16 +109,16 @@ private Type getTypeInBean(List propertyNames, Class beanType) { * Checks if a given type is a terminal type or an enum. * * @param type The type to be checked. + * * @return True: the given type is a terminal or an enum. False: the given type is neither a terminal type nor an enum. */ public boolean isTerminal(Type type) { if (TERMINAL_TYPES.contains(type)) { return true; } else if (type instanceof Class) { - return ((Class) type).isEnum() - || ((Class) type).isPrimitive() - || ApplicationConversionService.getSharedInstance().canConvert(String.class, (Class) type) - || ApplicationConversionService.getSharedInstance().canConvert(Number.class, (Class) type); + return ((Class) type).isEnum() || ((Class) type).isPrimitive() || ApplicationConversionService.getSharedInstance() + .canConvert(String.class, (Class) type) || (ApplicationConversionService.getSharedInstance() + .canConvert(Number.class, (Class) type)); } return false; } @@ -127,6 +128,7 @@ public boolean isTerminal(Type type) { * Every class which is no Collection, Map or terminal (see {@link #isTerminal(Type)} is classified as POJO. * * @param type the type to check + * * @return true if the given type is a pojo. */ public boolean isBean(Type type) { @@ -149,6 +151,7 @@ public boolean isBean(Type type) { * Checks if a given type is a list of terminal types * * @param type + * * @return True: the given type is a list of a terminal type. False: either the given type is not a list or not a list of terminal types */ public boolean isListOfTerminalTypes(Type type) { @@ -161,13 +164,13 @@ public boolean isListOfTerminalTypes(Type type) { return false; } - /** * This method takes an array of strings and returns each entry as ArrayList containing the parts of each element. *

* 'inspectit.hello-i-am-testing' would be returned as {'inspectit', 'helloIAmTesting'} * * @param propertyName A String containing the property path + * * @return a List containing containing the parts of the property path as String */ public List parse(String propertyName) { @@ -190,6 +193,7 @@ public List parse(String propertyName) { * * @param propertyName A String with the path of a property * @param result Reference to the list in which the extracted expressions should be saved in + * * @return the remaining expression */ private String extractExpression(String propertyName, List result) { @@ -237,13 +241,13 @@ private String removeLeadingDot(String string) { } } - /** * Checks if two paths are the same. If one path uses the Wildcard "*", the check with the corresponding literal in the * other path return true. * * @param pathA the first path to be compared * @param pathB the second path to be compared + * * @return */ public boolean comparePaths(List pathA, List pathB) { @@ -265,6 +269,7 @@ public boolean comparePaths(List pathA, List pathB) { * * @param a the first path to be compared * @param a the second path to be compared + * * @return Returns true if each String in the two paths is equal. */ public boolean comparePathsIgnoreCamelOrKebabCase(List a, List b) { @@ -279,7 +284,6 @@ public boolean comparePathsIgnoreCamelOrKebabCase(List a, List b return true; } - /** * Checks if the first given path starts with the second given full path *

@@ -289,6 +293,7 @@ public boolean comparePathsIgnoreCamelOrKebabCase(List a, List b * * @param path The path you want to check * @param prefix The prefix the other path should begin with + * * @return */ public boolean hasPathPrefix(List path, List prefix) {