Skip to content

Commit

Permalink
ui: all xhr paths from db console are now relative
Browse files Browse the repository at this point in the history
This commit removes all prefix `/` characters from request paths in
the DB Console and Cluster UI codebases. This ensures that if the
DB Console is proxied at a subpath the requests continue to work as
expected and are relative to the correct base path.

Resolves: cockroachdb/helm-charts#228
Resolves: cockroachdb#91429

Epic: None

Release note (ops change, ui change): The DB Console now constructs
client-side requests using relative URLs instead of absolute ones.
This enables proxying of the DB Console at arbitrary subpaths.
  • Loading branch information
dhartunian committed Aug 29, 2023
1 parent 8645582 commit 54be61f
Show file tree
Hide file tree
Showing 21 changed files with 76 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .node-version
@@ -1 +1 @@
16.13.0
16.14.0
17 changes: 17 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/api/basePath.spec.ts
@@ -0,0 +1,17 @@
// Copyright 2023 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.


describe("withBasePath", () => {
test("format", () => {
type customString = string;
type customNum = number;

})})
32 changes: 31 additions & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/basePath.ts
Expand Up @@ -12,4 +12,34 @@ let path = "";

export const setBasePath = (basePath: string): string => (path = basePath);

export const getBasePath = (): string => path;
// Do not call this directly, use `withBasePath` instead because it
// ensures that if the basePath is blank the paths remain relative.
const getBasePath = (): string => path;

export const withBasePath = (p: string): string => joinPathsEnsureRelative(getBasePath(), p)

// joinPathsEnsureRelative is a utility to ensure that paths relative
// to base don't have a prefix `/` to remain compatible with an empty
// relative basePath. It ensures that we append it to a base correctly,
// regardless of trailing slash on the base path.
//
// Examples:
// joinPathsEnsureRelative("", "/_status") -> Error! Not a relative path.
// joinPathsEnsureRelative("/", "/_status") -> Error! Not a relative path.
// joinPathsEnsureRelative("", "_status") -> "_status"
// joinPathsEnsureRelative("/", "_status") -> "/_status"
// joinPathsEnsureRelative("dbconsole", "_status") -> "dbconsole/_status"
//
const joinPathsEnsureRelative = (left: string, right: string): string => {
if (right.startsWith('/')) {
throw new Error("Application paths must remain compatible with relative base. Remove prefix `/` character.")
}
if (left === "") {
return right
}
if (left.endsWith('/')) {
return left.concat(right)
} else {
return left.concat('/', right)
}
}
9 changes: 3 additions & 6 deletions pkg/ui/workspaces/cluster-ui/src/api/fetchData.ts
Expand Up @@ -10,7 +10,7 @@

import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { RequestError } from "../util";
import { getBasePath } from "./basePath";
import { withBasePath } from "./basePath";

interface ProtoBuilder<
P extends ConstructorType,
Expand Down Expand Up @@ -62,9 +62,8 @@ export const fetchData = <P extends ProtoBuilder<P>, T extends ProtoBuilder<T>>(
params.method = "POST";
params.body = toArrayBuffer(encodedRequest);
}
const basePath = getBasePath();

return fetch(`${basePath}${path}`, params)
return fetch(withBasePath(path), params)
.then(response => {
if (!response.ok) {
return response.arrayBuffer().then(buffer => {
Expand Down Expand Up @@ -113,9 +112,7 @@ export function fetchDataJSON<ResponseType, RequestType>(
params.body = JSON.stringify(reqPayload);
}

const basePath = getBasePath();

return fetch(`${basePath}${path}`, params).then(response => {
return fetch(withBasePath(path), params).then(response => {
if (!response.ok) {
throw new RequestError(
response.statusText,
Expand Down
4 changes: 2 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/api/indexDetailsApi.ts
Expand Up @@ -45,7 +45,7 @@ export const getIndexStats = (
): Promise<TableIndexStatsResponse> => {
return fetchData(
cockroach.server.serverpb.TableIndexStatsResponse,
`/_status/databases/${req.database}/tables/${req.table}/indexstats`,
`_status/databases/${req.database}/tables/${req.table}/indexstats`,
null,
null,
"30M",
Expand All @@ -58,7 +58,7 @@ export const resetIndexStats = (
): Promise<ResetIndexUsageStatsResponse> => {
return fetchData(
cockroach.server.serverpb.ResetIndexUsageStatsResponse,
"/_status/resetindexusagestats",
"_status/resetindexusagestats",
cockroach.server.serverpb.ResetIndexUsageStatsRequest,
req,
"30M",
Expand Down
4 changes: 2 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/api/jobProfilerApi.ts
Expand Up @@ -13,7 +13,7 @@ import { fetchData } from "./fetchData";
import { SqlExecutionRequest, executeInternalSql } from "./sqlApi";
import { propsToQueryString } from "../util";

const JOB_PROFILER_PATH = "/_status/job_profiler_execution_details";
const JOB_PROFILER_PATH = "_status/job_profiler_execution_details";

export type ListJobProfilerExecutionDetailsRequest =
cockroach.server.serverpb.ListJobProfilerExecutionDetailsRequest;
Expand All @@ -30,7 +30,7 @@ export const listExecutionDetailFiles = (
): Promise<cockroach.server.serverpb.ListJobProfilerExecutionDetailsResponse> => {
return fetchData(
cockroach.server.serverpb.ListJobProfilerExecutionDetailsResponse,
`/_status/list_job_profiler_execution_details/${req.job_id}`,
`_status/list_job_profiler_execution_details/${req.job_id}`,
null,
null,
"30M",
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/jobsApi.ts
Expand Up @@ -12,7 +12,7 @@ import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "./fetchData";
import { propsToQueryString } from "../util";

const JOBS_PATH = "/_admin/v1/jobs";
const JOBS_PATH = "_admin/v1/jobs";

export type JobsRequest = cockroach.server.serverpb.JobsRequest;
export type JobsResponse = cockroach.server.serverpb.JobsResponse;
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/livenessApi.ts
Expand Up @@ -11,7 +11,7 @@
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "src/api";

const LIVENESS_PATH = "/_admin/v1/liveness";
const LIVENESS_PATH = "_admin/v1/liveness";

export const getLiveness =
(): Promise<cockroach.server.serverpb.LivenessResponse> => {
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/nodesApi.ts
Expand Up @@ -11,7 +11,7 @@
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "src/api";

const NODES_PATH = "/_status/nodes";
const NODES_PATH = "_status/nodes";

export const getNodes =
(): Promise<cockroach.server.serverpb.NodesResponse> => {
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/sessionsApi.ts
Expand Up @@ -11,7 +11,7 @@
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "src/api";

const SESSIONS_PATH = "/_status/sessions";
const SESSIONS_PATH = "_status/sessions";

export type SessionsRequestMessage =
cockroach.server.serverpb.ListSessionsRequest;
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/sqlApi.ts
Expand Up @@ -67,7 +67,7 @@ export type SqlApiResponse<ResultType> = {

export type SqlApiQueryResponse<Result> = Result & { error?: Error };

export const SQL_API_PATH = "/api/v2/sql/";
export const SQL_API_PATH = "api/v2/sql/";

/**
* executeSql executes the provided SQL statements in a single transaction
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/sqlStatsApi.ts
Expand Up @@ -11,7 +11,7 @@
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "src/api";

const RESET_SQL_STATS_PATH = "/_status/resetsqlstats";
const RESET_SQL_STATS_PATH = "_status/resetsqlstats";

export const resetSQLStats =
(): Promise<cockroach.server.serverpb.ResetSQLStatsResponse> => {
Expand Down
4 changes: 2 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/api/statementsApi.ts
Expand Up @@ -21,8 +21,8 @@ import Long from "long";
import moment from "moment-timezone";

import { AggregateStatistics } from "../statementsTable";
const STATEMENTS_PATH = "/_status/combinedstmts";
const STATEMENT_DETAILS_PATH = "/_status/stmtdetails";
const STATEMENTS_PATH = "_status/combinedstmts";
const STATEMENT_DETAILS_PATH = "_status/stmtdetails";

export type StatementsRequest =
cockroach.server.serverpb.CombinedStatementsStatsRequest;
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/terminateQueryApi.ts
Expand Up @@ -11,7 +11,7 @@
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "src/api";

const STATUS_PREFIX = "/_status";
const STATUS_PREFIX = "_status";

export type CancelSessionRequestMessage =
cockroach.server.serverpb.CancelSessionRequest;
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/userApi.ts
Expand Up @@ -19,7 +19,7 @@ export type UserSQLRolesResponseMessage =
export function getUserSQLRoles(): Promise<UserSQLRolesResponseMessage> {
return fetchData(
cockroach.server.serverpb.UserSQLRolesResponse,
`/_status/sqlroles`,
`_status/sqlroles`,
null,
null,
"30M",
Expand Down
Expand Up @@ -50,7 +50,7 @@ describe("SessionsPage Connections", () => {
await driver.cancelQuery({ node_id: "1" });
assert.deepStrictEqual(
fetchMock.mock.calls[0][0],
"/_status/cancel_query/1",
"_status/cancel_query/1",
);
});
});
Expand All @@ -62,7 +62,7 @@ describe("SessionsPage Connections", () => {
await driver.cancelSession({ node_id: "1" });
assert.deepStrictEqual(
fetchMock.mock.calls[0][0],
"/_status/cancel_session/1",
"_status/cancel_session/1",
);
});
});
Expand Down
Expand Up @@ -22,7 +22,7 @@ import emptyListResultsImg from "src/assets/emptyState/empty-list-results.svg";
import { filterByTimeScale, getDiagnosticsStatus } from "./diagnosticsUtils";
import { EmptyTable } from "src/empty";
import styles from "./diagnosticsView.module.scss";
import { getBasePath, StatementDiagnosticsReport } from "../../api";
import {StatementDiagnosticsReport, withBasePath} from "../../api";
import {
TimeScale,
timeScale1hMinOptions,
Expand Down Expand Up @@ -209,9 +209,9 @@ export class DiagnosticsView extends React.Component<
as="a"
size="small"
intent="tertiary"
href={`${getBasePath()}/_admin/v1/stmtbundle/${
href={withBasePath(`_admin/v1/stmtbundle/${
diagnostic.statement_diagnostics_id
}`}
}`)}
onClick={() =>
this.props.onDownloadDiagnosticBundleClick &&
this.props.onDownloadDiagnosticBundleClick(
Expand Down
Expand Up @@ -30,7 +30,7 @@ import {
} from "src/util";
import styles from "./statementsTableContent.module.scss";
import { EllipsisVertical } from "@cockroachlabs/icons";
import { getBasePath } from "src/api/basePath";
import { withBasePath } from "src/api/basePath";
import { StatementDiagnosticsReport } from "src/api/statementDiagnosticsApi";
import moment from "moment-timezone";

Expand Down Expand Up @@ -135,9 +135,9 @@ export const StatementTableCell = {
name: (
<a
className={cx("diagnostic-report-dropdown-option")}
href={`${getBasePath()}/_admin/v1/stmtbundle/${
href={withBasePath(`_admin/v1/stmtbundle/${
dr.statement_diagnostics_id
}`}
}`)}
>
{`Download ${moment(dr.requested_at).format(
"MMM DD, YYYY [at] H:mm [(UTC)] [diagnostic]",
Expand Down
1 change: 0 additions & 1 deletion pkg/ui/workspaces/db-console/src/redux/analytics.ts
Expand Up @@ -314,7 +314,6 @@ export function initializeAnalytics(store: Store<AdminUIState>) {
analyticsOpts,
);
analytics = new AnalyticsSync(analyticsInstance, store, defaultRedactions);

// Attach a listener to the history object which will track a 'page' event
// whenever the user navigates to a new path.
let lastPageLocation: Location;
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/workspaces/db-console/src/util/dataFromServer.ts
Expand Up @@ -33,7 +33,7 @@ declare global {
}

export function fetchDataFromServer(): Promise<DataFromServer> {
return fetch("/uiconfig", {
return fetch("uiconfig", {
method: "GET",
headers: {
Accept: "application/json",
Expand Down
Expand Up @@ -159,7 +159,7 @@ export const JwtAuthTokenPage = () => {
useEffect(() => {
const { State, Code } = JSON.parse(atob(oidc));

fetch(`/oidc/v1/jwt?state=${State}&code=${Code}`).then(
fetch(`oidc/v1/jwt?state=${State}&code=${Code}`).then(
(response: Response) => {
setLoading(false);
if (response.ok) {
Expand Down

0 comments on commit 54be61f

Please sign in to comment.