Skip to content

Commit

Permalink
feat: make url stateful (opensearch-project#35)
Browse files Browse the repository at this point in the history
* feat: make url stateful

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: optimize code

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: remove useless change

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: optimize url listener

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: make formatUrlWithWorkspaceId extensible

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: modify to related components

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: modify the async format to be sync function

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: modify the async format to be sync function

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* fix: type check

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>

* feat: use path to maintain workspace info

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize code

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize code

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize code

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize code

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize code

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize code

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

---------

Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>
Signed-off-by: SuZhou-Joe <suzhou@amazon.com>
  • Loading branch information
SuZhou-Joe committed Aug 31, 2023
1 parent 147b3e0 commit 02fae17
Show file tree
Hide file tree
Showing 16 changed files with 118 additions and 104 deletions.
1 change: 1 addition & 0 deletions src/core/public/fatal_errors/fatal_errors_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const createWorkspacesSetupContractMock = () => ({
update: jest.fn(),
},
formatUrlWithWorkspaceId: jest.fn(),
setFormatUrlWithWorkspaceId: jest.fn(),
});

const createWorkspacesStartContractMock = createWorkspacesSetupContractMock;
Expand Down
24 changes: 19 additions & 5 deletions src/core/public/http/base_path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,28 @@ import { modifyUrl } from '@osd/std';
export class BasePath {
constructor(
private readonly basePath: string = '',
public readonly serverBasePath: string = basePath
public readonly serverBasePath: string = basePath,
private readonly workspaceBasePath: string = ''
) {}

public get = () => {
return `${this.basePath}${this.workspaceBasePath}`;
};

public getBasePath = () => {
return this.basePath;
};

public prepend = (path: string): string => {
if (!this.get()) return path;
return modifyUrl(path, (parts) => {
if (!parts.hostname && parts.pathname && parts.pathname.startsWith('/')) {
parts.pathname = `${this.get()}${parts.pathname}`;
}
});
};

public prependWithoutWorkspacePath = (path: string): string => {
if (!this.basePath) return path;
return modifyUrl(path, (parts) => {
if (!parts.hostname && parts.pathname && parts.pathname.startsWith('/')) {
Expand All @@ -50,16 +64,16 @@ export class BasePath {
};

public remove = (path: string): string => {
if (!this.basePath) {
if (!this.get()) {
return path;
}

if (path === this.basePath) {
if (path === this.get()) {
return '/';
}

if (path.startsWith(`${this.basePath}/`)) {
return path.slice(this.basePath.length);
if (path.startsWith(`${this.get()}/`)) {
return path.slice(this.get().length);
}

return path;
Expand Down
3 changes: 2 additions & 1 deletion src/core/public/http/http_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export class HttpService implements CoreService<HttpSetup, HttpStart> {
const opensearchDashboardsVersion = injectedMetadata.getOpenSearchDashboardsVersion();
const basePath = new BasePath(
injectedMetadata.getBasePath(),
injectedMetadata.getServerBasePath()
injectedMetadata.getServerBasePath(),
injectedMetadata.getWorkspaceBasePath()
);
const fetchService = new Fetch({ basePath, opensearchDashboardsVersion });
const loadingCount = this.loadingCount.setup({ fatalErrors });
Expand Down
11 changes: 8 additions & 3 deletions src/core/public/http/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,17 @@ export type HttpStart = HttpSetup;
*/
export interface IBasePath {
/**
* Gets the `basePath` string.
* Gets the `basePath + workspace` string.
*/
get: () => string;

/**
* Prepends `path` with the basePath.
* Prepends `path` with the basePath + workspace.
*/
prepend: (url: string) => string;

/**
* Removes the prepended basePath from the `path`.
* Removes the prepended basePath + workspace from the `path`.
*/
remove: (url: string) => string;

Expand All @@ -113,6 +113,11 @@ export interface IBasePath {
* See {@link BasePath.get} for getting the basePath value for a specific request
*/
readonly serverBasePath: string;

/**
* Prepends `path` with the basePath.
*/
prependWithoutWorkspacePath: (url: string) => string;
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/core/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,5 +356,4 @@ export {
WorkspacesService,
WorkspaceAttribute,
WorkspaceFindOptions,
WORKSPACE_ID_QUERYSTRING_NAME,
} from './workspace';
11 changes: 11 additions & 0 deletions src/core/public/injected_metadata/injected_metadata_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
UserProvidedValues,
} from '../../server/types';
import { AppCategory, Branding } from '../';
import { getWorkspaceIdFromUrl } from '../utils';

export interface InjectedPluginMetadata {
id: PluginName;
Expand Down Expand Up @@ -151,6 +152,15 @@ export class InjectedMetadataService {
getSurvey: () => {
return this.state.survey;
},

getWorkspaceBasePath: () => {
const workspaceId = getWorkspaceIdFromUrl(window.location.href);
if (workspaceId) {
return `/w/${workspaceId}`;
}

return '';
},
};
}
}
Expand Down Expand Up @@ -186,6 +196,7 @@ export interface InjectedMetadataSetup {
};
getBranding: () => Branding;
getSurvey: () => string | undefined;
getWorkspaceBasePath: () => string;
}

/** @internal */
Expand Down
1 change: 1 addition & 0 deletions src/core/public/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@
export { shareWeakReplay } from './share_weak_replay';
export { Sha256 } from './crypto';
export { MountWrapper, mountReactNode } from './mount';
export { getWorkspaceIdFromUrl } from './workspace';
15 changes: 15 additions & 0 deletions src/core/public/utils/workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const getWorkspaceIdFromUrl = (url: string): string => {
const regexp = /\/w\/([^\/]*)/;
const urlObject = new URL(url);
const matchedResult = urlObject.pathname.match(regexp);
if (matchedResult) {
return matchedResult[1];
}

return '';
};
2 changes: 0 additions & 2 deletions src/core/public/workspace/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

export const WORKSPACES_API_BASE_URL = '/api/workspaces';

export const WORKSPACE_ID_QUERYSTRING_NAME = '_workspace_id_';

export enum WORKSPACE_ERROR_REASON_MAP {
WORKSPACE_STALED = 'WORKSPACE_STALED',
}
1 change: 0 additions & 1 deletion src/core/public/workspace/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@
export { WorkspacesClientContract, WorkspacesClient } from './workspaces_client';
export { WorkspacesStart, WorkspacesService, WorkspacesSetup } from './workspaces_service';
export type { WorkspaceAttribute, WorkspaceFindOptions } from '../../server/types';
export { WORKSPACE_ID_QUERYSTRING_NAME } from './consts';
30 changes: 10 additions & 20 deletions src/core/public/workspace/workspaces_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import { CoreService } from 'src/core/types';
import { WorkspacesClient, WorkspacesClientContract } from './workspaces_client';
import type { WorkspaceAttribute } from '../../server/types';
import { WORKSPACE_ID_QUERYSTRING_NAME } from './consts';
import { HttpSetup } from '../http';

/**
Expand All @@ -14,43 +13,34 @@ import { HttpSetup } from '../http';
export interface WorkspacesStart {
client: WorkspacesClientContract;
formatUrlWithWorkspaceId: (url: string, id: WorkspaceAttribute['id']) => string;
setFormatUrlWithWorkspaceId: (formatFn: WorkspacesStart['formatUrlWithWorkspaceId']) => void;
}

export type WorkspacesSetup = WorkspacesStart;

function setQuerystring(url: string, params: Record<string, string>): string {
const urlObj = new URL(url);
const searchParams = new URLSearchParams(urlObj.search);

for (const key in params) {
if (params.hasOwnProperty(key)) {
const value = params[key];
searchParams.set(key, value);
}
}

urlObj.search = searchParams.toString();
return urlObj.toString();
}

export class WorkspacesService implements CoreService<WorkspacesSetup, WorkspacesStart> {
private client?: WorkspacesClientContract;
private formatUrlWithWorkspaceId(url: string, id: string) {
return setQuerystring(url, {
[WORKSPACE_ID_QUERYSTRING_NAME]: id,
});
return url;
}
private setFormatUrlWithWorkspaceId(formatFn: WorkspacesStart['formatUrlWithWorkspaceId']) {
this.formatUrlWithWorkspaceId = formatFn;
}
public async setup({ http }: { http: HttpSetup }) {
this.client = new WorkspacesClient(http);
return {
client: this.client,
formatUrlWithWorkspaceId: this.formatUrlWithWorkspaceId,
formatUrlWithWorkspaceId: (url: string, id: string) => this.formatUrlWithWorkspaceId(url, id),
setFormatUrlWithWorkspaceId: (fn: WorkspacesStart['formatUrlWithWorkspaceId']) =>
this.setFormatUrlWithWorkspaceId(fn),
};
}
public async start(): Promise<WorkspacesStart> {
return {
client: this.client as WorkspacesClientContract,
formatUrlWithWorkspaceId: this.formatUrlWithWorkspaceId,
setFormatUrlWithWorkspaceId: (fn: WorkspacesStart['formatUrlWithWorkspaceId']) =>
this.setFormatUrlWithWorkspaceId(fn),
};
}
public async stop() {
Expand Down
19 changes: 19 additions & 0 deletions src/core/server/workspaces/workspaces_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { URL } from 'node:url';
import { CoreService } from '../../types';
import { CoreContext } from '../core_context';
import { InternalHttpServiceSetup } from '../http';
Expand Down Expand Up @@ -43,11 +44,29 @@ export class WorkspacesService
this.logger = coreContext.logger.get('workspaces-service');
}

private proxyWorkspaceTrafficToRealHandler(setupDeps: WorkspacesSetupDeps) {
/**
* Proxy all {basePath}/w/{workspaceId}{osdPath*} paths to
* {basePath}{osdPath*}
*/
setupDeps.http.registerOnPreRouting((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());
}
return toolkit.next();
});
}

public async setup(setupDeps: WorkspacesSetupDeps): Promise<InternalWorkspacesServiceSetup> {
this.logger.debug('Setting up Workspaces service');

this.client = new WorkspacesClientWithSavedObject(setupDeps);
await this.client.setup(setupDeps);
this.proxyWorkspaceTrafficToRealHandler(setupDeps);

registerRoutes({
http: setupDeps.http,
Expand Down
1 change: 0 additions & 1 deletion src/plugins/workspace/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

export const WORKSPACE_APP_ID = 'workspace';
export const WORKSPACE_APP_NAME = 'Workspace';
export const WORKSPACE_ID_IN_SESSION_STORAGE = '_workspace_id_';

export const PATHS = {
create: '/create',
Expand Down
17 changes: 0 additions & 17 deletions src/plugins/workspace/public/components/workspace_overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ import React, { useState } from 'react';
import { EuiPageHeader, EuiButton, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
import { useObservable } from 'react-use';
import { of } from 'rxjs';
import { i18n } from '@osd/i18n';
import { ApplicationStart } from '../../../../core/public';
import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public';
import { PATHS } from '../../common/constants';
import { WORKSPACE_APP_ID, WORKSPACE_ID_IN_SESSION_STORAGE } from '../../common/constants';

export const WorkspaceOverview = () => {
const {
Expand All @@ -22,20 +19,6 @@ export const WorkspaceOverview = () => {
workspaces ? workspaces.client.currentWorkspace$ : of(null)
);

const onUpdateWorkspaceClick = () => {
if (!currentWorkspace || !currentWorkspace.id) {
notifications?.toasts.addDanger({
title: i18n.translate('Cannot find current workspace', {
defaultMessage: 'Cannot update workspace',
}),
});
return;
}
application.navigateToApp(WORKSPACE_APP_ID, {
path: PATHS.update + '?' + WORKSPACE_ID_IN_SESSION_STORAGE + '=' + currentWorkspace.id,
});
};

return (
<>
<EuiPageHeader pageTitle="Overview" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ import { useOpenSearchDashboards } from '../../../../../../src/plugins/opensearc

import { PATHS } from '../../../common/constants';
import { WorkspaceForm, WorkspaceFormData } from '../workspace_creator/workspace_form';
import {
WORKSPACE_APP_ID,
WORKSPACE_ID_IN_SESSION_STORAGE,
WORKSPACE_OP_TYPE_UPDATE,
} from '../../../common/constants';
import { WORKSPACE_APP_ID, WORKSPACE_OP_TYPE_UPDATE } from '../../../common/constants';
import { ApplicationStart } from '../../../../../core/public';
import { DeleteWorkspaceModal } from '../delete_workspace_modal';

Expand Down Expand Up @@ -80,9 +76,14 @@ export const WorkspaceUpdater = () => {
defaultMessage: 'Update workspace successfully',
}),
});
await application.navigateToApp(WORKSPACE_APP_ID, {
path: PATHS.overview + '?' + WORKSPACE_ID_IN_SESSION_STORAGE + '=' + currentWorkspace.id,
});
window.location.href =
workspaces?.formatUrlWithWorkspaceId(
application.getUrlForApp(WORKSPACE_APP_ID, {
path: PATHS.overview,
absolute: true,
}),
currentWorkspace.id
) || '';
return;
}
notifications?.toasts.addDanger({
Expand All @@ -92,7 +93,7 @@ export const WorkspaceUpdater = () => {
text: result?.error,
});
},
[notifications?.toasts, workspaces?.client, currentWorkspace, application]
[notifications?.toasts, workspaces, currentWorkspace, application]
);

if (!currentWorkspaceFormData.name) {
Expand Down
Loading

0 comments on commit 02fae17

Please sign in to comment.