diff --git a/extensions/admin-tool-ext-win/coverConfig.json b/extensions/admin-tool-ext-win/coverConfig.json index 7c935463c5..cc99c6298c 100644 --- a/extensions/admin-tool-ext-win/coverConfig.json +++ b/extensions/admin-tool-ext-win/coverConfig.json @@ -4,7 +4,8 @@ "relativeCoverageDir": "../../coverage", "ignorePatterns": [ "**/node_modules/**", - "**/test/**" + "**/test/**", + "main.js" ], "reports": [ "cobertura", diff --git a/extensions/admin-tool-ext-win/src/main.ts b/extensions/admin-tool-ext-win/src/main.ts index 63d37e2a46..e9b436177d 100644 --- a/extensions/admin-tool-ext-win/src/main.ts +++ b/extensions/admin-tool-ext-win/src/main.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import * as azdata from 'azdata'; import * as vscode from 'vscode'; import { TelemetryReporter, TelemetryViews } from './telemetry'; -import { doubleEscapeSingleQuotes, backEscapeDoubleQuotes, getTelemetryErrorType } from './utils'; +import { getTelemetryErrorType, buildSsmsMinCommandArgs, buildUrn, LaunchSsmsDialogParams, nodeTypeToUrnNameMapping } from './utils'; import { ChildProcess, exec } from 'child_process'; import { promises as fs } from 'fs'; @@ -17,53 +17,6 @@ const localize = nls.loadMessageBundle(); let exePath: string; const runningProcesses: Map = new Map(); -interface SmoMapping { - action: string; - urnName: string; -} - -const nodeTypeToUrnNameMapping: { [oeNodeType: string]: SmoMapping } = { - 'Database': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Database', urnName: 'Database' }, - 'Server': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Server', urnName: 'Server' }, - 'ServerLevelServerAudit': { action: 'sqla:AuditProperties', urnName: 'Audit' }, - 'ServerLevelCredential': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Credential', urnName: 'Credential' }, - 'ServerLevelServerRole': { action: 'sqla:ManageServerRole', urnName: 'Role' }, - 'ServerLevelServerAuditSpecification': { action: 'sqla:ServerAuditSpecificationProperties', urnName: 'ServerAuditSpecification' }, - 'ServerLevelLinkedServer': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.LinkedServer', urnName: 'LinkedServer' }, - 'Table': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Table', urnName: 'Table' }, - 'View': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.View', urnName: 'View' }, - 'Column': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Column', urnName: 'Column' }, - 'Index': { action: 'sqla:IndexProperties', urnName: 'Index' }, - 'Statistic': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Statistic', urnName: 'Statistic' }, - 'StoredProcedure': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.StoredProcedure', urnName: 'StoredProcedure' }, - 'ScalarValuedFunction': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedFunction', urnName: 'UserDefinedFunction' }, - 'TableValuedFunction': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedFunction', urnName: 'UserDefinedFunction' }, - 'AggregateFunction': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedFunction', urnName: 'UserDefinedFunction' }, - 'Synonym': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Synonym', urnName: 'Synonym' }, - 'Assembly': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.SqlAssembly', urnName: 'SqlAssembly' }, - 'UserDefinedDataType': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedDataType', urnName: 'UserDefinedDataType' }, - 'UserDefinedType': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedType', urnName: 'UserDefinedType' }, - 'UserDefinedTableType': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedTableType', urnName: 'UserDefinedTableType' }, - 'Sequence': { action: 'sqla:SequenceProperties', urnName: 'Sequence' }, - 'User': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.User', urnName: 'User' }, - 'DatabaseRole': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.DatabaseRole', urnName: 'Role' }, - 'ApplicationRole': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.ApplicationRole', urnName: 'ApplicationRole' }, - 'Schema': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Schema', urnName: 'Schema' }, - 'SecurityPolicy': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.SecurityPolicy', urnName: 'SecurityPolicy' }, - 'ServerLevelLogin': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Login', urnName: 'Login' }, -}; - -// Params to pass to SsmsMin.exe, only an action and server are required - the rest are optional based on the -// action used. Exported for use in testing. -export interface LaunchSsmsDialogParams { - action: string; - server: string; - database?: string; - user?: string; - useAad?: boolean; - urn?: string; -} - export async function activate(context: vscode.ExtensionContext): Promise { // This is for Windows-specific support so do nothing on other platforms if (process.platform === 'win32') { @@ -225,41 +178,3 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object runningProcesses.set(proc.pid, proc); } -/** - * Builds the command arguments to pass to SsmsMin.exe. Values are expected to be escaped correctly - * already per their - they will be further escaped * for command-line usage but no additional - * escaping will occur. - * @param params The params used to build up the command parameter string - */ -export function buildSsmsMinCommandArgs(params: LaunchSsmsDialogParams): string { - return `-a "${backEscapeDoubleQuotes(params.action)}" \ --S "${backEscapeDoubleQuotes(params.server)}"\ -${params.database ? ' -D "' + backEscapeDoubleQuotes(params.database) + '"' : ''}\ -${params.user ? ' -U "' + backEscapeDoubleQuotes(params.user) + '"' : ''}\ -${params.useAad === true ? ' -G' : ''}\ -${params.urn ? ' -u "' + backEscapeDoubleQuotes(params.urn) + '"' : ''}`; -} - -/** - * Builds the URN string for a given ObjectExplorerNode in the form understood by SsmsMin - * @param node The node to get the URN of - */ -export async function buildUrn(node: azdata.objectexplorer.ObjectExplorerNode): Promise { - let urnNodes: string[] = []; - while (node) { - // Server is special since it's a connection node - always add it as the root - if (node.nodeType === 'Server') { - break; - } - else if (node.metadata && node.nodeType !== 'Folder') { - // SFC URN expects Name and Schema to be separate properties - const urnSegment = node.metadata.schema && node.metadata.schema !== '' ? - `${nodeTypeToUrnNameMapping[node.nodeType].urnName}[@Name='${doubleEscapeSingleQuotes(node.metadata.name)}' and @Schema='${doubleEscapeSingleQuotes(node.metadata.schema)}']` : - `${nodeTypeToUrnNameMapping[node.nodeType].urnName}[@Name='${doubleEscapeSingleQuotes(node.metadata.name)}']`; - urnNodes = [urnSegment].concat(urnNodes); - } - node = await node.getParent(); - } - - return ['Server'].concat(urnNodes).join('/'); -} diff --git a/extensions/admin-tool-ext-win/src/test/extension.test.ts b/extensions/admin-tool-ext-win/src/test/extension.test.ts new file mode 100644 index 0000000000..8a11d5b93a --- /dev/null +++ b/extensions/admin-tool-ext-win/src/test/extension.test.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as vscode from 'vscode'; + +describe('Extension activate test', () => { + it('Extension should activate correctly', async function (): Promise { + await vscode.extensions.getExtension('Microsoft.admin-tool-ext-win').activate(); + }); +}); diff --git a/extensions/admin-tool-ext-win/src/test/utils.test.ts b/extensions/admin-tool-ext-win/src/test/utils.test.ts index 015e2098b4..a4366bb784 100644 --- a/extensions/admin-tool-ext-win/src/test/utils.test.ts +++ b/extensions/admin-tool-ext-win/src/test/utils.test.ts @@ -5,9 +5,7 @@ import * as should from 'should'; import 'mocha'; - -import { buildSsmsMinCommandArgs, buildUrn, LaunchSsmsDialogParams } from '../main'; -import { doubleEscapeSingleQuotes, backEscapeDoubleQuotes, getTelemetryErrorType } from '../utils'; +import { doubleEscapeSingleQuotes, backEscapeDoubleQuotes, getTelemetryErrorType,buildSsmsMinCommandArgs, buildUrn, LaunchSsmsDialogParams } from '../utils'; import { ExtHostObjectExplorerNodeStub } from './stubs'; describe('buildSsmsMinCommandArgs Method Tests', () => { diff --git a/extensions/admin-tool-ext-win/src/utils.ts b/extensions/admin-tool-ext-win/src/utils.ts index 954189de39..9f3d0b99b0 100644 --- a/extensions/admin-tool-ext-win/src/utils.ts +++ b/extensions/admin-tool-ext-win/src/utils.ts @@ -3,6 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as azdata from 'azdata'; + export interface IPackageInfo { name: string; version: string; @@ -56,3 +58,90 @@ export function getTelemetryErrorType(msg: string): string { return 'Other'; } } + +// Params to pass to SsmsMin.exe, only an action and server are required - the rest are optional based on the +// action used. Exported for use in testing. +export interface LaunchSsmsDialogParams { + action: string; + server: string; + database?: string; + user?: string; + useAad?: boolean; + urn?: string; +} + +/** + * Builds the command arguments to pass to SsmsMin.exe. Values are expected to be escaped correctly + * already per their - they will be further escaped * for command-line usage but no additional + * escaping will occur. + * @param params The params used to build up the command parameter string + */ +export function buildSsmsMinCommandArgs(params: LaunchSsmsDialogParams): string { + return `-a "${backEscapeDoubleQuotes(params.action)}" \ +-S "${backEscapeDoubleQuotes(params.server)}"\ +${params.database ? ' -D "' + backEscapeDoubleQuotes(params.database) + '"' : ''}\ +${params.user ? ' -U "' + backEscapeDoubleQuotes(params.user) + '"' : ''}\ +${params.useAad === true ? ' -G' : ''}\ +${params.urn ? ' -u "' + backEscapeDoubleQuotes(params.urn) + '"' : ''}`; +} + + +interface SmoMapping { + action: string; + urnName: string; +} + +export const nodeTypeToUrnNameMapping: { [oeNodeType: string]: SmoMapping } = { + 'Database': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Database', urnName: 'Database' }, + 'Server': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Server', urnName: 'Server' }, + 'ServerLevelServerAudit': { action: 'sqla:AuditProperties', urnName: 'Audit' }, + 'ServerLevelCredential': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Credential', urnName: 'Credential' }, + 'ServerLevelServerRole': { action: 'sqla:ManageServerRole', urnName: 'Role' }, + 'ServerLevelServerAuditSpecification': { action: 'sqla:ServerAuditSpecificationProperties', urnName: 'ServerAuditSpecification' }, + 'ServerLevelLinkedServer': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.LinkedServer', urnName: 'LinkedServer' }, + 'Table': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Table', urnName: 'Table' }, + 'View': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.View', urnName: 'View' }, + 'Column': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Column', urnName: 'Column' }, + 'Index': { action: 'sqla:IndexProperties', urnName: 'Index' }, + 'Statistic': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Statistic', urnName: 'Statistic' }, + 'StoredProcedure': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.StoredProcedure', urnName: 'StoredProcedure' }, + 'ScalarValuedFunction': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedFunction', urnName: 'UserDefinedFunction' }, + 'TableValuedFunction': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedFunction', urnName: 'UserDefinedFunction' }, + 'AggregateFunction': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedFunction', urnName: 'UserDefinedFunction' }, + 'Synonym': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Synonym', urnName: 'Synonym' }, + 'Assembly': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.SqlAssembly', urnName: 'SqlAssembly' }, + 'UserDefinedDataType': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedDataType', urnName: 'UserDefinedDataType' }, + 'UserDefinedType': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedType', urnName: 'UserDefinedType' }, + 'UserDefinedTableType': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.UserDefinedTableType', urnName: 'UserDefinedTableType' }, + 'Sequence': { action: 'sqla:SequenceProperties', urnName: 'Sequence' }, + 'User': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.User', urnName: 'User' }, + 'DatabaseRole': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.DatabaseRole', urnName: 'Role' }, + 'ApplicationRole': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.ApplicationRole', urnName: 'ApplicationRole' }, + 'Schema': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Schema', urnName: 'Schema' }, + 'SecurityPolicy': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.SecurityPolicy', urnName: 'SecurityPolicy' }, + 'ServerLevelLogin': { action: 'sqla:Properties@Microsoft.SqlServer.Management.Smo.Login', urnName: 'Login' }, +}; + +/** + * Builds the URN string for a given ObjectExplorerNode in the form understood by SsmsMin + * @param node The node to get the URN of + */ +export async function buildUrn(node: azdata.objectexplorer.ObjectExplorerNode): Promise { + let urnNodes: string[] = []; + while (node) { + // Server is special since it's a connection node - always add it as the root + if (node.nodeType === 'Server') { + break; + } + else if (node.metadata && node.nodeType !== 'Folder') { + // SFC URN expects Name and Schema to be separate properties + const urnSegment = node.metadata.schema && node.metadata.schema !== '' ? + `${nodeTypeToUrnNameMapping[node.nodeType].urnName}[@Name='${doubleEscapeSingleQuotes(node.metadata.name)}' and @Schema='${doubleEscapeSingleQuotes(node.metadata.schema)}']` : + `${nodeTypeToUrnNameMapping[node.nodeType].urnName}[@Name='${doubleEscapeSingleQuotes(node.metadata.name)}']`; + urnNodes = [urnSegment].concat(urnNodes); + } + node = await node.getParent(); + } + + return ['Server'].concat(urnNodes).join('/'); +}