Skip to content

Commit 03a5c40

Browse files
craig[bot]dhartunian
andcommitted
Merge #94021
94021: ui: dynamically read ui data on js load r=sjbarag a=dhartunian Previous work that dynamically loaded the base ui data that we attach to `Window` did not account for JS code which referenced the `dataFromServer` variable directly in top-level consts. This led to a broken login widget in the top right corner and docs links that would pin to "stable" versions instead of the specific version link of the running cluster. This commit does not completely fix the issue but it applies several mitigations: First, the redux store is now initialized with the loaded copy of the `dataFromServer` info. This ensures that login information is initialized properly. Second, the store is now constructed in the promise we define in `index.tsx` instead of inline in the file in which it's defined, allowing us to control execution ordering. Third, one of the `dataFromServer` usages in the redux login selector is moved to be inside the selector to grab values at runtime. Last, the `docs.ts` file is updated to no longer use `const`s for all the doc link strings and instead use `let` which allows us to reload the links at runtime after we have version data from the server. Resolves #93273 Epic: None Release note (ui change): Secure clusters now show correct login information in the top right corner. Docs links correctly reference the current cluster version when necessary. Release note (bug fix): Secure clusters now show correct login information in the top right corner. Docs links correctly reference the current cluster version when necessary. Co-authored-by: David Hartunian <davidh@cockroachlabs.com>
2 parents 8ae6026 + a903401 commit 03a5c40

4 files changed

Lines changed: 137 additions & 83 deletions

File tree

pkg/ui/workspaces/db-console/src/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,24 @@ import "src/protobufInit";
1515
import { alertDataSync } from "src/redux/alerts";
1616
import { App } from "src/app";
1717
import { history } from "src/redux/history";
18-
import { store } from "src/redux/state";
18+
import { createAdminUIStore } from "src/redux/state";
1919
import "src/redux/analytics";
2020
import {
2121
DataFromServer,
2222
fetchDataFromServer,
23+
getDataFromServer,
2324
setDataFromServer,
2425
} from "src/util/dataFromServer";
26+
import { recomputeDocsURLs } from "src/util/docs";
2527

2628
async function fetchAndRender() {
2729
setDataFromServer(
2830
(await fetchDataFromServer().catch(() => {})) as DataFromServer,
2931
);
3032

33+
const store = createAdminUIStore(history, getDataFromServer());
34+
recomputeDocsURLs();
35+
3136
ReactDOM.render(
3237
<App history={history} store={store} />,
3338
document.getElementById("react-layout"),

pkg/ui/workspaces/db-console/src/redux/login.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class NoLoginState {
109109
export const selectLoginState = createSelector(
110110
(state: AdminUIState) => state.login,
111111
(login: LoginAPIState) => {
112+
const dataFromServer = getDataFromServer();
112113
if (!dataFromServer.ExperimentalUseLogin) {
113114
return new NoLoginState();
114115
}

pkg/ui/workspaces/db-console/src/redux/state.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
RouterState,
2626
} from "connected-react-router";
2727
import { History } from "history";
28-
import { history } from "./history";
2928

3029
import { apiReducersReducer, APIReducersState } from "./apiReducers";
3130
import { hoverReducer, HoverState } from "./hover";
@@ -37,6 +36,7 @@ import { uiDataReducer, UIDataState } from "./uiData";
3736
import { loginReducer, LoginAPIState } from "./login";
3837
import rootSaga from "./sagas";
3938
import { initializeAnalytics } from "./analytics";
39+
import { DataFromServer } from "src/util/dataFromServer";
4040

4141
export interface AdminUIState {
4242
cachedData: APIReducersState;
@@ -51,9 +51,25 @@ export interface AdminUIState {
5151
login: LoginAPIState;
5252
}
5353

54+
const emptyDataFromServer: DataFromServer = {
55+
ExperimentalUseLogin: false,
56+
FeatureFlags: {},
57+
LoggedInUser: "",
58+
LoginEnabled: false,
59+
NodeID: "",
60+
OIDCAutoLogin: false,
61+
OIDCButtonText: "",
62+
OIDCLoginEnabled: false,
63+
Tag: "",
64+
Version: "",
65+
};
66+
5467
// createAdminUIStore is a function that returns a new store for the admin UI.
5568
// It's in a function so it can be recreated as necessary for testing.
56-
export function createAdminUIStore(historyInst: History<any>) {
69+
export function createAdminUIStore(
70+
historyInst: History<any>,
71+
dataFromServer: DataFromServer = emptyDataFromServer,
72+
) {
5773
const sagaMiddleware = createSagaMiddleware();
5874
const routerReducer = connectRouter(historyInst);
5975

@@ -70,6 +86,16 @@ export function createAdminUIStore(historyInst: History<any>) {
7086
uiData: uiDataReducer,
7187
login: loginReducer,
7288
}),
89+
{
90+
login: {
91+
loggedInUser: dataFromServer.LoggedInUser,
92+
error: null,
93+
inProgress: false,
94+
oidcAutoLogin: dataFromServer.OIDCAutoLogin,
95+
oidcLoginEnabled: dataFromServer.OIDCLoginEnabled,
96+
oidcButtonText: dataFromServer.OIDCButtonText,
97+
},
98+
},
7399
compose(
74100
applyMiddleware(thunk, sagaMiddleware, routerMiddleware(historyInst)),
75101
// Support for redux dev tools
@@ -96,8 +122,4 @@ export function createAdminUIStore(historyInst: History<any>) {
96122
return s;
97123
}
98124

99-
const store = createAdminUIStore(history);
100-
101125
export type AppDispatch = ThunkDispatch<AdminUIState, unknown, Action>;
102-
103-
export { store };

pkg/ui/workspaces/db-console/src/util/docs.ts

Lines changed: 102 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -11,82 +11,54 @@
1111
import { getDataFromServer } from "src/util/dataFromServer";
1212

1313
const stable = "stable";
14-
const version = getDataFromServer().Version || stable;
15-
const docsURLBase = "https://www.cockroachlabs.com/docs/" + version;
16-
const docsURLBaseNoVersion = "https://www.cockroachlabs.com/docs/" + stable;
1714

1815
function docsURL(pageName: string): string {
16+
const version = getDataFromServer().Version || stable;
17+
const docsURLBase = "https://www.cockroachlabs.com/docs/" + version;
1918
return `${docsURLBase}/${pageName}`;
2019
}
2120

2221
function docsURLNoVersion(pageName: string): string {
22+
const docsURLBaseNoVersion = "https://www.cockroachlabs.com/docs/" + stable;
2323
return `${docsURLBaseNoVersion}/${pageName}`;
2424
}
2525

26-
export const adminUILoginNoVersion = docsURLNoVersion(
26+
export let adminUILoginNoVersion = docsURLNoVersion(
2727
"ui-overview.html#db-console-security",
2828
);
29-
export const startFlags = docsURL("start-a-node.html#flags");
30-
export const pauseJob = docsURL("pause-job.html");
31-
export const cancelJob = docsURL("cancel-job.html");
32-
export const enableNodeMap = docsURL("enable-node-map.html");
33-
export const configureReplicationZones = docsURL(
34-
"configure-replication-zones.html",
35-
);
36-
export const transactionalPipelining = docsURL(
37-
"architecture/transaction-layer.html#transaction-pipelining",
38-
);
39-
export const adminUIAccess = docsURL("ui-overview.html#db-console-access");
40-
export const howAreCapacityMetricsCalculated = docsURL(
41-
"ui-storage-dashboard.html#capacity-metrics",
42-
);
43-
export const howAreCapacityMetricsCalculatedOverview = docsURL(
44-
"ui-cluster-overview-page.html#capacity-metrics",
45-
);
46-
export const keyValuePairs = docsURL(
47-
"architecture/distribution-layer.html#table-data",
48-
);
49-
export const writeIntents = docsURL(
50-
"architecture/transaction-layer.html#write-intents",
51-
);
52-
export const metaRanges = docsURL(
53-
"architecture/distribution-layer.html#meta-ranges",
54-
);
55-
export const databaseTable = docsURL("ui-databases-page.html");
56-
export const jobTable = docsURL("ui-jobs-page.html");
57-
export const jobStatus = docsURL("ui-jobs-page.html#job-status");
58-
export const jobsPause = docsURL("pause-job");
59-
export const jobsResume = docsURL("resume-job");
60-
export const jobsCancel = docsURL("cancel-job");
61-
export const statementsTable = docsURL("ui-statements-page.html");
62-
export const statementDiagnostics = docsURL(
63-
"ui-statements-page.html#diagnostics",
64-
);
65-
export const statementsSql = docsURL(
66-
"ui-statements-page.html#sql-statement-fingerprints",
67-
);
68-
export const statementsRetries = docsURL(
69-
"transactions.html#transaction-retries",
70-
);
71-
export const transactionRetryErrorReference = docsURL(
72-
"transaction-retry-error-reference.html",
73-
);
74-
export const capacityMetrics = docsURL(
75-
"ui-cluster-overview-page.html#capacity-metrics",
76-
);
77-
export const nodeLivenessIssues = docsURL(
78-
"cluster-setup-troubleshooting.html#node-liveness-issues",
79-
);
80-
export const howItWork = docsURL("cockroach-quit.html#how-it-works");
81-
export const clusterStore = docsURL("cockroach-start.html#store");
82-
export const clusterGlossary = docsURL("architecture/overview.html#glossary");
83-
export const clusterSettings = docsURL("cluster-settings.html");
84-
export const reviewOfCockroachTerminology = docsURL(
85-
"ui-replication-dashboard.html#review-of-cockroachdb-terminology",
86-
);
87-
export const privileges = docsURL("authorization.html#privileges");
88-
export const showSessions = docsURL("show-sessions.html");
89-
export const sessionsTable = docsURL("ui-sessions-page.html");
29+
export let startFlags: string;
30+
export let pauseJob: string;
31+
export let cancelJob: string;
32+
export let enableNodeMap: string;
33+
export let configureReplicationZones: string;
34+
export let transactionalPipelining: string;
35+
export let adminUIAccess: string;
36+
export let howAreCapacityMetricsCalculated: string;
37+
export let howAreCapacityMetricsCalculatedOverview: string;
38+
export let keyValuePairs: string;
39+
export let writeIntents: string;
40+
export let metaRanges: string;
41+
export let databaseTable: string;
42+
export let jobTable: string;
43+
export let jobStatus: string;
44+
export let jobsPause: string;
45+
export let jobsResume: string;
46+
export let jobsCancel: string;
47+
export let statementsTable: string;
48+
export let statementDiagnostics: string;
49+
export let statementsSql: string;
50+
export let statementsRetries: string;
51+
export let transactionRetryErrorReference: string;
52+
export let capacityMetrics: string;
53+
export let nodeLivenessIssues: string;
54+
export let howItWork: string;
55+
export let clusterStore: string;
56+
export let clusterGlossary: string;
57+
export let clusterSettings: string;
58+
export let reviewOfCockroachTerminology: string;
59+
export let privileges: string;
60+
export let showSessions: string;
61+
export let sessionsTable: string;
9062
// Note that these explicitly don't use the current version, since we want to
9163
// link to the most up-to-date documentation available.
9264
export const upgradeCockroachVersion =
@@ -97,16 +69,70 @@ export const enterpriseLicensing =
9769
// Not actually a docs URL.
9870
export const startTrial = "https://www.cockroachlabs.com/pricing/start-trial/";
9971

100-
export const reduceStorageOfTimeSeriesDataOperationalFlags = docsURL(
101-
"operational-faqs.html#can-i-reduce-or-disable-the-storage-of-time-series-data",
102-
);
103-
104-
export const performanceBestPracticesHotSpots = docsURL(
105-
"performance-best-practices-overview.html#hot-spots",
106-
);
72+
export let reduceStorageOfTimeSeriesDataOperationalFlags: string;
73+
export let performanceBestPracticesHotSpots: string;
74+
export let uiDebugPages: string;
75+
export let readsAndWritesOverviewPage: string;
10776

108-
export const uiDebugPages = docsURL("ui-debug-pages.html");
77+
export const recomputeDocsURLs = () => {
78+
adminUILoginNoVersion = docsURLNoVersion(
79+
"ui-overview.html#db-console-security",
80+
);
81+
startFlags = docsURL("start-a-node.html#flags");
82+
pauseJob = docsURL("pause-job.html");
83+
cancelJob = docsURL("cancel-job.html");
84+
enableNodeMap = docsURL("enable-node-map.html");
85+
configureReplicationZones = docsURL("configure-replication-zones.html");
86+
transactionalPipelining = docsURL(
87+
"architecture/transaction-layer.html#transaction-pipelining",
88+
);
89+
adminUIAccess = docsURL("ui-overview.html#db-console-access");
90+
howAreCapacityMetricsCalculated = docsURL(
91+
"ui-storage-dashboard.html#capacity-metrics",
92+
);
93+
howAreCapacityMetricsCalculatedOverview = docsURL(
94+
"ui-cluster-overview-page.html#capacity-metrics",
95+
);
96+
keyValuePairs = docsURL("architecture/distribution-layer.html#table-data");
97+
writeIntents = docsURL("architecture/transaction-layer.html#write-intents");
98+
metaRanges = docsURL("architecture/distribution-layer.html#meta-ranges");
99+
databaseTable = docsURL("ui-databases-page.html");
100+
jobTable = docsURL("ui-jobs-page.html");
101+
jobStatus = docsURL("ui-jobs-page.html#job-status");
102+
jobsPause = docsURL("pause-job");
103+
jobsResume = docsURL("resume-job");
104+
jobsCancel = docsURL("cancel-job");
105+
statementsTable = docsURL("ui-statements-page.html");
106+
statementDiagnostics = docsURL("ui-statements-page.html#diagnostics");
107+
statementsSql = docsURL("ui-statements-page.html#sql-statement-fingerprints");
108+
statementsRetries = docsURL("transactions.html#transaction-retries");
109+
transactionRetryErrorReference = docsURL(
110+
"transaction-retry-error-reference.html",
111+
);
112+
capacityMetrics = docsURL("ui-cluster-overview-page.html#capacity-metrics");
113+
nodeLivenessIssues = docsURL(
114+
"cluster-setup-troubleshooting.html#node-liveness-issues",
115+
);
116+
howItWork = docsURL("cockroach-quit.html#how-it-works");
117+
clusterStore = docsURL("cockroach-start.html#store");
118+
clusterGlossary = docsURL("architecture/overview.html#glossary");
119+
clusterSettings = docsURL("cluster-settings.html");
120+
reviewOfCockroachTerminology = docsURL(
121+
"ui-replication-dashboard.html#review-of-cockroachdb-terminology",
122+
);
123+
privileges = docsURL("authorization.html#privileges");
124+
showSessions = docsURL("show-sessions.html");
125+
sessionsTable = docsURL("ui-sessions-page.html");
126+
reduceStorageOfTimeSeriesDataOperationalFlags = docsURL(
127+
"operational-faqs.html#can-i-reduce-or-disable-the-storage-of-time-series-data",
128+
);
129+
performanceBestPracticesHotSpots = docsURL(
130+
"performance-best-practices-overview.html#hot-spots",
131+
);
132+
uiDebugPages = docsURL("ui-debug-pages.html");
133+
readsAndWritesOverviewPage = docsURLNoVersion(
134+
"architecture/reads-and-writes-overview.html#important-concepts",
135+
);
136+
};
109137

110-
export const readsAndWritesOverviewPage = docsURLNoVersion(
111-
"architecture/reads-and-writes-overview.html#important-concepts",
112-
);
138+
recomputeDocsURLs();

0 commit comments

Comments
 (0)