diff --git a/src/plugins/workspace/public/application.tsx b/src/plugins/workspace/public/application.tsx
index 5440d98e6945..f85a0ec672c9 100644
--- a/src/plugins/workspace/public/application.tsx
+++ b/src/plugins/workspace/public/application.tsx
@@ -11,6 +11,7 @@ import { WorkspaceFatalError } from './components/workspace_fatal_error';
import { WorkspaceCreatorApp } from './components/workspace_creator_app';
import { WorkspaceUpdaterApp } from './components/workspace_updater_app';
import { WorkspaceListApp } from './components/workspace_list_app';
+import { WorkspaceUpdaterProps } from './components/workspace_updater';
import { Services } from './types';
export const renderCreatorApp = ({ element }: AppMountParameters, services: Services) => {
@@ -26,10 +27,14 @@ export const renderCreatorApp = ({ element }: AppMountParameters, services: Serv
};
};
-export const renderUpdaterApp = ({ element }: AppMountParameters, services: Services) => {
+export const renderUpdaterApp = (
+ { element }: AppMountParameters,
+ services: Services,
+ props: WorkspaceUpdaterProps
+) => {
ReactDOM.render(
-
+
,
element
);
diff --git a/src/plugins/workspace/public/components/workspace_form/types.ts b/src/plugins/workspace/public/components/workspace_form/types.ts
index 8014c2321ad5..592102eecada 100644
--- a/src/plugins/workspace/public/components/workspace_form/types.ts
+++ b/src/plugins/workspace/public/components/workspace_form/types.ts
@@ -35,4 +35,5 @@ export interface WorkspaceFormProps {
onSubmit?: (formData: WorkspaceFormSubmitData) => void;
defaultValues?: WorkspaceFormData;
operationType?: WorkspaceOperationType;
+ restrictedApps?: Set;
}
diff --git a/src/plugins/workspace/public/components/workspace_form/utils.test.ts b/src/plugins/workspace/public/components/workspace_form/utils.test.ts
index 6101bd078831..babaea2208e6 100644
--- a/src/plugins/workspace/public/components/workspace_form/utils.test.ts
+++ b/src/plugins/workspace/public/components/workspace_form/utils.test.ts
@@ -7,6 +7,43 @@ import { AppNavLinkStatus, DEFAULT_APP_CATEGORIES } from '../../../../../core/pu
import { convertApplicationsToFeaturesOrGroups } from './utils';
describe('convertApplicationsToFeaturesOrGroups', () => {
+ it('should not filter out restrict Apps', () => {
+ expect(
+ convertApplicationsToFeaturesOrGroups(
+ [
+ { id: 'foo1', title: 'Foo 1', navLinkStatus: AppNavLinkStatus.hidden },
+ { id: 'foo2', title: 'Foo 2', navLinkStatus: AppNavLinkStatus.visible, chromeless: true },
+ {
+ id: 'foo3',
+ title: 'Foo 3',
+ navLinkStatus: AppNavLinkStatus.visible,
+ category: DEFAULT_APP_CATEGORIES.management,
+ },
+ {
+ id: 'workspace_overview',
+ title: 'Workspace Overview',
+ navLinkStatus: AppNavLinkStatus.visible,
+ },
+ {
+ id: 'bar',
+ title: 'Bar',
+ navLinkStatus: AppNavLinkStatus.visible,
+ },
+ ],
+ new Set(['foo1'])
+ )
+ ).toEqual([
+ {
+ id: 'foo1',
+ name: 'Foo 1',
+ },
+ {
+ id: 'bar',
+ name: 'Bar',
+ },
+ ]);
+ });
+
it('should filter out invisible features', () => {
expect(
convertApplicationsToFeaturesOrGroups([
diff --git a/src/plugins/workspace/public/components/workspace_form/utils.ts b/src/plugins/workspace/public/components/workspace_form/utils.ts
index 5514e6a8fb9c..1f9d202769fd 100644
--- a/src/plugins/workspace/public/components/workspace_form/utils.ts
+++ b/src/plugins/workspace/public/components/workspace_form/utils.ts
@@ -44,18 +44,28 @@ export const getNumberOfErrors = (formErrors: WorkspaceFormErrors) => {
export const convertApplicationsToFeaturesOrGroups = (
applications: Array<
Pick
- >
+ >,
+ restrictedApps?: Set
) => {
const UNDEFINED = 'undefined';
// Filter out all hidden applications and management applications and default selected features
- const visibleApplications = applications.filter(
- ({ navLinkStatus, chromeless, category, id }) =>
+ const visibleApplications = applications.filter(({ navLinkStatus, chromeless, category, id }) => {
+ /**
+ * Restrict apps are apps that can be configured into a workspace, but restrict to access
+ * because the current workspace didn't have the apps configured, such apps should NOT filter out
+ */
+ if (restrictedApps && restrictedApps.has(id)) {
+ return true;
+ }
+
+ return (
navLinkStatus !== AppNavLinkStatus.hidden &&
!chromeless &&
!DEFAULT_SELECTED_FEATURES_IDS.includes(id) &&
category?.id !== DEFAULT_APP_CATEGORIES.management.id
- );
+ );
+ });
/**
*
diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_feature_selector.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_feature_selector.tsx
index da9deb174f52..e860d3a34b53 100644
--- a/src/plugins/workspace/public/components/workspace_form/workspace_feature_selector.tsx
+++ b/src/plugins/workspace/public/components/workspace_form/workspace_feature_selector.tsx
@@ -24,16 +24,19 @@ export interface WorkspaceFeatureSelectorProps {
>;
selectedFeatures: string[];
onChange: (newFeatures: string[]) => void;
+ restrictedApps?: Set;
}
export const WorkspaceFeatureSelector = ({
applications,
selectedFeatures,
onChange,
+ restrictedApps,
}: WorkspaceFeatureSelectorProps) => {
- const featuresOrGroups = useMemo(() => convertApplicationsToFeaturesOrGroups(applications), [
- applications,
- ]);
+ const featuresOrGroups = useMemo(
+ () => convertApplicationsToFeaturesOrGroups(applications, restrictedApps),
+ [applications, restrictedApps]
+ );
const handleFeatureChange = useCallback(
(featureId) => {
diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_form.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_form.tsx
index 69793c75395d..be50228512f2 100644
--- a/src/plugins/workspace/public/components/workspace_form/workspace_form.tsx
+++ b/src/plugins/workspace/public/components/workspace_form/workspace_form.tsx
@@ -136,6 +136,7 @@ export const WorkspaceForm = (props: WorkspaceFormProps) => {
applications={applications}
selectedFeatures={formData.features}
onChange={handleFeaturesChange}
+ restrictedApps={props.restrictedApps}
/>
)}
diff --git a/src/plugins/workspace/public/components/workspace_updater/index.tsx b/src/plugins/workspace/public/components/workspace_updater/index.tsx
index 711f19fd25f6..b00065a00f64 100644
--- a/src/plugins/workspace/public/components/workspace_updater/index.tsx
+++ b/src/plugins/workspace/public/components/workspace_updater/index.tsx
@@ -3,4 +3,4 @@
* SPDX-License-Identifier: Apache-2.0
*/
-export { WorkspaceUpdater } from './workspace_updater';
+export { WorkspaceUpdater, WorkspaceUpdaterProps } from './workspace_updater';
diff --git a/src/plugins/workspace/public/components/workspace_updater/workspace_updater.tsx b/src/plugins/workspace/public/components/workspace_updater/workspace_updater.tsx
index d39ddd650360..b877ccc8191b 100644
--- a/src/plugins/workspace/public/components/workspace_updater/workspace_updater.tsx
+++ b/src/plugins/workspace/public/components/workspace_updater/workspace_updater.tsx
@@ -8,7 +8,7 @@ import { EuiPage, EuiPageBody, EuiPageHeader, EuiPageContent } from '@elastic/eu
import { i18n } from '@osd/i18n';
import { WorkspaceAttribute } from 'opensearch-dashboards/public';
import { useObservable } from 'react-use';
-import { of } from 'rxjs';
+import { BehaviorSubject, of } from 'rxjs';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { WorkspaceForm, WorkspaceFormSubmitData, WorkspaceOperationType } from '../workspace_form';
import { WORKSPACE_OVERVIEW_APP_ID } from '../../../common/constants';
@@ -16,18 +16,23 @@ import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils';
import { WorkspaceClient } from '../../workspace_client';
import { WorkspaceFormData } from '../workspace_form/types';
+export interface WorkspaceUpdaterProps {
+ restrictedApps$?: BehaviorSubject>;
+}
+
function getFormDataFromWorkspace(
currentWorkspace: WorkspaceAttribute | null | undefined
): WorkspaceFormData {
return (currentWorkspace || {}) as WorkspaceFormData;
}
-export const WorkspaceUpdater = () => {
+export const WorkspaceUpdater = (props: WorkspaceUpdaterProps) => {
const {
services: { application, workspaces, notifications, http, workspaceClient },
} = useOpenSearchDashboards<{ workspaceClient: WorkspaceClient }>();
const currentWorkspace = useObservable(workspaces ? workspaces.currentWorkspace$ : of(null));
+ const restrictedApps = useObservable(props.restrictedApps$ ?? of(undefined));
const [currentWorkspaceFormData, setCurrentWorkspaceFormData] = useState(
getFormDataFromWorkspace(currentWorkspace)
);
@@ -108,6 +113,7 @@ export const WorkspaceUpdater = () => {
defaultValues={currentWorkspaceFormData}
onSubmit={handleWorkspaceFormSubmit}
operationType={WorkspaceOperationType.Update}
+ restrictedApps={restrictedApps}
/>
)}
diff --git a/src/plugins/workspace/public/components/workspace_updater_app.tsx b/src/plugins/workspace/public/components/workspace_updater_app.tsx
index e16c9ad72e0f..ab106b5c4b7a 100644
--- a/src/plugins/workspace/public/components/workspace_updater_app.tsx
+++ b/src/plugins/workspace/public/components/workspace_updater_app.tsx
@@ -7,9 +7,9 @@ import React, { useEffect } from 'react';
import { I18nProvider } from '@osd/i18n/react';
import { i18n } from '@osd/i18n';
import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public';
-import { WorkspaceUpdater } from './workspace_updater';
+import { WorkspaceUpdater, WorkspaceUpdaterProps } from './workspace_updater';
-export const WorkspaceUpdaterApp = () => {
+export const WorkspaceUpdaterApp = (props: WorkspaceUpdaterProps) => {
const {
services: { chrome },
} = useOpenSearchDashboards();
@@ -29,7 +29,7 @@ export const WorkspaceUpdaterApp = () => {
return (
-
+
);
};
diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts
index b27c4b3bdd4a..52ba42feb31e 100644
--- a/src/plugins/workspace/public/plugin.ts
+++ b/src/plugins/workspace/public/plugin.ts
@@ -14,6 +14,7 @@ import {
AppNavLinkStatus,
AppUpdater,
AppStatus,
+ DEFAULT_APP_CATEGORIES,
} from '../../../core/public';
import {
WORKSPACE_FATAL_ERROR_APP_ID,
@@ -30,7 +31,11 @@ import { WorkspaceMenu } from './components/workspace_menu/workspace_menu';
import { getWorkspaceColumn } from './components/workspace_column';
import { isAppAccessibleInWorkspace } from './utils';
-type WorkspaceAppType = (params: AppMountParameters, services: Services) => () => void;
+type WorkspaceAppType = (
+ params: AppMountParameters,
+ services: Services,
+ props: Record
+) => () => void;
interface WorkspacePluginSetupDeps {
savedObjectsManagement?: SavedObjectsManagementPluginSetup;
@@ -41,6 +46,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps>
private currentWorkspaceSubscription?: Subscription;
private currentWorkspaceIdSubscription?: Subscription;
private appUpdater$ = new BehaviorSubject(() => undefined);
+ private restrictedApps$ = new BehaviorSubject(new Set());
private _changeSavedObjectCurrentWorkspace() {
if (this.coreStart) {
return this.coreStart.workspaces.currentWorkspaceId$.subscribe((currentWorkspaceId) => {
@@ -65,6 +71,20 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps>
if (isAppAccessibleInWorkspace(app, currentWorkspace)) {
return;
}
+
+ if (app.status === AppStatus.inaccessible) {
+ return;
+ }
+
+ /**
+ * Restricted apps can be configured into a workspace, but they are not configured by the
+ * current workspace. Apps of management category can NOT configured into a workspace, so
+ * needs to be excluded.
+ */
+ if (app.category?.id !== DEFAULT_APP_CATEGORIES.management.id) {
+ this.restrictedApps$.next(this.restrictedApps$.value.add(app.id));
+ }
+
/**
* Change the app to `inaccessible` if it is not configured in the workspace
* If trying to access such app, an "Application Not Found" page will be displayed
@@ -129,7 +149,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps>
workspaceClient,
};
- return renderApp(params, services);
+ return renderApp(params, services, { restrictedApps$: this.restrictedApps$ });
};
// create