SQL Database Projects backend swap to DacFx/Tools Service (#22451)

* Move call to STS move api into project.ts (#22071)

* move call to STS move api into project.ts

* remove undefined

* Remove convert to sdk style code (#22081)

* remove convert to sdk style code

* remove from package.json

* Merging latest from main (#22097)

* [SQL-Migration] Login migrations telemetry (#22038)

This PR enhances telemetry for login migrations (and in the following ways:

Add details for starting migration (number of logins migrating, type of logins)
Log Migration result (number of errors per step, duration of each step, type of logins, if system error occurred)
Add sql-migration extension to our telemetry
Adds details when trying to connect to target
Tracks clicking "done" from the wizard
Fixes bucketizing for navigating telemetry in the login migration wizard
Sample usage of kusto query for new telemetry:
RawEventsADS
| where EventName contains 'sql-migration'
| extend view = tostring(Properties['view'])
| extend action = tostring(Properties['action'])
| extend buttonPressed = tostring(Properties['buttonpressed'])
| extend pageTitle = tostring(Properties['pagetitle'])
| extend adsVersion = tostring(Properties['common.adsversion'])
| extend targetType = tostring(Properties['targettype'])
| extend tenantId = tostring(Properties['tenantid'])
| extend subscriptionId = tostring(Properties['subscriptionid'])
| where view contains "login"
//| where adsVersion contains "1.42.0-insider"
| where ClientTimestamp >= ago(18h)
| project EventName, ClientTimestamp, SessionId, view, pageTitle, action, buttonPressed, targetType
, tenantId, subscriptionId
, adsVersion, OSVersion, Properties

* Add Secure Enclaves dropdown with customizable Advanced options (#22019)

* Update extension READMEs (#22079)

* Fix query-history README images (#22084)

* [Loc] update to mssql and sql-migration xlf files (#22087)

* [Loc] small fix to Portuguese lcl file (#22088)

* [Loc] small fix to Portuguese lcl file

* remove newline

* Adding None bindings to the sqlProjects service (#22085)

* Adding None bindings

* updating names of None bindings

---------

Co-authored-by: AkshayMata <akam520@gmail.com>
Co-authored-by: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com>
Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
Co-authored-by: Alex Ma <alma1@microsoft.com>

* Swap add and update sqlcmd variables in sql projects to use STS apis (#22086)

* delete sqlcmd variable working

* undo add

* remove variable from add and update sqlcmd variable apis

* hookup add and edit sqlcmd variable

* update vscode-mssql.d.ts

* move add and edit to project.ts

* update STS and tests

* move delete sqlcmd variable to project.ts (#22104)

* add test for add and edit sqlcmd variable (#22107)

* Swapping property access to STS (#22115)

* checkpoint

* Adding sqlproj property bindings

* Swapping out property loading and setting

* consolidating to this.sqlProjService

* Update dacpac reference to use STS api (#22116)

* Update add dacpac reference to use STS api

* remove changes for project ref

* validate unset settings from add database reference dialog

* update one more place getting sqlprojservice

* addressing comments

* fix path for dacpac reference (#22143)

* Swap add project reference to call STS (#22148)

* initial changes for swapping add project reference

* fix include path

* move common logic to helper function

* read sqlcmd variables from STS (#22147)

* Swapping .sqlproj- and crossplat compatibility-related functions to use STS (#22146)

* Supporting roundtrip

* Updating sqlproj style checks and cross-platform compatibility to use STS

* removing unnecessary awaits

* Fixing assertions

* Adding roundtrip update confirmations

* test cleanup

* cleaning up comment; localizing error

* Swap add system db reference (#22171)

* swap adding system database references

* fix tests

* remove only in test

* Read database references from STS (#22179)

* Read database references from STS

* fix system dacpac names

* fix project reference name

* Swap changeTargetPlatform to call STS (#22172)

* swap changeTargetPlatform to call STS

* Address comments

* De-duplicating enum for SystemDatabase (#22196)

* Deudping SystemDatabase enum

* simplifying enum refs

* Removing the now-unused imports code from SqlProjects (#22199)

* Removing unused importTargets entries

* whitespace; also to retrigger github checks on correct branch

* Hooking in Move() for Nones and PublishProfiles (#22200)

* Swap delete database reference to call STS (#22215)

* initial changes

* update contracts

* remove unnecessary info from SystemDatabaseReferenceProjectEntry

* uppercase master and msdb

* cleanup

* update test

* update comment

* undo change in projectController.ts

* remove unused system dacpac helper functions (#22204)

* more cleanup of project.ts (#22242)

* fix a couple database reference tests (#22248)

* Organizing sqlcmd variable and db reference code (#22284)

* organize database references and sqlcmd variable functions

* separate database reference tests

* Script and folder get + add support (#22166)

* Initial sqlobjectscripts

* adding mock filter

* test fixing

* another test passing

* swapping pre/post/none checkpoint

* awaiters

* convert addExistingItem

* swapping folders

* removing print

* stripping out project folder and file readers

* adding some regions

* Updating sqlproj style checks and cross-platform compatibility to use STS

* Updating sqlproj style checks and cross-platform compatibility to use STS

* added type property to tree items

* projectController swapovers

* removing imported targets

* Deleting the last of the TS XML parsing!

* Removing old functions

* renamed readNoneScripts to readNoneItems

* fixing path passed to STS calls

* remove system dacpac tests that were moved to DacFx (#22299)

* fix error when opening file after adding a file to sql project (#22309)

* fix error when opening file after adding a file to sql project

* remove unused import

* fix exclude for table and externalStreamingJob (#22310)

* add reload project (#22313)

* set DSP from STS (#22320)

* fix adding post-deployment script and existing item (#22317)

* Test cleanup for .sqlproj content operations (#22330)

* Fixing up tests

* sqlproj content operations tests

* remove only

* Cleanup

* Correcting collation

* cleanup constants.ts (#22336)

* fix folders not showing in project tree (#22319)

* Fix project controller tests (#22327)

* fixing ProjectController tests after swap

* remove only from database reference tests

* change system dbs back to lowercase in sql projects (#22353)

* Bump tools service

* Updated yarn.lock file

* pass SystemDacpacsLocation when building legacy style sql projects (#22329)

* Benjin/fix types (#22365)

* Updated yarn.lock file

* Fixing types

* fix projectController tests (#22375)

* Fixing the deletion flow for files and folders (#22366)

* checkpoint

* PR feedback

* Fixing up SDK-style operations project test group (#22377)

* Fixing up SDK-style project test group

* Removing .only

* Fixing up database reference tests (#22380)

* Fixing DB reference test group

* Extra cleanup

* removing only

* Consolidating None and PublishProfile; lighting up test (#22382)

* Lighting up project property tests (#22395)

* Checkpoint

* Lighting up project property tests

* removing timeout

* Fixing buildHelper test (#22400)

* Unskipping up roundtrip test (#22397)

* Refactoring database references to split literalVariable from databaseName (#22412)

* refactoring database references to split databaseVariableLiteralValue out from databaseName

* renaming more properties

* Removing branch in entry population

* removing only

* Fixing baselines for delete test

* PR feedback

* Fixing up ProjectTree tests (#22419)

* Fixing up projectTree tests

* remove only

* Updating projectController exclude test (#22413)

* Updating test

* moving filtering for external folders to readFolders() method

* Removing EntryType import

* fix ups (#22435)

* adding extra info for test failure

* hide exclude folder from context menu until it's supported (#22454)

* Adding current test name to generated folder to avoid conflicts (#22478)

* Adding current test name to generated folder to avoid conflicts

* passing correct test parameter in

* Adding trimming and entropy

* Deleting unused baselines (#22497)

* Replacing addToProject() with addSqlObjectScripts() (#22489)

* checkpoint

* Fixing test

* Updating file scraper function to filter only to .sql files (no folders, no txt)

* changing var names to reflect that the lists only contain .sql scripts

---------

Co-authored-by: Kim Santiago <31145923+kisantia@users.noreply.github.com>
Co-authored-by: AkshayMata <akam520@gmail.com>
Co-authored-by: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com>
Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
Co-authored-by: Alex Ma <alma1@microsoft.com>
This commit is contained in:
Benjin Dubishar
2023-03-28 13:39:57 -07:00
committed by GitHub
parent a7ecff77dd
commit 15c0c68e44
58 changed files with 2157 additions and 5084 deletions

View File

@@ -6,20 +6,22 @@
import * as should from 'should';
import * as path from 'path';
import * as os from 'os';
import * as constants from '../common/constants';
import * as utils from '../common/utils';
import { createDummyFileStructure, deleteGeneratedTestFolder } from './testUtils';
import { exists, trimUri, removeSqlCmdVariableFormatting, formatSqlCmdVariable, isValidSqlCmdVariableName, timeConversion, validateSqlServerPortNumber, isEmptyString, detectCommandInstallation, isValidSQLPassword, findSqlVersionInImageName, findSqlVersionInTargetPlatform } from '../common/utils';
import { Uri } from 'vscode';
describe('Tests to verify utils functions', function (): void {
it('Should determine existence of files/folders', async () => {
let testFolderPath = await createDummyFileStructure();
let testFolderPath = await createDummyFileStructure(undefined);
should(await exists(testFolderPath)).equal(true);
should(await exists(path.join(testFolderPath, 'file1.sql'))).equal(true);
should(await exists(path.join(testFolderPath, 'folder2'))).equal(true);
should(await exists(path.join(testFolderPath, 'folder4'))).equal(false);
should(await exists(path.join(testFolderPath, 'folder2', 'file4.sql'))).equal(true);
should(await exists(path.join(testFolderPath, 'folder4', 'file2.sql'))).equal(false);
should(await utils.exists(testFolderPath)).equal(true);
should(await utils.exists(path.join(testFolderPath, 'file1.sql'))).equal(true);
should(await utils.exists(path.join(testFolderPath, 'folder2'))).equal(true);
should(await utils.exists(path.join(testFolderPath, 'folder4'))).equal(false);
should(await utils.exists(path.join(testFolderPath, 'folder2', 'file4.sql'))).equal(true);
should(await utils.exists(path.join(testFolderPath, 'folder4', 'file2.sql'))).equal(false);
await deleteGeneratedTestFolder();
});
@@ -28,117 +30,124 @@ describe('Tests to verify utils functions', function (): void {
const root = os.platform() === 'win32' ? 'Z:\\' : '/';
let projectUri = Uri.file(path.join(root, 'project', 'folder', 'project.sqlproj'));
let fileUri = Uri.file(path.join(root, 'project', 'folder', 'file.sql'));
should(trimUri(projectUri, fileUri)).equal('file.sql');
should(utils.trimUri(projectUri, fileUri)).equal('file.sql');
fileUri = Uri.file(path.join(root, 'project', 'file.sql'));
let urifile = trimUri(projectUri, fileUri);
let urifile = utils.trimUri(projectUri, fileUri);
should(urifile).equal('../file.sql');
fileUri = Uri.file(path.join(root, 'project', 'forked', 'file.sql'));
should(trimUri(projectUri, fileUri)).equal('../forked/file.sql');
should(utils.trimUri(projectUri, fileUri)).equal('../forked/file.sql');
fileUri = Uri.file(path.join(root, 'forked', 'from', 'top', 'file.sql'));
should(trimUri(projectUri, fileUri)).equal('../../forked/from/top/file.sql');
should(utils.trimUri(projectUri, fileUri)).equal('../../forked/from/top/file.sql');
});
it('Should remove $() from sqlcmd variables', () => {
should(removeSqlCmdVariableFormatting('$(test)')).equal('test', '$() surrounding the variable should have been removed');
should(removeSqlCmdVariableFormatting('$(test')).equal('test', '$( at the beginning of the variable should have been removed');
should(removeSqlCmdVariableFormatting('test')).equal('test', 'string should not have been changed because it is not in sqlcmd variable format');
should(utils.removeSqlCmdVariableFormatting('$(test)')).equal('test', '$() surrounding the variable should have been removed');
should(utils.removeSqlCmdVariableFormatting('$(test')).equal('test', '$( at the beginning of the variable should have been removed');
should(utils.removeSqlCmdVariableFormatting('test')).equal('test', 'string should not have been changed because it is not in sqlcmd variable format');
});
it('Should make variable be in sqlcmd variable format with $()', () => {
should(formatSqlCmdVariable('$(test)')).equal('$(test)', 'string should not have been changed because it was already in the correct format');
should(formatSqlCmdVariable('test')).equal('$(test)', 'string should have been changed to be in sqlcmd variable format');
should(formatSqlCmdVariable('$(test')).equal('$(test)', 'string should have been changed to be in sqlcmd variable format');
should(formatSqlCmdVariable('')).equal('', 'should not do anything to an empty string');
should(utils.formatSqlCmdVariable('$(test)')).equal('$(test)', 'string should not have been changed because it was already in the correct format');
should(utils.formatSqlCmdVariable('test')).equal('$(test)', 'string should have been changed to be in sqlcmd variable format');
should(utils.formatSqlCmdVariable('$(test')).equal('$(test)', 'string should have been changed to be in sqlcmd variable format');
should(utils.formatSqlCmdVariable('')).equal('', 'should not do anything to an empty string');
});
it('Should determine invalid sqlcmd variable names', () => {
// valid names
should(isValidSqlCmdVariableName('$(test)')).equal(true);
should(isValidSqlCmdVariableName('$(test )')).equal(true, 'trailing spaces should be valid because they will be trimmed');
should(isValidSqlCmdVariableName('test')).equal(true);
should(isValidSqlCmdVariableName('test ')).equal(true, 'trailing spaces should be valid because they will be trimmed');
should(isValidSqlCmdVariableName('$(test')).equal(true);
should(isValidSqlCmdVariableName('$(test ')).equal(true, 'trailing spaces should be valid because they will be trimmed');
should(utils.isValidSqlCmdVariableName('$(test)')).equal(true);
should(utils.isValidSqlCmdVariableName('$(test )')).equal(true, 'trailing spaces should be valid because they will be trimmed');
should(utils.isValidSqlCmdVariableName('test')).equal(true);
should(utils.isValidSqlCmdVariableName('test ')).equal(true, 'trailing spaces should be valid because they will be trimmed');
should(utils.isValidSqlCmdVariableName('$(test')).equal(true);
should(utils.isValidSqlCmdVariableName('$(test ')).equal(true, 'trailing spaces should be valid because they will be trimmed');
// whitespace
should(isValidSqlCmdVariableName('')).equal(false);
should(isValidSqlCmdVariableName(' ')).equal(false);
should(isValidSqlCmdVariableName(' ')).equal(false);
should(isValidSqlCmdVariableName('test abc')).equal(false);
should(isValidSqlCmdVariableName(' ')).equal(false);
should(utils.isValidSqlCmdVariableName('')).equal(false);
should(utils.isValidSqlCmdVariableName(' ')).equal(false);
should(utils.isValidSqlCmdVariableName(' ')).equal(false);
should(utils.isValidSqlCmdVariableName('test abc')).equal(false);
should(utils.isValidSqlCmdVariableName(' ')).equal(false);
// invalid characters
should(isValidSqlCmdVariableName('$($test')).equal(false);
should(isValidSqlCmdVariableName('$test')).equal(false);
should(isValidSqlCmdVariableName('$test')).equal(false);
should(isValidSqlCmdVariableName('test@')).equal(false);
should(isValidSqlCmdVariableName('test#')).equal(false);
should(isValidSqlCmdVariableName('test"')).equal(false);
should(isValidSqlCmdVariableName('test\'')).equal(false);
should(isValidSqlCmdVariableName('test-1')).equal(false);
should(utils.isValidSqlCmdVariableName('$($test')).equal(false);
should(utils.isValidSqlCmdVariableName('$test')).equal(false);
should(utils.isValidSqlCmdVariableName('$test')).equal(false);
should(utils.isValidSqlCmdVariableName('test@')).equal(false);
should(utils.isValidSqlCmdVariableName('test#')).equal(false);
should(utils.isValidSqlCmdVariableName('test"')).equal(false);
should(utils.isValidSqlCmdVariableName('test\'')).equal(false);
should(utils.isValidSqlCmdVariableName('test-1')).equal(false);
});
it('Should convert from milliseconds to hr min sec correctly', () => {
should(timeConversion((60 * 60 * 1000) + (59 * 60 * 1000) + (59 * 1000))).equal('1 hr, 59 min, 59 sec');
should(timeConversion((60 * 60 * 1000) + (59 * 60 * 1000))).equal('1 hr, 59 min');
should(timeConversion((60 * 60 * 1000))).equal('1 hr');
should(timeConversion((60 * 60 * 1000) + (59 * 1000))).equal('1 hr, 59 sec');
should(timeConversion((59 * 60 * 1000) + (59 * 1000))).equal('59 min, 59 sec');
should(timeConversion((59 * 1000))).equal('59 sec');
should(timeConversion((59))).equal('59 msec');
should(utils.timeConversion((60 * 60 * 1000) + (59 * 60 * 1000) + (59 * 1000))).equal('1 hr, 59 min, 59 sec');
should(utils.timeConversion((60 * 60 * 1000) + (59 * 60 * 1000))).equal('1 hr, 59 min');
should(utils.timeConversion((60 * 60 * 1000))).equal('1 hr');
should(utils.timeConversion((60 * 60 * 1000) + (59 * 1000))).equal('1 hr, 59 sec');
should(utils.timeConversion((59 * 60 * 1000) + (59 * 1000))).equal('59 min, 59 sec');
should(utils.timeConversion((59 * 1000))).equal('59 sec');
should(utils.timeConversion((59))).equal('59 msec');
});
it('Should validate port number correctly', () => {
should(validateSqlServerPortNumber('invalid')).equals(false);
should(validateSqlServerPortNumber('')).equals(false);
should(validateSqlServerPortNumber(undefined)).equals(false);
should(validateSqlServerPortNumber('65536')).equals(false);
should(validateSqlServerPortNumber('-1')).equals(false);
should(validateSqlServerPortNumber('65530')).equals(true);
should(validateSqlServerPortNumber('1533')).equals(true);
should(utils.validateSqlServerPortNumber('invalid')).equals(false);
should(utils.validateSqlServerPortNumber('')).equals(false);
should(utils.validateSqlServerPortNumber(undefined)).equals(false);
should(utils.validateSqlServerPortNumber('65536')).equals(false);
should(utils.validateSqlServerPortNumber('-1')).equals(false);
should(utils.validateSqlServerPortNumber('65530')).equals(true);
should(utils.validateSqlServerPortNumber('1533')).equals(true);
});
it('Should validate empty string correctly', () => {
should(isEmptyString('invalid')).equals(false);
should(isEmptyString('')).equals(true);
should(isEmptyString(undefined)).equals(true);
should(isEmptyString('65536')).equals(false);
should(utils.isEmptyString('invalid')).equals(false);
should(utils.isEmptyString('')).equals(true);
should(utils.isEmptyString(undefined)).equals(true);
should(utils.isEmptyString('65536')).equals(false);
});
it('Should correctly detect present commands', async () => {
should(await detectCommandInstallation('node')).equal(true, '"node" should have been detected.');
should(await detectCommandInstallation('bogusFakeCommand')).equal(false, '"bogusFakeCommand" should have been detected.');
should(await utils.detectCommandInstallation('node')).equal(true, '"node" should have been detected.');
should(await utils.detectCommandInstallation('bogusFakeCommand')).equal(false, '"bogusFakeCommand" should have been detected.');
});
it('Should validate SQL server password correctly', () => {
should(isValidSQLPassword('invalid')).equals(false, 'string with chars only is invalid password');
should(isValidSQLPassword('')).equals(false, 'empty string is invalid password');
should(isValidSQLPassword('65536')).equals(false, 'string with numbers only is invalid password');
should(isValidSQLPassword('dFGj')).equals(false, 'string with lowercase and uppercase char only is invalid password');
should(isValidSQLPassword('dj$')).equals(false, 'string with char and symbols only is invalid password');
should(isValidSQLPassword('dF65530')).equals(false, 'string with char and numbers only is invalid password');
should(isValidSQLPassword('dF6$30')).equals(false, 'dF6$30 is invalid password');
should(isValidSQLPassword('dF65$530')).equals(true, 'dF65$530 is valid password');
should(isValidSQLPassword('dFdf65$530')).equals(true, 'dF65$530 is valid password');
should(isValidSQLPassword('av1fgh533@')).equals(true, 'dF65$530 is valid password');
should(utils.isValidSQLPassword('invalid')).equals(false, 'string with chars only is invalid password');
should(utils.isValidSQLPassword('')).equals(false, 'empty string is invalid password');
should(utils.isValidSQLPassword('65536')).equals(false, 'string with numbers only is invalid password');
should(utils.isValidSQLPassword('dFGj')).equals(false, 'string with lowercase and uppercase char only is invalid password');
should(utils.isValidSQLPassword('dj$')).equals(false, 'string with char and symbols only is invalid password');
should(utils.isValidSQLPassword('dF65530')).equals(false, 'string with char and numbers only is invalid password');
should(utils.isValidSQLPassword('dF6$30')).equals(false, 'dF6$30 is invalid password');
should(utils.isValidSQLPassword('dF65$530')).equals(true, 'dF65$530 is valid password');
should(utils.isValidSQLPassword('dFdf65$530')).equals(true, 'dF65$530 is valid password');
should(utils.isValidSQLPassword('av1fgh533@')).equals(true, 'dF65$530 is valid password');
});
it('findSqlVersionInImageName should return the version correctly', () => {
should(findSqlVersionInImageName('2017-CU1-ubuntu')).equals(2017, 'invalid number returned for 2017-CU1-ubuntu');
should(findSqlVersionInImageName('2019-latest')).equals(2019, 'invalid number returned for 2019-latest');
should(findSqlVersionInImageName('latest')).equals(undefined, 'invalid number returned for latest');
should(findSqlVersionInImageName('latest-ubuntu')).equals(undefined, 'invalid number returned for latest-ubuntu');
should(findSqlVersionInImageName('2017-CU20-ubuntu-16.04')).equals(2017, 'invalid number returned for 2017-CU20-ubuntu-16.04');
should(utils.findSqlVersionInImageName('2017-CU1-ubuntu')).equals(2017, 'invalid number returned for 2017-CU1-ubuntu');
should(utils.findSqlVersionInImageName('2019-latest')).equals(2019, 'invalid number returned for 2019-latest');
should(utils.findSqlVersionInImageName('latest')).equals(undefined, 'invalid number returned for latest');
should(utils.findSqlVersionInImageName('latest-ubuntu')).equals(undefined, 'invalid number returned for latest-ubuntu');
should(utils.findSqlVersionInImageName('2017-CU20-ubuntu-16.04')).equals(2017, 'invalid number returned for 2017-CU20-ubuntu-16.04');
});
it('findSqlVersionInTargetPlatform should return the version correctly', () => {
should(findSqlVersionInTargetPlatform('SQL Server 2012')).equals(2012, 'invalid number returned for SQL Server 2012');
should(findSqlVersionInTargetPlatform('SQL Server 2019')).equals(2019, 'invalid number returned for SQL Server 2019');
should(findSqlVersionInTargetPlatform('Azure SQL Database')).equals(undefined, 'invalid number returned for Azure SQL Database');
should(findSqlVersionInTargetPlatform('Azure Synapse SQL Pool')).equals(undefined, 'invalid number returned for Azure Synapse SQL Pool');
should(utils.findSqlVersionInTargetPlatform('SQL Server 2012')).equals(2012, 'invalid number returned for SQL Server 2012');
should(utils.findSqlVersionInTargetPlatform('SQL Server 2019')).equals(2019, 'invalid number returned for SQL Server 2019');
should(utils.findSqlVersionInTargetPlatform('Azure SQL Database')).equals(undefined, 'invalid number returned for Azure SQL Database');
should(utils.findSqlVersionInTargetPlatform('Azure Synapse SQL Pool')).equals(undefined, 'invalid number returned for Azure Synapse SQL Pool');
});
it('Should only return well known database strings when getWellKnownDatabaseSources function is called', async function (): Promise<void> {
const sources = ['test1', 'test2', 'test3', constants.WellKnownDatabaseSources[0]];
(utils.getWellKnownDatabaseSources(sources).length).should.equal(1);
(utils.getWellKnownDatabaseSources(sources)[0]).should.equal(constants.WellKnownDatabaseSources[0]);
});
});