Skip to content

Commit

Permalink
feat: introducing workspace level ui settings and hide non-global ui …
Browse files Browse the repository at this point in the history
…settings from advance settings page (#8500)

* feat: introducing workspace level ui settings and hide non-global
ui settings from advance settings page

+ make defaultIndex pattern a workspace ui setting when workspace is on
  and make it a global setting when workspace is off

Signed-off-by: Yulong Ruan <[email protected]>

* Changeset file for PR #8500 created/updated

* fix: failed tests

Signed-off-by: Yulong Ruan <[email protected]>

* fix: lint

Signed-off-by: Yulong Ruan <[email protected]>

---------

Signed-off-by: Yulong Ruan <[email protected]>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
(cherry picked from commit 91fd6d3)
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
1 parent fb38d67 commit 577bb93
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 4 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/8500.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Introducing workspace level ui settings and hide non-global ui settings from advance settings page ([#8500](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8500))
5 changes: 5 additions & 0 deletions src/core/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import {
CoreEnvironmentUsageData,
CoreServicesUsageData,
} from './core_usage_data';
import { WorkspaceSetup, WorkspaceStart } from './workspace';

export { CoreUsageData, CoreConfigUsageData, CoreEnvironmentUsageData, CoreServicesUsageData };

Expand Down Expand Up @@ -480,6 +481,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
auditTrail: AuditTrailSetup;
/** {@link DynamicConfigServiceSetup} */
dynamicConfigService: DynamicConfigServiceSetup;
/** {@link WorkspaceSetup} */
workspace: WorkspaceSetup;
}

/**
Expand Down Expand Up @@ -521,6 +524,8 @@ export interface CoreStart {
crossCompatibility: CrossCompatibilityServiceStart;
/** {@link DynamicConfigServiceStart} */
dynamicConfig: DynamicConfigServiceStart;
/** {@link WorkspaceStart} */
workspace: WorkspaceStart;
}

export {
Expand Down
3 changes: 3 additions & 0 deletions src/core/server/internal_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { InternalLoggingServiceSetup } from './logging';
import { CoreUsageDataStart } from './core_usage_data';
import { InternalSecurityServiceSetup } from './security/types';
import { CrossCompatibilityServiceStart } from './cross_compatibility';
import { InternalWorkspaceServiceSetup, InternalWorkspaceServiceStart } from './workspace';

/** @internal */
export interface InternalCoreSetup {
Expand All @@ -72,6 +73,7 @@ export interface InternalCoreSetup {
metrics: InternalMetricsServiceSetup;
security: InternalSecurityServiceSetup;
dynamicConfig: InternalDynamicConfigServiceSetup;
workspace: InternalWorkspaceServiceSetup;
}

/**
Expand All @@ -88,6 +90,7 @@ export interface InternalCoreStart {
coreUsageData: CoreUsageDataStart;
crossCompatibility: CrossCompatibilityServiceStart;
dynamicConfig: InternalDynamicConfigServiceStart;
workspace: InternalWorkspaceServiceStart;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/legacy/legacy_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { auditTrailServiceMock } from '../audit_trail/audit_trail_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { metricsServiceMock } from '../metrics/metrics_service.mock';
import { securityServiceMock } from '../security/security_service.mock';
import { workspaceServiceMock } from '../workspace/mocks';

const MockOsdServer: jest.Mock<OsdServer> = OsdServer as any;

Expand Down Expand Up @@ -114,6 +115,7 @@ beforeEach(() => {
metrics: metricsServiceMock.createInternalSetupContract(),
security: securityServiceMock.createSetupContract(),
dynamicConfig: dynamicConfigServiceMock.createInternalSetupContract(),
workspace: workspaceServiceMock.createInternalSetupContract(),
},
plugins: { 'plugin-id': 'plugin-value' },
uiPlugins: {
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export class LegacyService implements CoreService {
getAsyncLocalStore: startDeps.core.dynamicConfig.getAsyncLocalStore,
createStoreFromRequest: startDeps.core.dynamicConfig.createStoreFromRequest,
},
workspace: startDeps.core.workspace,
};

const router = setupDeps.core.http.createRouter('', this.legacyId);
Expand Down Expand Up @@ -308,6 +309,7 @@ export class LegacyService implements CoreService {
auditTrail: setupDeps.core.auditTrail,
getStartServices: () => Promise.resolve([coreStart, startDeps.plugins, {}]),
security: setupDeps.core.security,
workspace: setupDeps.core.workspace,
};

// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand Down
5 changes: 5 additions & 0 deletions src/core/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { coreUsageDataServiceMock } from './core_usage_data/core_usage_data_serv
import { securityServiceMock } from './security/security_service.mock';
import { crossCompatibilityServiceMock } from './cross_compatibility/cross_compatibility.mock';
import { dynamicConfigServiceMock } from './config/dynamic_config_service.mock';
import { workspaceServiceMock } from './workspace/mocks';

export { configServiceMock } from './config/mocks';
export { dynamicConfigServiceMock } from './config/mocks';
Expand Down Expand Up @@ -170,6 +171,7 @@ function createCoreSetupMock({
.mockResolvedValue([createCoreStartMock(), pluginStartDeps, pluginStartContract]),
security: securityServiceMock.createSetupContract(),
dynamicConfigService: dynamicConfigServiceMock.createSetupContract(),
workspace: workspaceServiceMock.createSetupContract(),
};

return mock;
Expand All @@ -187,6 +189,7 @@ function createCoreStartMock() {
coreUsageData: coreUsageDataServiceMock.createStartContract(),
crossCompatibility: crossCompatibilityServiceMock.createStartContract(),
dynamicConfig: dynamicConfigServiceMock.createStartContract(),
workspace: workspaceServiceMock.createStartContract(),
};

return mock;
Expand All @@ -209,6 +212,7 @@ function createInternalCoreSetupMock() {
metrics: metricsServiceMock.createInternalSetupContract(),
security: securityServiceMock.createSetupContract(),
dynamicConfig: dynamicConfigServiceMock.createInternalSetupContract(),
workspace: workspaceServiceMock.createInternalSetupContract(),
};
return setupDeps;
}
Expand All @@ -225,6 +229,7 @@ function createInternalCoreStartMock() {
coreUsageData: coreUsageDataServiceMock.createStartContract(),
crossCompatibility: crossCompatibilityServiceMock.createStartContract(),
dynamicConfig: dynamicConfigServiceMock.createInternalStartContract(),
workspace: workspaceServiceMock.createInternalStartContract(),
};
return startDeps;
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/plugins/plugin_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
registerAsyncLocalStoreRequestHeader: deps.dynamicConfig.registerAsyncLocalStoreRequestHeader,
getStartService: deps.dynamicConfig.getStartService,
},
workspace: deps.workspace,
};
}

Expand Down Expand Up @@ -282,5 +283,6 @@ export function createPluginStartContext<TPlugin, TPluginDependencies>(
getClient: deps.dynamicConfig.getClient,
createStoreFromRequest: deps.dynamicConfig.createStoreFromRequest,
},
workspace: deps.workspace,
};
}
7 changes: 7 additions & 0 deletions src/core/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { InternalCoreSetup, InternalCoreStart, ServiceConfigDescriptor } from '.
import { CoreUsageDataService } from './core_usage_data';
import { CoreRouteHandlerContext } from './core_route_handler_context';
import { DynamicConfigService } from './config/dynamic_config_service';
import { WorkspaceService } from './workspace/workspace_service';

const coreId = Symbol('core');
const rootConfigPath = '';
Expand All @@ -88,6 +89,7 @@ export class Server {
private readonly plugins: PluginsService;
private readonly savedObjects: SavedObjectsService;
private readonly uiSettings: UiSettingsService;
private readonly workspace: WorkspaceService;
private readonly environment: EnvironmentService;
private readonly metrics: MetricsService;
private readonly httpResources: HttpResourcesService;
Expand Down Expand Up @@ -130,6 +132,7 @@ export class Server {
this.opensearch = new OpenSearchService(core);
this.savedObjects = new SavedObjectsService(core);
this.uiSettings = new UiSettingsService(core);
this.workspace = new WorkspaceService(core);
this.capabilities = new CapabilitiesService(core);
this.environment = new EnvironmentService(core);
this.metrics = new MetricsService(core);
Expand Down Expand Up @@ -199,6 +202,7 @@ export class Server {
http: httpSetup,
savedObjects: savedObjectsSetup,
});
const workspaceSetup = await this.workspace.setup();

const metricsSetup = await this.metrics.setup({ http: httpSetup });

Expand Down Expand Up @@ -247,6 +251,7 @@ export class Server {
metrics: metricsSetup,
security: securitySetup,
dynamicConfig: dynamicConfigServiceSetup,
workspace: workspaceSetup,
};

const pluginsSetup = await this.plugins.setup(coreSetup);
Expand Down Expand Up @@ -285,6 +290,7 @@ export class Server {
soStartSpan?.end();
const capabilitiesStart = this.capabilities.start();
const uiSettingsStart = await this.uiSettings.start();
const workspaceStart = await this.workspace.start();
const metricsStart = await this.metrics.start();
const httpStart = this.http.getStartContract();
const coreUsageDataStart = this.coreUsageData.start({
Expand All @@ -308,6 +314,7 @@ export class Server {
coreUsageData: coreUsageDataStart,
crossCompatibility: crossCompatibilityServiceStart,
dynamicConfig: dynamicConfigServiceStart,
workspace: workspaceStart,
};

const pluginsStart = await this.plugins.start(this.coreStart);
Expand Down
19 changes: 19 additions & 0 deletions src/core/server/workspace/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { PublicMethodsOf } from '@osd/utility-types';

import {
InternalWorkspaceServiceSetup,
InternalWorkspaceServiceStart,
WorkspaceService,
} from './workspace_service';

export { InternalWorkspaceServiceSetup, InternalWorkspaceServiceStart } from './workspace_service';

export type WorkspaceSetup = InternalWorkspaceServiceSetup;
export type WorkspaceStart = InternalWorkspaceServiceStart;

export type IWorkspaceService = PublicMethodsOf<WorkspaceService>;
43 changes: 43 additions & 0 deletions src/core/server/workspace/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { IWorkspaceService } from '.';
import { InternalWorkspaceServiceSetup, InternalWorkspaceServiceStart } from './workspace_service';

const createWorkspaceServiceMock = () => {
const mocked: jest.Mocked<IWorkspaceService> = {

Check warning on line 10 in src/core/server/workspace/mocks.ts

View check run for this annotation

Codecov / codecov/patch

src/core/server/workspace/mocks.ts#L10

Added line #L10 was not covered by tests
setup: jest.fn().mockReturnValue(createInternalSetupContractMock()),
start: jest.fn().mockReturnValue({}),
stop: jest.fn(),
};

return mocked;

Check warning on line 16 in src/core/server/workspace/mocks.ts

View check run for this annotation

Codecov / codecov/patch

src/core/server/workspace/mocks.ts#L16

Added line #L16 was not covered by tests
};

const createInternalSetupContractMock = () => {
const mocked: jest.Mocked<InternalWorkspaceServiceSetup> = {
isWorkspaceEnabled: jest.fn(),
};

return mocked;
};
const createSetupContractMock = createInternalSetupContractMock;

const createInternalStartContractMock = () => {
const mocked: jest.Mocked<InternalWorkspaceServiceStart> = {
isWorkspaceEnabled: jest.fn(),
};

return mocked;
};
const createStartContractMock = createInternalStartContractMock;

export const workspaceServiceMock = {
create: createWorkspaceServiceMock,
createInternalSetupContract: createInternalSetupContractMock,
createInternalStartContract: createInternalStartContractMock,
createSetupContract: createSetupContractMock,
createStartContract: createStartContractMock,
};
53 changes: 53 additions & 0 deletions src/core/server/workspace/workspace_service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';

import { CoreService } from '../../types';
import { CoreContext } from '../core_context';
import { Logger } from '../logging';

export interface InternalWorkspaceServiceSetup {
isWorkspaceEnabled: () => boolean;
}

export interface InternalWorkspaceServiceStart {
isWorkspaceEnabled: () => boolean;
}

/** @internal */
export class WorkspaceService
implements CoreService<InternalWorkspaceServiceSetup, InternalWorkspaceServiceStart> {
private readonly log: Logger;
private readonly config$: Observable<{ enabled: boolean }>;

constructor(private readonly coreContext: CoreContext) {
this.log = this.coreContext.logger.get('workspace-service');
this.config$ = this.coreContext.configService.atPath<{ enabled: boolean }>('workspace');
}

public async setup(): Promise<InternalWorkspaceServiceSetup> {
this.log.debug('Setting up workspace service');

const workspaceConfig = await this.config$.pipe(first()).toPromise();

return {
isWorkspaceEnabled: () => workspaceConfig.enabled,

Check warning on line 38 in src/core/server/workspace/workspace_service.ts

View check run for this annotation

Codecov / codecov/patch

src/core/server/workspace/workspace_service.ts#L38

Added line #L38 was not covered by tests
};
}

public async start(): Promise<InternalWorkspaceServiceStart> {
this.log.debug('Starting workspace service');

const workspaceConfig = await this.config$.pipe(first()).toPromise();

return {
isWorkspaceEnabled: () => workspaceConfig.enabled,

Check warning on line 48 in src/core/server/workspace/workspace_service.ts

View check run for this annotation

Codecov / codecov/patch

src/core/server/workspace/workspace_service.ts#L48

Added line #L48 was not covered by tests
};
}

public async stop() {}
}
1 change: 1 addition & 0 deletions src/core/types/ui_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface DeprecationSettings {
export enum UiSettingScope {
GLOBAL = 'global',
USER = 'user',
WORKSPACE = 'workspace',
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,23 @@ export class AdvancedSettingsComponent extends Component<
mapConfig(config: IUiSettingsClient) {
const all = config.getAll();
return Object.entries(all)
.filter(([, setting]) => setting.scope !== UiSettingScope.USER)
.filter(([, setting]) => {
const scope = setting.scope;
// if scope is not defined, then it's a global ui setting
if (!scope) {
return true;
}

if (typeof scope === 'string') {
return scope === UiSettingScope.GLOBAL;

Check warning on line 183 in src/plugins/advanced_settings/public/management_app/advanced_settings.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/advanced_settings/public/management_app/advanced_settings.tsx#L183

Added line #L183 was not covered by tests
}

if (Array.isArray(scope)) {
return scope.includes(UiSettingScope.GLOBAL);

Check warning on line 187 in src/plugins/advanced_settings/public/management_app/advanced_settings.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/advanced_settings/public/management_app/advanced_settings.tsx#L187

Added line #L187 was not covered by tests
}

return false;

Check warning on line 190 in src/plugins/advanced_settings/public/management_app/advanced_settings.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/advanced_settings/public/management_app/advanced_settings.tsx#L190

Added line #L190 was not covered by tests
})
.map((setting) => {
return toEditableConfig({
def: setting[1],
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class DataServerPlugin
this.autocompleteService.setup(core);
this.dqlTelemetryService.setup(core, { usageCollection });

core.uiSettings.register(getUiSettings());
core.uiSettings.register(getUiSettings(core.workspace.isWorkspaceEnabled()));

Check warning on line 109 in src/plugins/data/server/plugin.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/data/server/plugin.ts#L109

Added line #L109 was not covered by tests

const searchSetup = await this.searchService.setup(core, {
registerFunction: expressions.registerFunction,
Expand Down
11 changes: 9 additions & 2 deletions src/plugins/data/server/ui_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ import { UiSettingsParams } from 'opensearch-dashboards/server';
// @ts-ignore untyped module
import numeralLanguages from '@elastic/numeral/languages';
import { DEFAULT_QUERY_LANGUAGE, UI_SETTINGS } from '../common';
// cannot import from core/server due to src/core/server/saved_objects/opensearch_query.js which
// export { opensearchKuery } from '../../../plugins/data/server';
// eslint-disable-next-line @osd/eslint/no-restricted-paths
import { UiSettingScope } from '../../../core/server/ui_settings/types';

const luceneQueryLanguageLabel = i18n.translate('data.advancedSettings.searchQueryLanguageLucene', {
defaultMessage: 'Lucene',
Expand Down Expand Up @@ -79,7 +83,9 @@ const numeralLanguageIds = [
}),
];

export function getUiSettings(): Record<string, UiSettingsParams<unknown>> {
export function getUiSettings(
workspaceEnabled: boolean
): Record<string, UiSettingsParams<unknown>> {
return {
[UI_SETTINGS.META_FIELDS]: {
name: i18n.translate('data.advancedSettings.metaFieldsTitle', {
Expand Down Expand Up @@ -200,6 +206,7 @@ export function getUiSettings(): Record<string, UiSettingsParams<unknown>> {
defaultMessage: 'The index to access if no index is set',
}),
schema: schema.nullable(schema.string()),
scope: workspaceEnabled ? UiSettingScope.WORKSPACE : UiSettingScope.GLOBAL,
},
[UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX]: {
name: i18n.translate('data.advancedSettings.courier.ignoreFilterTitle', {
Expand Down Expand Up @@ -757,7 +764,7 @@ export function getUiSettings(): Record<string, UiSettingsParams<unknown>> {
}),
value: ['none'],
description: i18n.translate('data.advancedSettings.searchQueryLanguageBlocklistText', {
defaultMessage: `Additional languages that are blocked from being used in the query editor.
defaultMessage: `Additional languages that are blocked from being used in the query editor.
<strong>Note</strong>: DQL and Lucene will not be blocked even if set.`,
}),
schema: schema.arrayOf(schema.string()),
Expand Down

0 comments on commit 577bb93

Please sign in to comment.