Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions localization/l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,10 @@
"Clear cache and refresh token": "Clear cache and refresh token",
"Clear token cache": "Clear token cache",
"No workspaces found. Please change Fabric account or tenant to view available workspaces.": "No workspaces found. Please change Fabric account or tenant to view available workspaces.",
"Unable to enforce default User Connections group: {0}/{0} is the error message": {
"message": "Unable to enforce default User Connections group: {0}",
"comment": ["{0} is the error message"]
},
"Add Firewall Rule to {0}/{0} is the server name": {
"message": "Add Firewall Rule to {0}",
"comment": ["{0} is the server name"]
Expand Down
4 changes: 4 additions & 0 deletions localization/xliff/vscode-mssql.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -3295,6 +3295,10 @@
<trans-unit id="++CODE++baaddf70fb5d432b8bd948ef91d6f910124a6d138edae4d5f000c4610ddc8eae">
<source xml:lang="en">Type</source>
</trans-unit>
<trans-unit id="++CODE++42e6c5ad1e91dbc1117d11a06c9c8e77abe8ab091d68d4447b85e9c124c3e0ee">
<source xml:lang="en">Unable to enforce default User Connections group: {0}</source>
<note>{0} is the error message</note>
</trans-unit>
<trans-unit id="++CODE++e395097d9cde221cdb9af8b04e1073d0d3394439fce660d1c954d012cdeae6c1">
<source xml:lang="en">Unable to execute the command while the extension is initializing. Please try again later.</source>
</trans-unit>
Expand Down
21 changes: 21 additions & 0 deletions src/connectionconfig/connectionDialogWebviewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,23 @@ export class ConnectionDialogWebviewController extends FormWebviewController<
this.state.connectionProfile.groupId = initialConnectionGroup.id;
}

// Enforce default group selection: if groupId is missing or points to ROOT, set to User Connections
try {
const rootGroupId = this._mainController.connectionManager.connectionStore.rootGroupId;
if (
!this.state.connectionProfile.groupId ||
this.state.connectionProfile.groupId === rootGroupId
) {
const userGroupId =
this._mainController.connectionManager.connectionStore.connectionConfig.getUserConnectionsGroupId();
if (userGroupId) {
this.state.connectionProfile.groupId = userGroupId;
}
}
} catch (err) {
this.logger.error(Loc.unableToEnforceDefaultUserConnectionsGroup(getErrorMessage(err)));
}

await this.updateItemVisibility();
}

Expand Down Expand Up @@ -1076,6 +1093,10 @@ export class ConnectionDialogWebviewController extends FormWebviewController<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
cleanedConnection as any,
);
// Refresh the Object Explorer tree to include new connections/groups
if (self._objectExplorerProvider?.objectExplorerService?.refreshTree) {
await self._objectExplorerProvider.objectExplorerService.refreshTree();
}
const node =
await self._mainController.createObjectExplorerSession(cleanedConnection);
await self.updateLoadedConnections(state);
Expand Down
415 changes: 352 additions & 63 deletions src/connectionconfig/connectionconfig.ts

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/constants/locConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,14 @@ export class ConnectionDialog {
public static noWorkspacesFound = l10n.t(
"No workspaces found. Please change Fabric account or tenant to view available workspaces.",
);

public static unableToEnforceDefaultUserConnectionsGroup(error: string) {
return l10n.t({
message: "Unable to enforce default User Connections group: {0}",
args: [error],
comment: ["{0} is the error message"],
});
}
}

export class FirewallRule {
Expand Down
86 changes: 51 additions & 35 deletions src/controllers/connectionGroupWebviewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,44 +77,58 @@ export class ConnectionGroupWebviewController extends ReactWebviewPanelControlle
return state;
});

this.registerReducer("saveConnectionGroup", async (state, payload) => {
try {
if (this.connectionGroupToEdit) {
this.logger.verbose("Updating existing connection group", payload);
await this.connectionConfig.updateGroup({
...this.connectionGroupToEdit,
name: payload.name,
description: payload.description,
color: payload.color,
});
} else {
this.logger.verbose("Creating new connection group", payload);
await this.connectionConfig.addGroup(createConnectionGroupFromSpec(payload));
}
this.registerReducer(
"saveConnectionGroup",
async (state, payload: ConnectionGroupSpec & { scope: "user" | "workspace" }) => {
try {
if (this.connectionGroupToEdit) {
this.logger.verbose("Updating existing connection group", payload);
// Only update name, description, color; parentId and scope are not editable for existing groups
await this.connectionConfig.updateGroup({
...this.connectionGroupToEdit,
name: payload.name,
description: payload.description,
color: payload.color,
});
} else {
this.logger.verbose("Creating new connection group", payload);
// Set parentId based on scope
let parentId: string | undefined;
if (payload.scope === "workspace") {
parentId = this.connectionConfig.getWorkspaceConnectionsGroupId();
} else {
parentId = this.connectionConfig.getUserConnectionsGroupId();
}
const groupSpec = { ...payload, parentId };
await this.connectionConfig.addGroup(
createConnectionGroupFromSpec(groupSpec),
);
}

sendActionEvent(
TelemetryViews.ConnectionGroup,
TelemetryActions.SaveConnectionGroup,
{ newOrEdit: this.connectionGroupToEdit ? "edit" : "new" },
);
sendActionEvent(
TelemetryViews.ConnectionGroup,
TelemetryActions.SaveConnectionGroup,
{ newOrEdit: this.connectionGroupToEdit ? "edit" : "new" },
);

this.dialogResult.resolve(true);
await this.panel.dispose();
} catch (err) {
state.message = getErrorMessage(err);
sendErrorEvent(
TelemetryViews.ConnectionGroup,
TelemetryActions.SaveConnectionGroup,
err,
true, // includeErrorMessage
undefined, // errorCode
undefined, // errorType
{ newOrEdit: this.connectionGroupToEdit ? "edit" : "new" },
);
}
this.dialogResult.resolve(true);
await this.panel.dispose();
} catch (err) {
state.message = getErrorMessage(err);
sendErrorEvent(
TelemetryViews.ConnectionGroup,
TelemetryActions.SaveConnectionGroup,
err,
true, // includeErrorMessage
undefined, // errorCode
undefined, // errorType
{ newOrEdit: this.connectionGroupToEdit ? "edit" : "new" },
);
}

return state;
});
return state;
},
);
}
}

Expand All @@ -124,6 +138,8 @@ export function createConnectionGroupFromSpec(spec: ConnectionGroupState): IConn
description: spec.description,
color: spec.color,
id: Utils.generateGuid(),
parentId:
"parentId" in spec && spec.parentId !== undefined ? String(spec.parentId) : undefined,
};
}

Expand Down
15 changes: 13 additions & 2 deletions src/models/connectionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
IConnectionGroup,
} from "../models/interfaces";
import { ICredentialStore } from "../credentialstore/icredentialstore";
import { ConnectionConfig } from "../connectionconfig/connectionconfig";
import { ConnectionConfig, ConfigurationTarget } from "../connectionconfig/connectionconfig";
import VscodeWrapper from "../controllers/vscodeWrapper";
import { IConnectionInfo } from "vscode-mssql";
import { Logger } from "./logger";
Expand Down Expand Up @@ -369,6 +369,17 @@ export class ConnectionStore {
): Promise<IConnectionProfile> {
await this._connectionConfig.populateMissingConnectionIds(profile);

// Determine the correct target for saving based on groupId
let target = ConfigurationTarget.Global;
// Get all workspace group IDs
const workspaceGroups = this._connectionConfig.getGroupsFromSettings(
ConfigurationTarget.Workspace,
);
const workspaceGroupIds = new Set(workspaceGroups.map((g) => g.id));
if (workspaceGroupIds.has(profile.groupId)) {
target = ConfigurationTarget.Workspace;
}

// Add the profile to the saved list, taking care to clear out the password field if necessary
let savedProfile: IConnectionProfile;
if (profile.authenticationType === Utils.authTypeToString(AuthenticationTypes.AzureMFA)) {
Expand All @@ -383,7 +394,7 @@ export class ConnectionStore {
}
}

await this._connectionConfig.addConnection(savedProfile);
await this._connectionConfig.addConnection(savedProfile, target);

if (await this.saveProfilePasswordIfNeeded(profile)) {
ConnInfo.fixupConnectionCredentials(profile);
Expand Down
2 changes: 2 additions & 0 deletions src/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface IConnectionProfile extends vscodeMssql.IConnectionInfo {
accountStore: AccountStore;
isValidProfile(): boolean;
isAzureActiveDirectory(): boolean;
scope?: "user" | "workspace";
}

export interface IConnectionGroup {
Expand All @@ -76,6 +77,7 @@ export interface IConnectionGroup {
parentId?: string;
color?: string;
description?: string;
scope?: "user" | "workspace";
}

export enum CredentialsQuickPickItemType {
Expand Down
79 changes: 60 additions & 19 deletions src/objectExplorer/objectExplorerDragAndDropController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ export class ObjectExplorerDragAndDropController
): void {
const item = source[0]; // Handle only the first item for simplicity

// Prevent dragging User Connections and Workspace Connections groups
if (
item instanceof ConnectionGroupNode &&
(item.label === "User Connections" || item.label === "Workspace Connections")
) {
// Do not set drag data, effectively disabling drag
return;
}

if (item instanceof ConnectionNode || item instanceof ConnectionGroupNode) {
const dragData: ObjectExplorerDragMetadata = {
name: item.label.toString(),
Expand Down Expand Up @@ -83,23 +92,32 @@ export class ObjectExplorerDragAndDropController
return;
}

// Prevent dropping User Connections or Workspace Connections groups anywhere
const userGroupId = this.connectionStore.connectionConfig.getUserConnectionsGroupId();
const workspaceGroupId =
this.connectionStore.connectionConfig.getWorkspaceConnectionsGroupId();
if (
dragData.type === "connectionGroup" &&
(dragData.id === userGroupId || dragData.id === workspaceGroupId)
) {
// Do nothing, prevent drop
return;
}

// Allow dropping child items into User Connections or Workspace Connections groups

// Prevent drag-and-drop if target is root
if (target === undefined) {
return;
}

try {
if (dragData.isConnectionOrGroup && dragData.type && dragData.id) {
if (target instanceof ConnectionGroupNode || target === undefined) {
let targetInfo: { label: string; id: string };

// If the target is undefined, we're dropping onto the root of the Object Explorer
if (target === undefined) {
targetInfo = {
label: "ROOT",
id: this.connectionStore.rootGroupId,
};
} else {
targetInfo = {
label: target.label.toString(),
id: target.id,
};
}
if (target instanceof ConnectionGroupNode) {
let targetInfo: { label: string; id: string } = {
label: target.label.toString(),
id: target.id,
};

this._logger.verbose(
`Dragged ${dragData.type} '${dragData.name}' (ID: ${dragData.id}) onto group '${targetInfo.label}' (ID: ${targetInfo.id})`,
Expand All @@ -110,24 +128,47 @@ export class ObjectExplorerDragAndDropController
dragData.id,
);
conn.groupId = targetInfo.id;
await this.connectionStore.connectionConfig.updateConnection(conn);
// Set scope based on target group
const targetGroup = this.connectionStore.connectionConfig.getGroupById(
targetInfo.id,
);
conn.scope = targetGroup?.scope || "user";
const configTarget =
conn.scope === "workspace"
? vscode.ConfigurationTarget.Workspace
: vscode.ConfigurationTarget.Global;
await this.connectionStore.connectionConfig.updateConnectionWithTarget(
conn,
configTarget,
);
} else {
const group = this.connectionStore.connectionConfig.getGroupById(
dragData.id,
);

if (group.id === targetInfo.id) {
this._logger.verbose("Cannot move group into itself; skipping.");
return;
}

group.parentId = targetInfo.id;
await this.connectionStore.connectionConfig.updateGroup(group);
// Set scope based on target group
const targetGroup = this.connectionStore.connectionConfig.getGroupById(
targetInfo.id,
);
group.scope = targetGroup?.scope || "user";
const configTarget =
group.scope === "workspace"
? vscode.ConfigurationTarget.Workspace
: vscode.ConfigurationTarget.Global;
await this.connectionStore.connectionConfig.updateGroupWithTarget(
group,
configTarget,
);
}

sendActionEvent(TelemetryViews.ObjectExplorer, TelemetryActions.DragAndDrop, {
dragType: dragData.type,
dropTarget: target ? "connectionGroup" : "ROOT",
dropTarget: "connectionGroup",
});
}
}
Expand Down
Loading