-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat: Made changes to support datasources configuration page for workspace #41324
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
base: release
Are you sure you want to change the base?
Changes from 1 commit
228474d
ad664c8
e4c0ba2
5aa777b
1a30e1b
3f3d190
9698179
ca19f3f
aacb669
547f3f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; | ||
|
|
||
| export interface InitWorkspaceIDEPayload { | ||
| workspaceId: string; | ||
| } | ||
|
|
||
| export const initWorkspaceIDE = (payload: InitWorkspaceIDEPayload) => ({ | ||
| type: ReduxActionTypes.INITIALIZE_WORKSPACE_IDE, | ||
| payload, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import { call, put } from "redux-saga/effects"; | ||
| import CodemirrorTernService from "utils/autocomplete/CodemirrorTernService"; | ||
| import { | ||
| ReduxActionErrorTypes, | ||
| ReduxActionTypes, | ||
| } from "ee/constants/ReduxActionConstants"; | ||
| import { isAirgapped } from "ee/utils/airgapHelpers"; | ||
| import { fetchPluginFormConfigs, fetchPlugins } from "actions/pluginActions"; | ||
| import { | ||
| fetchDatasources, | ||
| fetchMockDatasources, | ||
| } from "actions/datasourceActions"; | ||
| import { failFastApiCalls } from "sagas/InitSagas"; | ||
| import { | ||
| PluginFormConfigsNotFoundError, | ||
| PluginsNotFoundError, | ||
| } from "entities/Engine"; | ||
| import type { ReduxAction } from "actions/ReduxActionTypes"; | ||
| import { waitForFetchEnvironments } from "ee/sagas/EnvironmentSagas"; | ||
| import { fetchingEnvironmentConfigs } from "ee/actions/environmentAction"; | ||
|
|
||
| export default class WorkspaceEditorEngine { | ||
| constructor() { | ||
| this.setupEngine = this.setupEngine.bind(this); | ||
| this.loadWorkspace = this.loadWorkspace.bind(this); | ||
| this.loadPluginsAndDatasources = this.loadPluginsAndDatasources.bind(this); | ||
| this.completeChore = this.completeChore.bind(this); | ||
| } | ||
|
|
||
| *loadWorkspace(workspaceId: string) { | ||
| // Set the current workspace context | ||
| yield put({ | ||
| type: ReduxActionTypes.SET_CURRENT_WORKSPACE_ID, | ||
| payload: { | ||
| workspaceId, | ||
| editorId: workspaceId, // Use workspaceId as editorId for workspace context | ||
| }, | ||
| }); | ||
|
|
||
| // Fetch environment configs for the workspace | ||
| yield put( | ||
| fetchingEnvironmentConfigs({ | ||
| workspaceId, | ||
| editorId: workspaceId, | ||
| fetchDatasourceMeta: true, | ||
| }), | ||
| ); | ||
|
|
||
| // Wait for environments to be fetched | ||
| yield call(waitForFetchEnvironments); | ||
| } | ||
|
|
||
| public *setupEngine() { | ||
| yield put({ type: ReduxActionTypes.START_EVALUATION }); | ||
| CodemirrorTernService.resetServer(); | ||
| } | ||
|
|
||
| *loadPluginsAndDatasources(workspaceId: string) { | ||
| const isAirgappedInstance = isAirgapped(); | ||
| const initActions: ReduxAction<unknown>[] = [ | ||
| fetchPlugins({ workspaceId }), | ||
| fetchDatasources({ workspaceId }), | ||
| ]; | ||
|
|
||
| const successActions = [ | ||
| ReduxActionTypes.FETCH_PLUGINS_SUCCESS, | ||
| ReduxActionTypes.FETCH_DATASOURCES_SUCCESS, | ||
| ]; | ||
|
|
||
| const errorActions = [ | ||
| ReduxActionErrorTypes.FETCH_PLUGINS_ERROR, | ||
| ReduxActionErrorTypes.FETCH_DATASOURCES_ERROR, | ||
| ]; | ||
|
|
||
| if (!isAirgappedInstance) { | ||
| initActions.push(fetchMockDatasources()); | ||
| successActions.push(ReduxActionTypes.FETCH_MOCK_DATASOURCES_SUCCESS); | ||
| errorActions.push(ReduxActionErrorTypes.FETCH_MOCK_DATASOURCES_ERROR); | ||
| } | ||
|
|
||
| const initActionCalls: boolean = yield call( | ||
| failFastApiCalls, | ||
| initActions, | ||
| successActions, | ||
| errorActions, | ||
| ); | ||
|
|
||
| if (!initActionCalls) | ||
| throw new PluginsNotFoundError("Unable to fetch plugins"); | ||
|
|
||
| const pluginFormCall: boolean = yield call( | ||
| failFastApiCalls, | ||
| [fetchPluginFormConfigs()], | ||
| [ReduxActionTypes.FETCH_PLUGIN_FORM_CONFIGS_SUCCESS], | ||
| [ReduxActionErrorTypes.FETCH_PLUGIN_FORM_CONFIGS_ERROR], | ||
| ); | ||
|
|
||
| if (!pluginFormCall) | ||
| throw new PluginFormConfigsNotFoundError( | ||
| "Unable to fetch plugin form configs", | ||
| ); | ||
| } | ||
|
|
||
| *completeChore() { | ||
| yield put({ | ||
| type: ReduxActionTypes.INITIALIZE_WORKSPACE_IDE_SUCCESS, | ||
| }); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ import { APP_MODE } from "entities/App"; | |
| import { generatePath } from "react-router"; | ||
| import getQueryParamsObject from "utils/getQueryParamsObject"; | ||
| import { isNil } from "lodash"; | ||
| import { objectKeys } from "@appsmith/utils"; | ||
|
|
||
| export interface URLBuilderParams { | ||
| suffix?: string; | ||
|
|
@@ -26,6 +27,7 @@ export interface URLBuilderParams { | |
| // This is used to pass ID if the sender doesn't know the type of the entity | ||
| // base version of parent entity id, can be basePageId or moduleId | ||
| baseParentEntityId?: string; | ||
| workspaceId?: string; | ||
| generateEditorPath?: boolean; | ||
| } | ||
|
|
||
|
|
@@ -65,7 +67,7 @@ export interface PageURLParams { | |
| export function getQueryStringfromObject( | ||
| params: Record<string, string> = {}, | ||
| ): string { | ||
| const paramKeys = Object.keys(params); | ||
| const paramKeys = objectKeys(params); | ||
| const queryParams: string[] = []; | ||
|
|
||
| if (paramKeys) { | ||
|
|
@@ -241,9 +243,24 @@ export class URLBuilder { | |
| } | ||
|
|
||
| generateBasePath(basePageId: string, mode: APP_MODE) { | ||
| // Check if we're in workspace context | ||
| if (this.isWorkspaceContext()) { | ||
| return this.generateBasePathForWorkspace(basePageId); | ||
| } | ||
|
|
||
| return this.generateBasePathForApp(basePageId, mode); | ||
| } | ||
|
|
||
| isWorkspaceContext(): boolean { | ||
| const currentUrl = window.location.pathname; | ||
|
|
||
| return currentUrl.startsWith("/workspace"); | ||
| } | ||
|
|
||
| generateBasePathForWorkspace(workspaceId: string) { | ||
| return `/workspace/${workspaceId}/datasources`; | ||
| } | ||
|
|
||
| getCustomSlugPathPreview(basePageId: string, customSlug: string) { | ||
| const urlPattern = | ||
| baseURLRegistry[URL_TYPE.CUSTOM_SLUG][APP_MODE.PUBLISHED]; | ||
|
|
@@ -284,9 +301,26 @@ export class URLBuilder { | |
| } | ||
|
|
||
| resolveEntityId(builderParams: URLBuilderParams): string { | ||
| // Check if we're in workspace context | ||
| if (this.isWorkspaceContext()) { | ||
| return this.resolveEntityIdForWorkspace(builderParams); | ||
| } | ||
|
|
||
| return this.resolveEntityIdForApp(builderParams); | ||
| } | ||
|
|
||
| resolveEntityIdForWorkspace(builderParams: URLBuilderParams): string { | ||
| // Extract workspaceId from current URL if not provided | ||
| if (builderParams?.workspaceId) { | ||
| return builderParams.workspaceId; | ||
| } | ||
|
|
||
| const currentUrl = window.location.pathname; | ||
| const workspaceMatch = currentUrl.match(/^\/workspace\/([^\/]+)/); | ||
|
|
||
| return workspaceMatch ? workspaceMatch[1] : ""; | ||
| } | ||
|
|
||
|
Comment on lines
+312
to
+323
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate workspaceId; don’t fall back to empty string. Returning "" yields “/workspace//…” URLs. Align with app path behavior and throw. - const workspaceMatch = currentUrl.match(/^\/workspace\/([^\/]+)/);
-
- return workspaceMatch ? workspaceMatch[1] : "";
+ const workspaceMatch = currentUrl.match(/^\/workspace\/([^\/]+)/);
+ const id = workspaceMatch ? workspaceMatch[1] : "";
+ if (!id) {
+ throw new URIError("Missing workspaceId. Provide builderParams.workspaceId or navigate under /workspace/:workspaceId.");
+ }
+ return id;🤖 Prompt for AI Agents |
||
| /** | ||
| * @throws {URIError} | ||
| * @param builderParams | ||
|
|
@@ -304,6 +338,14 @@ export class URLBuilder { | |
|
|
||
| const entityId = this.resolveEntityId(builderParams); | ||
|
|
||
| // Handle workspace-specific suffix modification | ||
| let modifiedSuffix = suffix; | ||
|
|
||
| if (this.isWorkspaceContext() && suffix?.startsWith("datasource/")) { | ||
| // For workspace context, remove the 'datasource/' prefix from suffix | ||
| modifiedSuffix = suffix.replace("datasource/", ""); | ||
| } | ||
|
|
||
| const basePath = this.generateBasePath(entityId, mode); | ||
|
|
||
| const queryParamsToPersist = fetchQueryParamsToPersist( | ||
|
|
@@ -320,7 +362,7 @@ export class URLBuilder { | |
|
|
||
| const queryString = getQueryStringfromObject(modifiedQueryParams); | ||
|
|
||
| const suffixPath = suffix ? `/${suffix}` : ""; | ||
| const suffixPath = modifiedSuffix ? `/${modifiedSuffix}` : ""; | ||
|
|
||
| const hashPath = hash ? `#${hash}` : ""; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,7 @@ import type { UpdateCanvasPayload } from "actions/pageActions"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const initialState: EditorReduxState = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| widgetConfigBuilt: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initialized: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isWorkspaceEditorInitialized: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loadingStates: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| publishing: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| publishingError: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -65,6 +66,14 @@ export const handlers = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS]: (state: EditorReduxState) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { ...state, initialized: true }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ReduxActionTypes.INITIALIZE_WORKSPACE_IDE]: (state: EditorReduxState) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { ...state, isWorkspaceEditorInitialized: false }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ReduxActionTypes.INITIALIZE_WORKSPACE_IDE_SUCCESS]: ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: EditorReduxState, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { ...state, isWorkspaceEditorInitialized: true }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+76
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle initialization error to avoid infinite spinner. Add INITIALIZE_WORKSPACE_IDE_ERROR handler and store the error so UI can show a fallback. [ReduxActionTypes.INITIALIZE_WORKSPACE_IDE]: (state: EditorReduxState) => {
return { ...state, isWorkspaceEditorInitialized: false };
},
[ReduxActionTypes.INITIALIZE_WORKSPACE_IDE_SUCCESS]: (
state: EditorReduxState,
) => {
return { ...state, isWorkspaceEditorInitialized: true };
},
+ [ReduxActionErrorTypes.INITIALIZE_WORKSPACE_IDE_ERROR]: (
+ state: EditorReduxState,
+ action: ReduxAction<{ error: unknown }>,
+ ) => {
+ return {
+ ...state,
+ isWorkspaceEditorInitialized: false,
+ workspaceEditorInitError: action.payload?.error,
+ };
+ },Additionally add the field to state shape and initial state: // initialState
onLoadActionExecution: false,
+ workspaceEditorInitError: undefined, export interface EditorReduxState {
...
onLoadActionExecution?: boolean;
+ workspaceEditorInitError?: unknown;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ReduxActionTypes.UPDATE_PAGE_SUCCESS]: ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: EditorReduxState, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: ReduxAction<UpdatePageResponse>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -318,6 +327,7 @@ const editorReducer = createReducer(initialState, handlers); | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface EditorReduxState { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| widgetConfigBuilt: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initialized: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isWorkspaceEditorInitialized: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pageWidgetId?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentLayoutId?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentPageName?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { all, put, call, takeLatest } from "redux-saga/effects"; | ||
|
|
||
| import { | ||
| ReduxActionErrorTypes, | ||
| ReduxActionTypes, | ||
| } from "ee/constants/ReduxActionConstants"; | ||
| import WorkspaceEditorEngine from "ee/entities/Engine/WorkspaceEditorEngine"; | ||
| import type { ReduxAction } from "actions/ReduxActionTypes"; | ||
| import type { InitWorkspaceIDEPayload } from "ee/actions/workspaceIDEActions"; | ||
| import { resetEditorRequest } from "actions/initActions"; | ||
|
|
||
| export function* startWorkspaceIDE( | ||
| action: ReduxAction<InitWorkspaceIDEPayload>, | ||
| ) { | ||
| try { | ||
| const workspaceEngine = new WorkspaceEditorEngine(); | ||
| const { workspaceId } = action.payload; | ||
|
|
||
| /** | ||
| * During editor switches like app (view mode) -> workspace | ||
| * there are certain cases were stale data stays in reducers. | ||
| * This ensures a clean state of reducers and avoids any dependency | ||
| */ | ||
| yield put(resetEditorRequest()); | ||
| yield call(workspaceEngine.setupEngine); | ||
| yield call(workspaceEngine.loadWorkspace, workspaceId); | ||
| yield call(workspaceEngine.loadPluginsAndDatasources, workspaceId); | ||
| yield call(workspaceEngine.completeChore); | ||
| } catch (error) { | ||
| yield put({ | ||
| type: ReduxActionErrorTypes.INITIALIZE_WORKSPACE_IDE_ERROR, | ||
| payload: { | ||
| error, | ||
| }, | ||
| }); | ||
| } | ||
|
Comment on lines
+27
to
+33
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surface error state for UI. INITIALIZE_WORKSPACE_IDE_ERROR is dispatched, but there’s no reducer handling to unblock or show an error. Pair this with a reducer flag and UI fallback. See reducer comment for concrete diff. 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| export default function* watchWorkspaceIDESagas() { | ||
| yield all([ | ||
| takeLatest(ReduxActionTypes.INITIALIZE_WORKSPACE_IDE, startWorkspaceIDE), | ||
| ]); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from "ce/actions/workspaceIDEActions"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { default } from "ce/entities/Engine/WorkspaceEditorEngine"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export * from "ce/sagas/workspaceIDESagas"; | ||
| export { default } from "ce/sagas/workspaceIDESagas"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
LGTM — new IDE type addition is consistent.
No concerns here. Please ensure downstream switch/route maps cover IDE_TYPE.Workspace.
Run to spot any missing cases:
🏁 Script executed:
Length of output: 8283
Length of output: 8750
🏁 Script executed:
Length of output: 498
🏁 Script executed:
Length of output: 386
🏁 Script executed:
Length of output: 528
🏁 Script executed:
Length of output: 434
🏁 Script executed:
Length of output: 168
🏁 Script executed:
Length of output: 0
🏁 Script executed:
Length of output: 784
🏁 Script executed:
Length of output: 179
🏁 Script executed:
Length of output: 53
🏁 Script executed:
Length of output: 44
🏁 Script executed:
Length of output: 2489
Add case for IDE_TYPE.Workspace in getIDEFocusStrategy or update default handler.
The switch statement in
app/client/src/ce/navigation/FocusStrategy/index.tsonly handlesIDE_TYPE.NoneandIDE_TYPE.App, butIDE_TYPE.Workspaceis now inIDEBasePaths. SinceFocusRetentionSagacallsgetIDEFocusStrategy(ideType)whereideTypecomes fromgetIDETypeByUrl(), this will throw an error if Workspace paths are configured.Currently,
IDE_TYPE.Workspacehas an empty path array, so it won't trigger at runtime yet—but this is a latent bug waiting to happen.🤖 Prompt for AI Agents