/*! * SAPUI5 * Copyright (c) 2025 SAP SE or an SAP affiliate company. All rights reserved. */ import SearchConfigurationSettings, { defaultSearchConfigurationSettings, deprecatedParameters, urlForbiddenParameters, } from "./SearchConfigurationSettings"; import Log from "sap/base/Log"; import assert from "sap/base/assert"; import { SelectionMode } from "./SelectionMode"; import { ProgramError } from "./error/errors"; import { searchConfigurationSafeAssigment } from "./searchConfigurationSafeAssigment"; import { SearchDataSourceConfigurations } from "./SearchDataSourceConfigurations"; export default class SearchConfiguration extends SearchConfigurationSettings { private log = Log.getLogger("sap.esh.search.ui.SearchConfiguration"); // marker that FF_useWebComponentsSearchInput is set by URL // necessary in order to avoid overwrite of URL value in SearchShellHelper private isSetByUrl_FF_useWebComponentsSearchInput = false; public dataSourceConfigurations = new SearchDataSourceConfigurations(); /** * @this SearchConfiguration * @constructor */ constructor(configuration?: Partial | SearchConfiguration) { super(); SearchConfiguration.extendSearchConfiguration(this, configuration); if (this.isUshell) { this.aiNlq = true; this.aiNlqExplainBar = true; this.aiSuggestions = true; this.enableCharts = true; this.reloadOnUrlChange = false; this.readUshellConfiguration(); this.readOutdatedUshellConfiguration(); } this.updateConfigFromUrlParameters(); this.dataSourceConfigurations.initDataSourceConfig(configuration?.dataSources); this.checkForDeprecatedParameters(configuration); this.initWebCompFeatureFlags(); } private checkForDeprecatedParameters( configuration?: Partial | SearchConfiguration ): void { for (const parameter in deprecatedParameters) { if (configuration && Object.prototype.hasOwnProperty.call(configuration, parameter)) { // if the provided value is the same as the default, skip the assertion if (configuration[parameter] === defaultSearchConfigurationSettings[parameter]) { continue; } // stakeholder explicitly set this property let msg = `You are using a deprecated configuration property for SearchCompositeControl which will be removed in a future release: '${parameter}'.`; const replacement = deprecatedParameters[parameter]; if (replacement.replacedBy) { // do auto-assignment old => new if (replacement.replacedBy === "enableMultiSelectionResultItems") { if (this[parameter] === true) { this.resultviewSelectionMode = SelectionMode.MultipleItems; } else { this.resultviewSelectionMode = SelectionMode.None; } } else { this[replacement.replacedBy] = this[parameter]; } msg += `\nPlease use '${replacement.replacedBy}' instead.`; } if (replacement.replacementInfo) { msg += `\n${replacement.replacementInfo}`; } assert(false, msg); } } } private readUshellConfiguration(): void { // read global config try { const config = window["sap-ushell-config"].renderers.fiori2.componentData.config.esearch; SearchConfiguration.extendSearchConfiguration(this, config); } catch (e) { this.log.debug("Could not read ushell configuration", e); } } private static extendSearchConfiguration(target: T, ...sources: any[]): T { // we calso take over properties having value null/undefined. // - it is currently possible to wipe out properties like 'performanceLogger' (ELISA default) // - we might want to use more sophisticated merging logic in the future return Object.assign(target, ...sources); } readOutdatedUshellConfiguration(): void { try { // get config const config = window["sap-ushell-config"].renderers.fiori2.componentData.config; // due to historical reasons the config parameter searchBusinessObjects is not in esearch but in parent object // copy this parameter to config object if (typeof config.searchBusinessObjects !== "undefined") { if (config.searchBusinessObjects === "hidden" || config.searchBusinessObjects === false) { this.searchBusinessObjects = false; } else { this.searchBusinessObjects = true; } } } catch (e) { this.log.debug("Could not read ushell configuration", e); } } getParameterType(parameterName): string { if (parameterName in deprecatedParameters) { if (deprecatedParameters[parameterName]) { // if there a replacement use type from replacement parameterName = deprecatedParameters[parameterName].replacedBy; } else { return "string"; // just return something so that following logic for printing outdated message works. } } // eslint-disable-next-line no-prototype-builtins if (!defaultSearchConfigurationSettings.hasOwnProperty(parameterName)) { return ""; } return typeof defaultSearchConfigurationSettings[parameterName]; } parseBoolean(value: string): boolean { if (value.toLowerCase() === "true") { return true; } return false; } parseEsDevConfig(value: string) { const config = JSON.parse(value); SearchConfiguration.extendSearchConfiguration( this, searchConfigurationSafeAssigment.makeSafe(config) ); } updateConfigFromUrlParameters(): void { const urlParameters = this.parseUrlParameters(); for (const parameterName in urlParameters) { const parameterValue = urlParameters[parameterName]; // ignore forbidden parameters if (urlForbiddenParameters.includes(parameterName)) { continue; } // ignore sina url parameters (these parameters are handled by sina itself, see sinaFactory) if (parameterName.startsWith("sina")) { continue; } // special handling for parameter demoMode if (parameterName === "demoMode") { this.searchBusinessObjects = true; continue; } // special handling for parameter resultViewTypes if (parameterName === "resultViewTypes") { let resultViewTypes = parameterValue.split(","); // convert to array resultViewTypes = resultViewTypes.filter((resultViewType) => resultViewType.length > 0); // remove empty element searchConfigurationSafeAssigment.setProperty(this, "resultViewTypes", resultViewTypes); continue; } // special handling for suggestionsLimit / suggestionsLimitDsAll if ( parameterName.endsWith("SuggestionsLimit") || parameterName.endsWith("SuggestionsLimitDsAll") ) { searchConfigurationSafeAssigment.setProperty(this, parameterName, parseInt(parameterValue)); continue; } // special handling for parameter esDevConfig if (parameterName === "esDevConfig") { this.parseEsDevConfig(parameterValue); continue; } // special handling for parameter FF_useWebComponentsSearchInput if (parameterName === "FF_useWebComponentsSearchInput") { searchConfigurationSafeAssigment.setProperty( this, "FF_useWebComponentsSearchInput", this.parseBoolean(parameterValue) ); this.isSetByUrl_FF_useWebComponentsSearchInput = true; continue; } // default parameter handling const parameterType = this.getParameterType(parameterName); switch (parameterType) { case "string": searchConfigurationSafeAssigment.setProperty(this, parameterName, parameterValue); break; case "number": searchConfigurationSafeAssigment.setProperty( this, parameterName, parseInt(parameterValue) ); break; case "boolean": searchConfigurationSafeAssigment.setProperty( this, parameterName, this.parseBoolean(parameterValue) ); break; default: // ignore parameters not defined in SearchConfigurationSettings } } } parseUrlParameters(): { [name: string]: string } { if (!URLSearchParams) { return {}; } const urlSearchParams = new URLSearchParams(window.location.search); return Object.fromEntries(urlSearchParams.entries()); } webCompFeatureFlagPromise: Promise; webCompFeatureFlagPromiseResolve: () => void; webCompFeatureFlagPromiseIsResolved = false; initWebCompFeatureFlags(): Promise { if (this.webCompFeatureFlagPromise) { return this.webCompFeatureFlagPromise; } this.webCompFeatureFlagPromise = new Promise((resolve) => { this.webCompFeatureFlagPromiseResolve = resolve; }); if (!this.isUshell) { // not ushell: setUseWebComponentsSearchInput does not need to be called // therefor promise immediately is resolved this.webCompFeatureFlagPromiseResolve(); this.webCompFeatureFlagPromiseIsResolved = true; } return this.webCompFeatureFlagPromise; } setUseWebComponentsSearchInput(FF_useWebComponentsSearchInput: boolean) { // do not overwrite URL value if (!this.isSetByUrl_FF_useWebComponentsSearchInput) { this.FF_useWebComponentsSearchInput = FF_useWebComponentsSearchInput; } // resolve initialization promise this.webCompFeatureFlagPromiseResolve(); this.webCompFeatureFlagPromiseIsResolved = true; } isWebCompSearchFieldGroupEnabled(): boolean { if (this.isUshell) { // ushell (FLP) : check that initialization (SearchShellHelper calls setUseWebComponentsSearchInput) has happenend if (!this.webCompFeatureFlagPromiseIsResolved) { new ProgramError( null, "checking feature flags without finished initialization is not allowed, call initWebCompFeatureFlags" ); } } return this.FF_useWebComponentsSearchInput; } }