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

allow user to turn on/off workspace from advance settings #46

Merged
merged 4 commits into from
Jul 14, 2023
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
4 changes: 2 additions & 2 deletions src/core/public/chrome/chrome_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export class ChromeService {
});

const getWorkspaceUrl = (id: string) => {
return workspaces?.formatUrlWithWorkspaceId(
return workspaces.formatUrlWithWorkspaceId(
application.getUrlForApp(WORKSPACE_APP_ID, {
path: '/',
absolute: true,
Expand All @@ -194,7 +194,7 @@ export class ChromeService {
const exitWorkspace = async () => {
let result;
try {
result = await workspaces?.client.exitWorkspace();
result = await workspaces.client.exitWorkspace();
} catch (error) {
notifications?.toasts.addDanger({
title: i18n.translate('workspace.exit.failed', {
Expand Down
2 changes: 0 additions & 2 deletions src/core/public/core_app/core_app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,12 @@ import type { IUiSettingsClient } from '../ui_settings';
import type { InjectedMetadataSetup } from '../injected_metadata';
import { renderApp as renderErrorApp, setupUrlOverflowDetection } from './errors';
import { renderApp as renderStatusApp } from './status';
import { WorkspacesSetup } from '../workspace';

interface SetupDeps {
application: InternalApplicationSetup;
http: HttpSetup;
injectedMetadata: InjectedMetadataSetup;
notifications: NotificationsSetup;
workspaces: WorkspacesSetup;
}

interface StartDeps {
Expand Down
6 changes: 3 additions & 3 deletions src/core/public/core_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,14 @@ export class CoreSystem {
const http = this.http.setup({ injectedMetadata, fatalErrors: this.fatalErrorsSetup });
const uiSettings = this.uiSettings.setup({ http, injectedMetadata });
const notifications = this.notifications.setup({ uiSettings });
const workspaces = await this.workspaces.setup({ http });
const workspaces = await this.workspaces.setup({ http, uiSettings });

const pluginDependencies = this.plugins.getOpaqueIds();
const context = this.context.setup({
pluginDependencies: new Map([...pluginDependencies]),
});
const application = this.application.setup({ context, http });
this.coreApp.setup({ application, http, injectedMetadata, notifications, workspaces });
this.coreApp.setup({ application, http, injectedMetadata, notifications });

const core: InternalCoreSetup = {
application,
Expand Down Expand Up @@ -204,7 +204,6 @@ export class CoreSystem {
const uiSettings = await this.uiSettings.start();
const docLinks = this.docLinks.start({ injectedMetadata });
const http = await this.http.start();
const workspaces = await this.workspaces.start();
const savedObjects = await this.savedObjects.start({ http });
const i18n = await this.i18n.start();
const fatalErrors = await this.fatalErrors.start();
Expand All @@ -226,6 +225,7 @@ export class CoreSystem {
targetDomElement: notificationsTargetDomElement,
});
const application = await this.application.start({ http, overlays });
const workspaces = await this.workspaces.start();
const chrome = await this.chrome.start({
application,
docLinks,
Expand Down
5 changes: 5 additions & 0 deletions src/core/public/http/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ export interface IBasePath {
*/
get: () => string;

/**
* Gets the `basePath
*/
getBasePath: () => string;

/**
* Prepends `path` with the basePath + workspace.
*/
Expand Down
9 changes: 5 additions & 4 deletions src/core/public/ui_settings/ui_settings_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import type { PublicMethodsOf } from '@osd/utility-types';
import { UiSettingsService } from './';
import { IUiSettingsClient } from './types';

const createSetupContractMock = () => {
const createUiSettingsClientMock = () => {
const setupContract: jest.Mocked<IUiSettingsClient> = {
getAll: jest.fn(),
get: jest.fn(),
Expand Down Expand Up @@ -66,12 +66,13 @@ const createMock = () => {
stop: jest.fn(),
};

mocked.setup.mockReturnValue(createSetupContractMock());
mocked.setup.mockReturnValue(createUiSettingsClientMock());
mocked.start.mockReturnValue(createUiSettingsClientMock());
return mocked;
};

export const uiSettingsServiceMock = {
create: createMock,
createSetupContract: createSetupContractMock,
createStartContract: createSetupContractMock,
createSetupContract: createUiSettingsClientMock,
createStartContract: createUiSettingsClientMock,
};
8 changes: 5 additions & 3 deletions src/core/public/workspace/workspaces_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ export class WorkspacesClient {
}
}
);
}

/**
* Initialize workspace list
*/
/**
* Initialize workspace list
*/
init() {
this.updateWorkspaceListAndNotify();
}

Expand Down
9 changes: 8 additions & 1 deletion src/core/public/workspace/workspaces_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CoreService } from 'src/core/types';
import { WorkspacesClient, WorkspacesClientContract } from './workspaces_client';
import type { WorkspaceAttribute } from '../../server/types';
import { HttpSetup } from '../http';
import { IUiSettingsClient } from '../ui_settings';

/**
* @public
Expand All @@ -26,8 +27,14 @@ export class WorkspacesService implements CoreService<WorkspacesSetup, Workspace
private setFormatUrlWithWorkspaceId(formatFn: WorkspacesStart['formatUrlWithWorkspaceId']) {
this.formatUrlWithWorkspaceId = formatFn;
}
public async setup({ http }: { http: HttpSetup }) {
public async setup({ http, uiSettings }: { http: HttpSetup; uiSettings: IUiSettingsClient }) {
this.client = new WorkspacesClient(http);

// If workspace was disabled while opening a workspace url, navigate to basePath
if (uiSettings.get('workspace:enabled') === true) {
this.client.init();
}

return {
client: this.client,
formatUrlWithWorkspaceId: (url: string, id: string) => this.formatUrlWithWorkspaceId(url, id),
Expand Down
1 change: 1 addition & 0 deletions src/core/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export class Server {
});
await this.workspaces.start({
savedObjects: savedObjectsStart,
uiSettings: uiSettingsStart,
});

this.coreStart = {
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/ui_settings/settings/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { getNotificationsSettings } from './notifications';
import { getThemeSettings } from './theme';
import { getCoreSettings } from './index';
import { getStateSettings } from './state';
import { getWorkspaceSettings } from './workspace';

describe('getCoreSettings', () => {
it('should not have setting overlaps', () => {
Expand All @@ -48,6 +49,7 @@ describe('getCoreSettings', () => {
getNotificationsSettings(),
getThemeSettings(),
getStateSettings(),
getWorkspaceSettings(),
].reduce((sum, settings) => sum + Object.keys(settings).length, 0);

expect(coreSettingsLength).toBe(summedLength);
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/ui_settings/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { getNavigationSettings } from './navigation';
import { getNotificationsSettings } from './notifications';
import { getThemeSettings } from './theme';
import { getStateSettings } from './state';
import { getWorkspaceSettings } from './workspace';

export const getCoreSettings = (): Record<string, UiSettingsParams> => {
return {
Expand All @@ -46,5 +47,6 @@ export const getCoreSettings = (): Record<string, UiSettingsParams> => {
...getNotificationsSettings(),
...getThemeSettings(),
...getStateSettings(),
...getWorkspaceSettings(),
};
};
25 changes: 25 additions & 0 deletions src/core/server/ui_settings/settings/workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { schema } from '@osd/config-schema';
import { i18n } from '@osd/i18n';
import { UiSettingsParams } from '../../../types';

export const getWorkspaceSettings = (): Record<string, UiSettingsParams> => {
return {
'workspace:enabled': {
name: i18n.translate('core.ui_settings.params.workspace.enableWorkspaceTitle', {
defaultMessage: 'Enable Workspace',
}),
value: false,
requiresPageReload: true,
description: i18n.translate('core.ui_settings.params.workspace.enableWorkspaceTitle', {
defaultMessage: 'Enable or disable OpenSearch Dashboards Workspace',
}),
category: ['workspace'],
schema: schema.boolean(),
},
};
};
29 changes: 23 additions & 6 deletions src/core/server/workspaces/workspaces_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { IWorkspaceDBImpl } from './types';
import { WorkspacesClientWithSavedObject } from './workspaces_client';
import { WorkspacePermissionControl } from './workspace_permission_control';
import { UiSettingsServiceStart } from '../ui_settings/types';

export interface WorkspacesServiceSetup {
client: IWorkspaceDBImpl;
Expand All @@ -37,30 +38,45 @@ export type InternalWorkspacesServiceStart = WorkspacesServiceStart;
/** @internal */
export interface WorkspacesStartDeps {
savedObjects: InternalSavedObjectsServiceStart;
uiSettings: UiSettingsServiceStart;
}

export class WorkspacesService
implements CoreService<WorkspacesServiceSetup, WorkspacesServiceStart> {
private logger: Logger;
private client?: IWorkspaceDBImpl;
private permissionControl?: WorkspacePermissionControl;

private startDeps?: WorkspacesStartDeps;
constructor(coreContext: CoreContext) {
this.logger = coreContext.logger.get('workspaces-service');
}

private proxyWorkspaceTrafficToRealHandler(setupDeps: WorkspacesSetupDeps) {
/**
* Proxy all {basePath}/w/{workspaceId}{osdPath*} paths to
* {basePath}{osdPath*}
* {basePath}{osdPath*} when workspace is enabled
*
* Return HTTP 404 if accessing {basePath}/w/{workspaceId} when workspace is disabled
*/
setupDeps.http.registerOnPreRouting((request, response, toolkit) => {
setupDeps.http.registerOnPreRouting(async (request, response, toolkit) => {
const regexp = /\/w\/([^\/]*)/;
const matchedResult = request.url.pathname.match(regexp);

if (matchedResult) {
const requestUrl = new URL(request.url.toString());
requestUrl.pathname = requestUrl.pathname.replace(regexp, '');
return toolkit.rewriteUrl(requestUrl.toString());
if (this.startDeps) {
const savedObjectsClient = this.startDeps.savedObjects.getScopedClient(request);
const uiSettingsClient = this.startDeps.uiSettings.asScopedToClient(savedObjectsClient);
const workspacesEnabled = await uiSettingsClient.get<boolean>('workspace:enabled');

if (workspacesEnabled) {
const requestUrl = new URL(request.url.toString());
requestUrl.pathname = requestUrl.pathname.replace(regexp, '');
return toolkit.rewriteUrl(requestUrl.toString());
} else {
// If workspace was disable, return HTTP 404
return response.notFound();
}
}
}
return toolkit.next();
});
Expand Down Expand Up @@ -90,6 +106,7 @@ export class WorkspacesService
}

public async start(deps: WorkspacesStartDeps): Promise<InternalWorkspacesServiceStart> {
this.startDeps = deps;
this.logger.debug('Starting SavedObjects service');

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import React, { useState, useCallback, useMemo, useEffect } from 'react';

import { EuiButton, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import useObservable from 'react-use/lib/useObservable';
import { CoreStart, WorkspaceAttribute } from '../../../../../core/public';
import { ApplicationStart, WorkspaceAttribute, WorkspacesStart } from '../../../../../core/public';
import { WORKSPACE_APP_ID, PATHS } from '../../../common/constants';
import { switchWorkspace } from '../../components/utils/workspace';

type WorkspaceOption = EuiComboBoxOptionOption<WorkspaceAttribute>;

interface WorkspaceDropdownListProps {
coreStart: CoreStart;
workspaces: WorkspacesStart;
application: ApplicationStart;
}

function workspaceToOption(workspace: WorkspaceAttribute): WorkspaceOption {
Expand All @@ -27,10 +28,8 @@ export function getErrorMessage(err: any) {
}

export function WorkspaceDropdownList(props: WorkspaceDropdownListProps) {
const { coreStart } = props;

const workspaceList = useObservable(coreStart.workspaces.client.workspaceList$, []);
const currentWorkspace = useObservable(coreStart.workspaces.client.currentWorkspace$, null);
const workspaceList = useObservable(props.workspaces.client.workspaceList$, []);
const currentWorkspace = useObservable(props.workspaces.client.currentWorkspace$, null);

const [loading, setLoading] = useState(false);
const [workspaceOptions, setWorkspaceOptions] = useState([] as WorkspaceOption[]);
Expand Down Expand Up @@ -58,14 +57,14 @@ export function WorkspaceDropdownList(props: WorkspaceDropdownListProps) {
/** switch the workspace */
setLoading(true);
const id = workspaceOption[0].key!;
switchWorkspace(coreStart, id);
switchWorkspace({ workspaces: props.workspaces, application: props.application }, id);
setLoading(false);
},
[coreStart]
[props.application, props.workspaces]
);

const onCreateWorkspaceClick = () => {
coreStart.application.navigateToApp(WORKSPACE_APP_ID, { path: PATHS.create });
props.application.navigateToApp(WORKSPACE_APP_ID, { path: PATHS.create });
};

useEffect(() => {
Expand Down
19 changes: 15 additions & 4 deletions src/plugins/workspace/public/mount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@

import React from 'react';
import ReactDOM from 'react-dom';
import { CoreStart } from '../../../core/public';
import { ApplicationStart, ChromeStart, WorkspacesStart } from '../../../core/public';
import { WorkspaceDropdownList } from './containers/workspace_dropdown_list';

export const mountDropdownList = (core: CoreStart) => {
core.chrome.navControls.registerLeft({
export const mountDropdownList = ({
application,
workspaces,
chrome,
}: {
application: ApplicationStart;
workspaces: WorkspacesStart;
chrome: ChromeStart;
}) => {
chrome.navControls.registerLeft({
order: 0,
mount: (element) => {
ReactDOM.render(<WorkspaceDropdownList coreStart={core} />, element);
ReactDOM.render(
<WorkspaceDropdownList workspaces={workspaces} application={application} />,
element
);
return () => {
ReactDOM.unmountComponentAtNode(element);
};
Expand Down
Loading
Loading