-
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 6 commits
228474d
ad664c8
e4c0ba2
5aa777b
1a30e1b
3f3d190
9698179
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,7 +338,15 @@ export class URLBuilder { | |
|
|
||
| const entityId = this.resolveEntityId(builderParams); | ||
|
|
||
| const basePath = this.generateBasePath(entityId, mode); | ||
| // Handle workspace-specific URL generation | ||
| let basePath = this.generateBasePath(entityId, mode); | ||
| let suffixPath = suffix ? `/${suffix}` : ""; | ||
|
|
||
| if (this.isWorkspaceContext() && suffix?.startsWith("datasource/")) { | ||
| // For workspace datasource URLs, use singular /datasource path | ||
| basePath = `/workspace/${entityId}/datasource`; | ||
| suffixPath = `/${suffix.replace("datasource/", "")}`; | ||
| } | ||
|
|
||
| const queryParamsToPersist = fetchQueryParamsToPersist( | ||
| persistExistingParams, | ||
|
|
@@ -320,8 +362,6 @@ export class URLBuilder { | |
|
|
||
| const queryString = getQueryStringfromObject(modifiedQueryParams); | ||
|
|
||
| const suffixPath = suffix ? `/${suffix}` : ""; | ||
|
|
||
| const hashPath = hash ? `#${hash}` : ""; | ||
|
|
||
| // hash fragment should be at the end of the href | ||
|
|
||
| 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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
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