Compare commits

...

20 Commits

Author SHA1 Message Date
Hai Cao
31bee67f00 bump STS (#23148) 2023-05-16 14:40:54 -07:00
Alan Ren
e2949d4494 add securable settings (#22936) (#23141)
* wip

* Update typings

* nullable

* update test service

* support securables

* updata test data

* fix issues

* fix build failure

* update test mocks

* fix typo

* fix reference

* fix findobjectdialog issue

* update SearchResultItem type

* fix table component perf issue

* hide effective permission for server role

* hide effective permission for app role and db role

* vbump sts and fix a couple issues

* STS update and UI update

* fix user login display issue

* vbump sts
2023-05-15 15:35:47 -07:00
Cheena Malhotra
b6bd726066 Support advanced options in command line arguments (#23104) (#23124) 2023-05-12 13:51:15 -07:00
Benjin Dubishar
0fe638974d Fixing issue where sqlcmdvars wouldn't load from publish profile in ADS (#23116) (#23121)
* fixing issue where sqlcmdvars wouldn't load from publish profile in ADS

* in -> of
2023-05-12 13:50:26 -07:00
Benjin Dubishar
f364e52079 Fix deploy/generatePlan/saveProfile when no sqlcmdvars are defined (#23112) (#23120)
* fix deploy/generate when no sqlcmdvars are defined

* saveProfile
2023-05-12 13:50:02 -07:00
Maddy
84143d3caf remove the access point (#23105) (#23114) 2023-05-11 19:31:57 -07:00
Kim Santiago
0cfaf69647 fix sqlcmd variables not getting loaded correctly in vscode (#23103) (#23108) 2023-05-11 19:31:34 -07:00
Aasim Khan
343d878457 Adding telemetry to ads OE filter (#23089) (#23106)
* Adding telemetry to ads oe filter

* Fixing prop names

* fixing prop name

* Fixing localized strings

* Update src/sql/azdata.proposed.d.ts



* Update src/sql/workbench/contrib/objectExplorer/browser/serverTreeView.ts



---------

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
2023-05-11 19:31:07 -07:00
Cheena Malhotra
7d0cfc4f99 Support migrating credentials to new format (#23088) (#23095) 2023-05-11 18:41:11 -07:00
Barbara Valdez
f13406570e update workbench file and fix relative link not working in markdown (#23109) (#23113) 2023-05-11 16:52:05 -07:00
Benjin Dubishar
c44ecf56f2 Fixing bug where SQLCMD vars weren't getting JSONified (#23082) (#23096)
* changing param for sqlcmdvars back to Record since Json.stringify doesn't handle Maps

* swapping over savePublishProfile
2023-05-10 23:12:12 -07:00
Aasim Khan
f10fc9b56d Adding back OE filtering in rmay release. (#23070) 2023-05-10 08:41:26 -07:00
Benjin Dubishar
72acd2af83 Bumping SQL Tools Service (#23046) (#23051) 2023-05-09 14:09:15 -07:00
Cheena Malhotra
6112eabc3c Fixes import wizard to work with enabled SQL auth provider (#23004) (#23052) 2023-05-09 11:12:19 -07:00
Lewis Sanchez
27bac701bb Use notification prompt before running a command (#23035) (#23048)
* Use notification prompt before running a command

https://github.com/microsoft/vscode/pull/179702/commits

* Removes unused declarations
2023-05-09 11:10:46 -07:00
Cheena Malhotra
0bcd010d9a Fixes build folder compilation + enable linux .deb files (#23006) (#23042) 2023-05-09 11:10:14 -07:00
Aasim Khan
bca671bc3f disabling async tree by default (#23037) 2023-05-09 11:09:33 -07:00
Aasim Khan
706ba6c974 Adding filtering dialog and action to OE (#22937) (#23036)
* Adding init change

* Adding filter cache in OE

* Adding more filtering changes

* Fixed stuff with dialog

* Fixing filter

* Adding support for connecitons

* Fixed stuff

* filtering

* Fixing  date

* Filters

* Removing is filtering supported

* Removing contracts

* Fixing filters

* Fixing cache

* Adding some accessibility changes

* Reverting some more changes to pull in changes from the main

* Adding comments

* Fixing boolean operators

* Fixing stuff

* Fixing stuff

* Fixing error handling and making dialog generic

* Fixing more stuff

* Making filter a generic dialog

* adding erase icon

* removing floating promises

* Fixing compile issue

* Adding support for choice filter with different and actual value.

* Adding null checks

* Adding durability type fix

* Fixing filtering for providers that do not play well with empty filter properties
2023-05-09 11:07:03 -07:00
Kim Santiago
8e38295691 vbump sql projects to 1.1.1 (#23029) (#23039) 2023-05-08 16:44:44 -07:00
Alan Ren
a97d882e3c fix #174264 (#174845) (#23027) (#23034)
Co-authored-by: Sandeep Somavarapu <sasomava@microsoft.com>
2023-05-08 15:49:00 -07:00
108 changed files with 3190 additions and 2357 deletions

View File

@@ -4,6 +4,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OctoKitIssue = exports.OctoKit = void 0;
const core_1 = require("@actions/core");
const github_1 = require("@actions/github");
const child_process_1 = require("child_process");
@@ -20,7 +21,6 @@ class OctoKit {
}
async *query(query) {
const q = query.q + ` repo:${this.params.owner}/${this.params.repo}`;
console.log(`Querying for ${q}:`);
const options = this.octokit.search.issuesAndPullRequests.endpoint.merge({
...query,
q,
@@ -41,19 +41,17 @@ class OctoKit {
};
for await (const pageResponse of this.octokit.paginate.iterator(options)) {
await timeout();
await utils_1.logRateLimit(this.token);
await (0, utils_1.logRateLimit)(this.token);
const page = pageResponse.data;
console.log(`Page ${++pageNum}: ${page.map(({ number }) => number).join(' ')}`);
yield page.map((issue) => new OctoKitIssue(this.token, this.params, this.octokitIssueToIssue(issue)));
}
}
async createIssue(owner, repo, title, body) {
core_1.debug(`Creating issue \`${title}\` on ${owner}/${repo}`);
(0, core_1.debug)(`Creating issue \`${title}\` on ${owner}/${repo}`);
if (!this.options.readonly)
await this.octokit.issues.create({ owner, repo, title, body });
}
octokitIssueToIssue(issue) {
var _a, _b, _c, _d, _e, _f;
return {
author: { name: issue.user.login, isGitHubApp: issue.user.type === 'Bot' },
body: issue.body,
@@ -64,8 +62,8 @@ class OctoKit {
locked: issue.locked,
numComments: issue.comments,
reactions: issue.reactions,
assignee: (_b = (_a = issue.assignee) === null || _a === void 0 ? void 0 : _a.login) !== null && _b !== void 0 ? _b : (_d = (_c = issue.assignees) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.login,
milestoneId: (_f = (_e = issue.milestone) === null || _e === void 0 ? void 0 : _e.number) !== null && _f !== void 0 ? _f : null,
assignee: issue.assignee?.login ?? issue.assignees?.[0]?.login,
milestoneId: issue.milestone?.number ?? null,
createdAt: +new Date(issue.created_at),
updatedAt: +new Date(issue.updated_at),
closedAt: issue.closed_at ? +new Date(issue.closed_at) : undefined,
@@ -73,10 +71,10 @@ class OctoKit {
}
async hasWriteAccess(user) {
if (user.name in this.writeAccessCache) {
core_1.debug('Got permissions from cache for ' + user);
(0, core_1.debug)('Got permissions from cache for ' + user);
return this.writeAccessCache[user.name];
}
core_1.debug('Fetching permissions for ' + user);
(0, core_1.debug)('Fetching permissions for ' + user);
const permissions = (await this.octokit.repos.getCollaboratorPermissionLevel({
...this.params,
username: user.name,
@@ -96,14 +94,14 @@ class OctoKit {
}
}
async createLabel(name, color, description) {
core_1.debug('Creating label ' + name);
(0, core_1.debug)('Creating label ' + name);
if (!this.options.readonly)
await this.octokit.issues.createLabel({ ...this.params, color, description, name });
else
this.mockLabels.add(name);
}
async deleteLabel(name) {
core_1.debug('Deleting label ' + name);
(0, core_1.debug)('Deleting label ' + name);
try {
if (!this.options.readonly)
await this.octokit.issues.deleteLabel({ ...this.params, name });
@@ -116,7 +114,7 @@ class OctoKit {
}
}
async readConfig(path) {
core_1.debug('Reading config at ' + path);
(0, core_1.debug)('Reading config at ' + path);
const repoPath = `.github/${path}.json`;
const data = (await this.octokit.repos.getContents({ ...this.params, path: repoPath })).data;
if ('type' in data && data.type === 'file') {
@@ -128,10 +126,10 @@ class OctoKit {
throw Error('Found directory at config path when expecting file' + JSON.stringify(data));
}
async releaseContainsCommit(release, commit) {
if (utils_1.getInput('commitReleasedDebuggingOverride')) {
if ((0, utils_1.getInput)('commitReleasedDebuggingOverride')) {
return true;
}
return new Promise((resolve, reject) => child_process_1.exec(`git -C ./repo merge-base --is-ancestor ${commit} ${release}`, (err) => !err || err.code === 1 ? resolve(!err) : reject(err)));
return new Promise((resolve, reject) => (0, child_process_1.exec)(`git -C ./repo merge-base --is-ancestor ${commit} ${release}`, (err) => !err || err.code === 1 ? resolve(!err) : reject(err)));
}
}
exports.OctoKit = OctoKit;
@@ -142,7 +140,7 @@ class OctoKitIssue extends OctoKit {
this.issueData = issueData;
}
async addAssignee(assignee) {
core_1.debug('Adding assignee ' + assignee + ' to ' + this.issueData.number);
(0, core_1.debug)('Adding assignee ' + assignee + ' to ' + this.issueData.number);
if (!this.options.readonly) {
await this.octokit.issues.addAssignees({
...this.params,
@@ -152,7 +150,7 @@ class OctoKitIssue extends OctoKit {
}
}
async closeIssue() {
core_1.debug('Closing issue ' + this.issueData.number);
(0, core_1.debug)('Closing issue ' + this.issueData.number);
if (!this.options.readonly)
await this.octokit.issues.update({
...this.params,
@@ -161,16 +159,15 @@ class OctoKitIssue extends OctoKit {
});
}
async lockIssue() {
core_1.debug('Locking issue ' + this.issueData.number);
(0, core_1.debug)('Locking issue ' + this.issueData.number);
if (!this.options.readonly)
await this.octokit.issues.lock({ ...this.params, issue_number: this.issueData.number });
}
async getIssue() {
if (isIssue(this.issueData)) {
core_1.debug('Got issue data from query result ' + this.issueData.number);
(0, core_1.debug)('Got issue data from query result ' + this.issueData.number);
return this.issueData;
}
console.log('Fetching issue ' + this.issueData.number);
const issue = (await this.octokit.issues.get({
...this.params,
issue_number: this.issueData.number,
@@ -179,7 +176,7 @@ class OctoKitIssue extends OctoKit {
return (this.issueData = this.octokitIssueToIssue(issue));
}
async postComment(body) {
core_1.debug(`Posting comment ${body} on ${this.issueData.number}`);
(0, core_1.debug)(`Posting comment ${body} on ${this.issueData.number}`);
if (!this.options.readonly)
await this.octokit.issues.createComment({
...this.params,
@@ -188,7 +185,7 @@ class OctoKitIssue extends OctoKit {
});
}
async deleteComment(id) {
core_1.debug(`Deleting comment ${id} on ${this.issueData.number}`);
(0, core_1.debug)(`Deleting comment ${id} on ${this.issueData.number}`);
if (!this.options.readonly)
await this.octokit.issues.deleteComment({
owner: this.params.owner,
@@ -197,7 +194,7 @@ class OctoKitIssue extends OctoKit {
});
}
async setMilestone(milestoneId) {
core_1.debug(`Setting milestone for ${this.issueData.number} to ${milestoneId}`);
(0, core_1.debug)(`Setting milestone for ${this.issueData.number} to ${milestoneId}`);
if (!this.options.readonly)
await this.octokit.issues.update({
...this.params,
@@ -206,7 +203,7 @@ class OctoKitIssue extends OctoKit {
});
}
async *getComments(last) {
core_1.debug('Fetching comments for ' + this.issueData.number);
(0, core_1.debug)('Fetching comments for ' + this.issueData.number);
const response = this.octokit.paginate.iterator(this.octokit.issues.listComments.endpoint.merge({
...this.params,
issue_number: this.issueData.number,
@@ -223,7 +220,7 @@ class OctoKitIssue extends OctoKit {
}
}
async addLabel(name) {
core_1.debug(`Adding label ${name} to ${this.issueData.number}`);
(0, core_1.debug)(`Adding label ${name} to ${this.issueData.number}`);
if (!(await this.repoHasLabel(name))) {
throw Error(`Action could not execute becuase label ${name} is not defined.`);
}
@@ -235,7 +232,7 @@ class OctoKitIssue extends OctoKit {
});
}
async removeLabel(name) {
core_1.debug(`Removing label ${name} from ${this.issueData.number}`);
(0, core_1.debug)(`Removing label ${name} from ${this.issueData.number}`);
try {
if (!this.options.readonly)
await this.octokit.issues.removeLabel({
@@ -246,14 +243,12 @@ class OctoKitIssue extends OctoKit {
}
catch (err) {
if (err.status === 404) {
console.log(`Label ${name} not found on issue`);
return;
}
throw err;
}
}
async getClosingInfo() {
var _a;
if ((await this.getIssue()).open) {
return;
}
@@ -267,13 +262,12 @@ class OctoKitIssue extends OctoKit {
for (const timelineEvent of timelineEvents) {
if (timelineEvent.event === 'closed') {
closingCommit = {
hash: (_a = timelineEvent.commit_id) !== null && _a !== void 0 ? _a : undefined,
hash: timelineEvent.commit_id ?? undefined,
timestamp: +new Date(timelineEvent.created_at),
};
}
}
}
console.log(`Got ${closingCommit} as closing commit of ${this.issueData.number}`);
return closingCommit;
}
}

View File

@@ -25,7 +25,6 @@ export class OctoKit implements GitHub {
async *query(query: Query): AsyncIterableIterator<GitHubIssue[]> {
const q = query.q + ` repo:${this.params.owner}/${this.params.repo}`
console.log(`Querying for ${q}:`)
const options = this.octokit.search.issuesAndPullRequests.endpoint.merge({
...query,
@@ -50,7 +49,6 @@ export class OctoKit implements GitHub {
await timeout()
await logRateLimit(this.token)
const page: Array<Octokit.SearchIssuesAndPullRequestsResponseItemsItem> = pageResponse.data
console.log(`Page ${++pageNum}: ${page.map(({ number }) => number).join(' ')}`)
yield page.map(
(issue) => new OctoKitIssue(this.token, this.params, this.octokitIssueToIssue(issue)),
)
@@ -199,7 +197,6 @@ export class OctoKitIssue extends OctoKit implements GitHubIssue {
return this.issueData
}
console.log('Fetching issue ' + this.issueData.number)
const issue = (
await this.octokit.issues.get({
...this.params,
@@ -286,7 +283,6 @@ export class OctoKitIssue extends OctoKit implements GitHubIssue {
})
} catch (err) {
if (err.status === 404) {
console.log(`Label ${name} not found on issue`)
return
}
throw err
@@ -314,7 +310,6 @@ export class OctoKitIssue extends OctoKit implements GitHubIssue {
}
}
}
console.log(`Got ${closingCommit} as closing commit of ${this.issueData.number}`)
return closingCommit
}
}

View File

@@ -4,17 +4,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestbedIssue = exports.Testbed = void 0;
class Testbed {
constructor(config) {
var _a, _b, _c, _d, _e;
this.config = {
globalLabels: (_a = config === null || config === void 0 ? void 0 : config.globalLabels) !== null && _a !== void 0 ? _a : [],
configs: (_b = config === null || config === void 0 ? void 0 : config.configs) !== null && _b !== void 0 ? _b : {},
writers: (_c = config === null || config === void 0 ? void 0 : config.writers) !== null && _c !== void 0 ? _c : [],
releasedCommits: (_d = config === null || config === void 0 ? void 0 : config.releasedCommits) !== null && _d !== void 0 ? _d : [],
queryRunner: (_e = config === null || config === void 0 ? void 0 : config.queryRunner) !== null && _e !== void 0 ? _e : async function* () {
yield [];
},
globalLabels: config?.globalLabels ?? [],
configs: config?.configs ?? {},
writers: config?.writers ?? [],
releasedCommits: config?.releasedCommits ?? [],
queryRunner: config?.queryRunner ??
async function* () {
yield [];
},
};
}
async *query(query) {
@@ -47,16 +48,15 @@ class Testbed {
exports.Testbed = Testbed;
class TestbedIssue extends Testbed {
constructor(globalConfig, issueConfig) {
var _a, _b, _c;
super(globalConfig);
issueConfig = issueConfig !== null && issueConfig !== void 0 ? issueConfig : {};
issueConfig.comments = (_a = issueConfig === null || issueConfig === void 0 ? void 0 : issueConfig.comments) !== null && _a !== void 0 ? _a : [];
issueConfig.labels = (_b = issueConfig === null || issueConfig === void 0 ? void 0 : issueConfig.labels) !== null && _b !== void 0 ? _b : [];
issueConfig = issueConfig ?? {};
issueConfig.comments = issueConfig?.comments ?? [];
issueConfig.labels = issueConfig?.labels ?? [];
issueConfig.issue = {
author: { name: 'JacksonKearl' },
body: 'issue body',
locked: false,
numComments: ((_c = issueConfig === null || issueConfig === void 0 ? void 0 : issueConfig.comments) === null || _c === void 0 ? void 0 : _c.length) || 0,
numComments: issueConfig?.comments?.length || 0,
number: 1,
open: true,
title: 'issue title',
@@ -90,7 +90,7 @@ class TestbedIssue extends Testbed {
}
async postComment(body, author) {
this.issueConfig.comments.push({
author: { name: author !== null && author !== void 0 ? author : 'bot' },
author: { name: author ?? 'bot' },
body,
id: Math.random(),
timestamp: +new Date(),

View File

@@ -8,15 +8,15 @@ const core = require("@actions/core");
const github_1 = require("@actions/github");
const octokit_1 = require("../api/octokit");
const utils_1 = require("../utils/utils");
const token = utils_1.getRequiredInput('token');
const label = utils_1.getRequiredInput('label');
const token = (0, utils_1.getRequiredInput)('token');
const label = (0, utils_1.getRequiredInput)('label');
async function main() {
const pr = new octokit_1.OctoKitIssue(token, github_1.context.repo, { number: github_1.context.issue.number });
pr.addLabel(label);
}
main()
.then(() => utils_1.logRateLimit(token))
.then(() => (0, utils_1.logRateLimit)(token))
.catch(async (error) => {
core.setFailed(error.message);
await utils_1.logErrorToIssue(error.message, true, token);
await (0, utils_1.logErrorToIssue)(error.message, true, token);
});

View File

@@ -1,24 +1,25 @@
{
"name": "github-actions",
"version": "1.0.0",
"description": "GitHub Actions",
"scripts": {
"test": "mocha -r ts-node/register **/*.test.ts",
"build": "tsc -p ./tsconfig.json",
"lint": "eslint -c .eslintrc --fix --ext .ts .",
"watch-typecheck": "tsc --watch"
},
"repository": {
"type": "git",
"url": "git+https://github.com/microsoft/azuredatastudio.git"
},
"keywords": [],
"author": "",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/github": "^2.1.1",
"axios": "^0.21.4",
"name": "github-actions",
"version": "1.0.0",
"description": "GitHub Actions",
"scripts": {
"test": "mocha -r ts-node/register **/*.test.ts",
"build": "tsc -p ./tsconfig.json",
"compile": "tsc -p ./tsconfig.json",
"lint": "eslint -c .eslintrc --fix --ext .ts .",
"watch-typecheck": "tsc --watch"
},
"repository": {
"type": "git",
"url": "git+https://github.com/microsoft/azuredatastudio.git"
},
"keywords": [],
"author": "",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/github": "^2.1.1",
"axios": "^0.21.4",
"ts-node": "^8.6.2",
"typescript": "^3.8.3"
}
}
}

View File

@@ -4,13 +4,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.logErrorToIssue = exports.logRateLimit = exports.daysAgoToHumanReadbleDate = exports.daysAgoToTimestamp = exports.loadLatestRelease = exports.normalizeIssue = exports.getRequiredInput = exports.getInput = void 0;
const core = require("@actions/core");
const github_1 = require("@actions/github");
const axios_1 = require("axios");
const octokit_1 = require("../api/octokit");
exports.getInput = (name) => core.getInput(name) || undefined;
exports.getRequiredInput = (name) => core.getInput(name, { required: true });
exports.normalizeIssue = (issue) => {
const getInput = (name) => core.getInput(name) || undefined;
exports.getInput = getInput;
const getRequiredInput = (name) => core.getInput(name, { required: true });
exports.getRequiredInput = getRequiredInput;
const normalizeIssue = (issue) => {
const { body, title } = issue;
const isBug = body.includes('bug_report_template') || /Issue Type:.*Bug.*/.test(body);
const isFeatureRequest = body.includes('feature_request_template') || /Issue Type:.*Feature Request.*/.test(body);
@@ -33,23 +36,25 @@ exports.normalizeIssue = (issue) => {
issueType: isBug ? 'bug' : isFeatureRequest ? 'feature_request' : 'unknown',
};
};
exports.loadLatestRelease = async (quality) => (await axios_1.default.get(`https://vscode-update.azurewebsites.net/api/update/darwin/${quality}/latest`)).data;
exports.daysAgoToTimestamp = (days) => +new Date(Date.now() - days * 24 * 60 * 60 * 1000);
exports.daysAgoToHumanReadbleDate = (days) => new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().replace(/\.\d{3}\w$/, '');
exports.logRateLimit = async (token) => {
exports.normalizeIssue = normalizeIssue;
const loadLatestRelease = async (quality) => (await axios_1.default.get(`https://vscode-update.azurewebsites.net/api/update/darwin/${quality}/latest`)).data;
exports.loadLatestRelease = loadLatestRelease;
const daysAgoToTimestamp = (days) => +new Date(Date.now() - days * 24 * 60 * 60 * 1000);
exports.daysAgoToTimestamp = daysAgoToTimestamp;
const daysAgoToHumanReadbleDate = (days) => new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().replace(/\.\d{3}\w$/, '');
exports.daysAgoToHumanReadbleDate = daysAgoToHumanReadbleDate;
const logRateLimit = async (token) => {
const usageData = (await new github_1.GitHub(token).rateLimit.get()).data.resources;
['core', 'graphql', 'search'].forEach(async (category) => {
const usage = 1 - usageData[category].remaining / usageData[category].limit;
const message = `Usage at ${usage} for ${category}`;
if (usage > 0) {
console.log(message);
}
if (usage > 0.5) {
await exports.logErrorToIssue(message, false, token);
await (0, exports.logErrorToIssue)(message, false, token);
}
});
};
exports.logErrorToIssue = async (message, ping, token) => {
exports.logRateLimit = logRateLimit;
const logErrorToIssue = async (message, ping, token) => {
// Attempt to wait out abuse detection timeout if present
await new Promise((resolve) => setTimeout(resolve, 10000));
const dest = github_1.context.repo.repo === 'vscode-internalbacklog'
@@ -70,3 +75,4 @@ ${JSON.stringify(github_1.context, null, 2).replace(/<!--/gu, '<@--').replace(/-
-->
`);
};
exports.logErrorToIssue = logErrorToIssue;

View File

@@ -58,13 +58,10 @@ export const daysAgoToHumanReadbleDate = (days: number) =>
new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().replace(/\.\d{3}\w$/, '')
export const logRateLimit = async (token: string) => {
const usageData = (await new GitHub(token).rateLimit.get()).data.resources
;(['core', 'graphql', 'search'] as const).forEach(async (category) => {
const usageData = (await new GitHub(token).rateLimit.get()).data.resources;
(['core', 'graphql', 'search'] as const).forEach(async (category) => {
const usage = 1 - usageData[category].remaining / usageData[category].limit
const message = `Usage at ${usage} for ${category}`
if (usage > 0) {
console.log(message)
}
if (usage > 0.5) {
await logErrorToIssue(message, false, token)
}

View File

@@ -285,6 +285,8 @@ node-fetch@^2.3.0:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
npm-run-path@^2.0.0:
version "2.0.2"
@@ -371,6 +373,11 @@ strip-eof@^1.0.0:
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
ts-node@^8.6.2:
version "8.9.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.9.0.tgz#d7bf7272dcbecd3a2aa18bd0b96c7d2f270c15d4"
@@ -388,9 +395,9 @@ tunnel@0.0.6, tunnel@^0.0.6:
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
typescript@^3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
version "3.9.10"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
universal-user-agent@^4.0.0:
version "4.0.1"
@@ -411,6 +418,19 @@ uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"

View File

@@ -130,7 +130,6 @@ function getEnv(name) {
return result;
}
async function main() {
var _a;
const [, , product, os, arch, unprocessedType, fileName, filePath] = process.argv;
// getPlatform needs the unprocessedType
const platform = getPlatform(product, os, arch, unprocessedType);
@@ -169,7 +168,7 @@ async function main() {
console.log('Blob successfully uploaded to Azure storage.');
})
];
const shouldUploadToMooncake = /true/i.test((_a = process.env['VSCODE_PUBLISH_TO_MOONCAKE']) !== null && _a !== void 0 ? _a : 'true');
const shouldUploadToMooncake = /true/i.test(process.env['VSCODE_PUBLISH_TO_MOONCAKE'] ?? 'true');
if (shouldUploadToMooncake) {
const mooncakeCredential = new identity_1.ClientSecretCredential(process.env['AZURE_MOONCAKE_TENANT_ID'], process.env['AZURE_MOONCAKE_CLIENT_ID'], process.env['AZURE_MOONCAKE_CLIENT_SECRET']);
const mooncakeBlobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, storagePipelineOptions);

View File

@@ -19,12 +19,11 @@ function getEnv(name) {
return result;
}
async function main() {
var _a, _b, _c;
const [, , _version] = process.argv;
const quality = getEnv('VSCODE_QUALITY');
const commit = ((_a = process.env['VSCODE_DISTRO_COMMIT']) === null || _a === void 0 ? void 0 : _a.trim()) || getEnv('BUILD_SOURCEVERSION');
const commit = process.env['VSCODE_DISTRO_COMMIT']?.trim() || getEnv('BUILD_SOURCEVERSION');
const queuedBy = getEnv('BUILD_QUEUEDBY');
const sourceBranch = ((_b = process.env['VSCODE_DISTRO_REF']) === null || _b === void 0 ? void 0 : _b.trim()) || getEnv('BUILD_SOURCEBRANCH');
const sourceBranch = process.env['VSCODE_DISTRO_REF']?.trim() || getEnv('BUILD_SOURCEBRANCH');
const version = _version + (quality === 'stable' ? '' : `-${quality}`);
console.log('Creating build...');
console.log('Quality:', quality);
@@ -35,7 +34,7 @@ async function main() {
timestamp: (new Date()).getTime(),
version,
isReleased: false,
private: Boolean((_c = process.env['VSCODE_DISTRO_REF']) === null || _c === void 0 ? void 0 : _c.trim()),
private: Boolean(process.env['VSCODE_DISTRO_REF']?.trim()),
sourceBranch,
queuedBy,
assets: [],
@@ -44,7 +43,7 @@ async function main() {
const aadCredentials = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']);
const client = new cosmos_1.CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT'], aadCredentials });
const scripts = client.database('builds').container(quality).scripts;
await (0, retry_1.retry)(() => scripts.storedProcedure('createBuild').execute('', [Object.assign(Object.assign({}, build), { _partitionKey: '' })]));
await (0, retry_1.retry)(() => scripts.storedProcedure('createBuild').execute('', [{ ...build, _partitionKey: '' }]));
}
main().then(() => {
console.log('Build successfully created');

View File

@@ -109,7 +109,6 @@ async function assertContainer(containerClient) {
return containerResponse && !!containerResponse.errorCode;
}
async function uploadBlob(blobClient, file) {
var _a, _b;
const result = await blobClient.uploadFile(file, {
blobHTTPHeaders: {
blobContentType: mime.lookup(file),
@@ -117,10 +116,10 @@ async function uploadBlob(blobClient, file) {
}
});
if (result && !result.errorCode) {
console.log(`Blobs uploaded successfully, response status: ${(_a = result === null || result === void 0 ? void 0 : result._response) === null || _a === void 0 ? void 0 : _a.status}`);
console.log(`Blobs uploaded successfully, response status: ${result?._response?.status}`);
}
else {
console.error(`Blobs failed to upload, response status: ${(_b = result === null || result === void 0 ? void 0 : result._response) === null || _b === void 0 ? void 0 : _b.status}, errorcode: ${result === null || result === void 0 ? void 0 : result.errorCode}`);
console.error(`Blobs failed to upload, response status: ${result?._response?.status}, errorcode: ${result?.errorCode}`);
}
}
async function publish(commit, quality, platform, type, name, version, _isUpdate, file, opts) {

View File

@@ -180,12 +180,11 @@ steps:
# continueOnError: true
# condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
# {{SQL CARBON TODO}} - Reenable
# - script: |
# set -e
# yarn gulp vscode-linux-x64-build-deb
# displayName: Build Deb
# condition: and(succeeded(), ne(variables['EXTENSIONS_ONLY'], 'true'))
- script: |
set -e
yarn gulp vscode-linux-x64-build-deb
displayName: Build Deb
condition: and(succeeded(), ne(variables['EXTENSIONS_ONLY'], 'true'))
- script: |
set -e

View File

@@ -27,16 +27,16 @@ If (-NOT ($Quality -eq "stable")) {
node $sourcesDir\build\azure-pipelines\common\publish.js $Quality $PlatformLinux archive-unsigned "$TarballUploadName.tar.gz" $Version true $TarballPath $CommitId
# Publish DEB
# $PlatformDeb = "linux-deb-$Arch"
# $DebFilename = "$(Get-ChildItem -File -Name $artifactsDir\linux\deb\amd64\deb\*.deb)"
# $DebPath = "$artifactsDir\linux\deb\amd64\deb\$DebFilename"
# $DebUploadName = "azuredatastudio-linux-$Version"
$PlatformDeb = "linux-deb-$Arch"
$DebFilename = "$(Get-ChildItem -File -Name $artifactsDir\linux\deb\amd64\deb\*.deb)"
$DebPath = "$artifactsDir\linux\deb\amd64\deb\$DebFilename"
$DebUploadName = "azuredatastudio-linux-$Version"
# If (-NOT ($Quality -eq "stable")) {
# $DebUploadName = "$DebUploadName-$Quality"
# }
If (-NOT ($Quality -eq "stable")) {
$DebUploadName = "$DebUploadName-$Quality"
}
# node $sourcesDir\build\azure-pipelines\common\publish.js $Quality $PlatformDeb package "$DebUploadName.deb" $Version true $DebPath $CommitId
node $sourcesDir\build\azure-pipelines\common\publish.js $Quality $PlatformDeb package "$DebUploadName.deb" $Version true $DebPath $CommitId
# Publish RPM
$PlatformRpm = "linux-rpm-$Arch"

View File

@@ -43,7 +43,7 @@ async function mixinClient(quality) {
else {
fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name));
}
return Object.assign(Object.assign({ webBuiltInExtensions: originalProduct.webBuiltInExtensions }, o), { builtInExtensions });
return { webBuiltInExtensions: originalProduct.webBuiltInExtensions, ...o, builtInExtensions };
}))
.pipe(productJsonFilter.restore)
.pipe(es.mapSync((f) => {
@@ -64,7 +64,7 @@ function mixinServer(quality) {
fancyLog(ansiColors.blue('[mixin]'), `Mixing in server:`);
const originalProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8'));
const serverProductJson = JSON.parse(fs.readFileSync(serverProductJsonPath, 'utf8'));
fs.writeFileSync('product.json', JSON.stringify(Object.assign(Object.assign({}, originalProduct), serverProductJson), undefined, '\t'));
fs.writeFileSync('product.json', JSON.stringify({ ...originalProduct, ...serverProductJson }, undefined, '\t'));
fancyLog(ansiColors.blue('[mixin]'), 'product.json', ansiColors.green('✔︎'));
}
function main() {

View File

@@ -40,14 +40,26 @@ async function main() {
identity: '99FM488X57',
'gatekeeper-assess': false
};
const appOpts = Object.assign(Object.assign({}, defaultOpts), {
const appOpts = {
...defaultOpts,
// TODO(deepak1556): Incorrectly declared type in electron-osx-sign
ignore: (filePath) => {
return filePath.includes(gpuHelperAppName) ||
filePath.includes(rendererHelperAppName);
} });
const gpuHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, gpuHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist') });
const rendererHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, rendererHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist') });
}
};
const gpuHelperOpts = {
...defaultOpts,
app: path.join(appFrameworkPath, gpuHelperAppName),
entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'),
'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'),
};
const rendererHelperOpts = {
...defaultOpts,
app: path.join(appFrameworkPath, rendererHelperAppName),
entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'),
'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'),
};
// Only overwrite plist entries for x64 and arm64 builds,
// universal will get its copy from the x64 build.
if (arch !== 'universal') {

View File

@@ -1,15 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"jsx": "preserve",
"checkJs": true
},
"include": [
"**/*.js"
],
"exclude": [
"node_modules",
"**/node_modules/*"
]
}

View File

@@ -45,8 +45,7 @@ function isUpToDate(extension) {
}
}
function syncMarketplaceExtension(extension) {
var _a;
const galleryServiceUrl = (_a = productjson.extensionsGallery) === null || _a === void 0 ? void 0 : _a.serviceUrl;
const galleryServiceUrl = productjson.extensionsGallery?.serviceUrl;
const source = ansiColors.blue(galleryServiceUrl ? '[marketplace]' : '[github]');
if (isUpToDate(extension)) {
log(source, `${extension.name}@${extension.version}`, ansiColors.green('✔︎'));

View File

@@ -18,7 +18,6 @@ const token = process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'
const contentBasePath = 'raw.githubusercontent.com';
const contentFileNames = ['package.json', 'package-lock.json', 'yarn.lock'];
async function downloadExtensionDetails(extension) {
var _a, _b, _c;
const extensionLabel = `${extension.name}@${extension.version}`;
const repository = url.parse(extension.repo).path.substr(1);
const repositoryContentBaseUrl = `https://${token ? `${token}@` : ''}${contentBasePath}/${repository}/v${extension.version}`;
@@ -56,11 +55,11 @@ async function downloadExtensionDetails(extension) {
}
}
// Validation
if (!((_a = results.find(r => r.fileName === 'package.json')) === null || _a === void 0 ? void 0 : _a.body)) {
if (!results.find(r => r.fileName === 'package.json')?.body) {
// throw new Error(`The "package.json" file could not be found for the built-in extension - ${extensionLabel}`);
}
if (!((_b = results.find(r => r.fileName === 'package-lock.json')) === null || _b === void 0 ? void 0 : _b.body) &&
!((_c = results.find(r => r.fileName === 'yarn.lock')) === null || _c === void 0 ? void 0 : _c.body)) {
if (!results.find(r => r.fileName === 'package-lock.json')?.body &&
!results.find(r => r.fileName === 'yarn.lock')?.body) {
// throw new Error(`The "package-lock.json"/"yarn.lock" could not be found for the built-in extension - ${extensionLabel}`);
}
}

View File

@@ -21,12 +21,11 @@ function bundle(entryPoints, config, callback) {
});
const allMentionedModulesMap = {};
entryPoints.forEach((module) => {
var _a, _b;
allMentionedModulesMap[module.name] = true;
(_a = module.include) === null || _a === void 0 ? void 0 : _a.forEach(function (includedModule) {
module.include?.forEach(function (includedModule) {
allMentionedModulesMap[includedModule] = true;
});
(_b = module.exclude) === null || _b === void 0 ? void 0 : _b.forEach(function (excludedModule) {
module.exclude?.forEach(function (excludedModule) {
allMentionedModulesMap[excludedModule] = true;
});
});

View File

@@ -1,8 +1,8 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = exports.watchTask = exports.compileTask = exports.transpileTask = void 0;
const es = require("event-stream");
@@ -38,7 +38,7 @@ function createCompile(src, build, emitError, transpileOnly) {
const tsb = require('./tsb');
const sourcemaps = require('gulp-sourcemaps');
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
const overrideOptions = Object.assign(Object.assign({}, getTypeScriptCompilerOptions(src)), { inlineSources: Boolean(build) });
const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) };
// {{SQL CARBON EDIT}} Add override for not inlining the sourcemap during build so we can get code coverage - it
// currently expects a *.map.js file to exist next to the source file for proper source mapping
if (!build && !process.env['SQL_NO_INLINE_SOURCEMAP']) {
@@ -52,7 +52,7 @@ function createCompile(src, build, emitError, transpileOnly) {
console.warn('* and re-run the build/watch task *');
console.warn('********************************************************************************************');
}
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly }, err => reporter(err));
function pipeline(token) {
const bom = require('gulp-bom');
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));

View File

@@ -60,7 +60,7 @@ function createCompile(src: string, build: boolean, emitError: boolean, transpil
}
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly }, err => reporter(err));
function pipeline(token?: util.ICancellationToken) {
const bom = require('gulp-bom') as typeof import('gulp-bom');

View File

@@ -36,7 +36,7 @@ function asYarnDependency(prefix, tree) {
return { name, version, path: dependencyPath, children };
}
function getYarnProductionDependencies(cwd) {
const raw = cp.execSync('yarn list --json', { cwd, encoding: 'utf8', env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production' }), stdio: [null, null, 'inherit'] });
const raw = cp.execSync('yarn list --json', { cwd, encoding: 'utf8', env: { ...process.env, NODE_ENV: 'production' }, stdio: [null, null, 'inherit'] });
const match = /^{"type":"tree".*$/m.exec(raw);
if (!match || match.length !== 1) {
throw new Error('Could not parse result of `yarn list --json`');

View File

@@ -40,7 +40,7 @@ const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSyn
function darwinBundleDocumentType(extensions, icon, nameOrSuffix, utis) {
// If given a suffix, generate a name from it. If not given anything, default to 'document'
if (isDocumentSuffix(nameOrSuffix) || !nameOrSuffix) {
nameOrSuffix = icon.charAt(0).toUpperCase() + icon.slice(1) + ' ' + (nameOrSuffix !== null && nameOrSuffix !== void 0 ? nameOrSuffix : 'document');
nameOrSuffix = icon.charAt(0).toUpperCase() + icon.slice(1) + ' ' + (nameOrSuffix ?? 'document');
}
return {
name: nameOrSuffix,

View File

@@ -20,8 +20,7 @@ module.exports = {
return {
// /.../
['Literal[regex]']: (node) => {
var _a;
const pattern = (_a = node.regex) === null || _a === void 0 ? void 0 : _a.pattern;
const pattern = node.regex?.pattern;
if (_containsLookBehind(pattern)) {
context.report({
node,

View File

@@ -14,9 +14,8 @@ module.exports = new class ApiLiteralOrTypes {
create(context) {
return {
['TSDeclareFunction Identifier[name=/create.*/]']: (node) => {
var _a;
const decl = node.parent;
if (((_a = decl.returnType) === null || _a === void 0 ? void 0 : _a.typeAnnotation.type) !== experimental_utils_1.AST_NODE_TYPES.TSTypeReference) {
if (decl.returnType?.typeAnnotation.type !== experimental_utils_1.AST_NODE_TYPES.TSTypeReference) {
return;
}
if (decl.returnType.typeAnnotation.typeName.type !== experimental_utils_1.AST_NODE_TYPES.Identifier) {

View File

@@ -25,8 +25,7 @@ module.exports = new (_a = class ApiEventNaming {
const verbs = new Set(config.verbs);
return {
['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node) => {
var _a, _b;
const def = (_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent;
const def = node.parent?.parent?.parent;
const ident = this.getIdent(def);
if (!ident) {
// event on unknown structure...

View File

@@ -17,8 +17,7 @@ module.exports = new (_a = class ApiProviderNaming {
const allowed = new Set(config.allowed);
return {
['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature']: (node) => {
var _a;
const interfaceName = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent).id.name;
const interfaceName = (node.parent?.parent).id.name;
if (allowed.has(interfaceName)) {
// allowed
return;

View File

@@ -34,14 +34,14 @@ function minifyExtensionResources(input) {
.pipe(jsonFilter)
.pipe(buffer())
.pipe(es.mapSync((f) => {
const errors = [];
const value = jsoncParser.parse(f.contents.toString('utf8'), errors);
if (errors.length === 0) {
// file parsed OK => just stringify to drop whitespace and comments
f.contents = Buffer.from(JSON.stringify(value));
}
return f;
}))
const errors = [];
const value = jsoncParser.parse(f.contents.toString('utf8'), errors);
if (errors.length === 0) {
// file parsed OK => just stringify to drop whitespace and comments
f.contents = Buffer.from(JSON.stringify(value));
}
return f;
}))
.pipe(jsonFilter.restore);
}
function updateExtensionPackageJSON(input, update) {
@@ -50,10 +50,10 @@ function updateExtensionPackageJSON(input, update) {
.pipe(packageJsonFilter)
.pipe(buffer())
.pipe(es.mapSync((f) => {
const data = JSON.parse(f.contents.toString('utf8'));
f.contents = Buffer.from(JSON.stringify(update(data)));
return f;
}))
const data = JSON.parse(f.contents.toString('utf8'));
f.contents = Buffer.from(JSON.stringify(update(data)));
return f;
}))
.pipe(packageJsonFilter.restore);
}
function fromLocal(extensionPath, forWeb) {
@@ -95,11 +95,11 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
const files = fileNames
.map(fileName => path.join(extensionPath, fileName))
.map(filePath => new File({
path: filePath,
stat: fs.statSync(filePath),
base: extensionPath,
contents: fs.createReadStream(filePath)
}));
path: filePath,
stat: fs.statSync(filePath),
base: extensionPath,
contents: fs.createReadStream(filePath)
}));
// check for a webpack configuration files, then invoke webpack
// and merge its output with the files stream.
const webpackConfigLocations = glob.sync(path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] });
@@ -119,24 +119,27 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
};
const exportedConfig = require(webpackConfigPath);
return (Array.isArray(exportedConfig) ? exportedConfig : [exportedConfig]).map(config => {
const webpackConfig = Object.assign(Object.assign({}, config), { mode: 'production' });
const webpackConfig = {
...config,
...{ mode: 'production' }
};
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);
return webpackGulp(webpackConfig, webpack, webpackDone)
.pipe(es.through(function (data) {
data.stat = data.stat || {};
data.base = extensionPath;
this.emit('data', data);
}))
data.stat = data.stat || {};
data.base = extensionPath;
this.emit('data', data);
}))
.pipe(es.through(function (data) {
// source map handling:
// * rewrite sourceMappingURL
// * save to disk so that upload-task picks this up
const contents = data.contents.toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
}), 'utf8');
this.emit('data', data);
}));
// source map handling:
// * rewrite sourceMappingURL
// * save to disk so that upload-task picks this up
const contents = data.contents.toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
}), 'utf8');
this.emit('data', data);
}));
});
});
es.merge(...webpackStreams, es.readArray(files))
@@ -158,16 +161,16 @@ function fromLocalNormal(extensionPath) {
const result = es.through();
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn })
.then(fileNames => {
const files = fileNames
.map(fileName => path.join(extensionPath, fileName))
.map(filePath => new File({
path: filePath,
stat: fs.statSync(filePath),
base: extensionPath,
contents: fs.createReadStream(filePath)
}));
es.readArray(files).pipe(result);
})
const files = fileNames
.map(fileName => path.join(extensionPath, fileName))
.map(filePath => new File({
path: filePath,
stat: fs.statSync(filePath),
base: extensionPath,
contents: fs.createReadStream(filePath)
}));
es.readArray(files).pipe(result);
})
.catch(err => result.emit('error', err));
return result.pipe((0, stats_1.createStatsStream)(path.basename(extensionPath)));
}
@@ -209,7 +212,10 @@ const ghApiHeaders = {
if (process.env.GITHUB_TOKEN) {
ghApiHeaders.Authorization = 'Basic ' + Buffer.from(process.env.GITHUB_TOKEN).toString('base64');
}
const ghDownloadHeaders = Object.assign(Object.assign({}, ghApiHeaders), { Accept: 'application/octet-stream' });
const ghDownloadHeaders = {
...ghApiHeaders,
Accept: 'application/octet-stream',
};
function fromGithub({ name, version, repo, metadata }) {
const remote = require('gulp-remote-retry-src');
const json = require('gulp-json-editor');
@@ -244,6 +250,7 @@ const excludedExtensions = [
'ms-vscode.node-debug',
'ms-vscode.node-debug2',
'vscode-custom-editor-tests',
'vscode-notebook-tests',
'integration-tests', // {{SQL CARBON EDIT}}
];
// {{SQL CARBON EDIT}}
@@ -258,7 +265,6 @@ const externalExtensions = [
'arc',
'asde-deployment',
'azcli',
'azurehybridtoolkit',
'azuremonitor',
'cms',
'dacpac',
@@ -324,11 +330,11 @@ function isWebExtension(manifest) {
function packageLocalExtensionsStream(forWeb) {
const localExtensionsDescriptions = (glob.sync('extensions/*/package.json')
.map(manifestPath => {
const absoluteManifestPath = path.join(root, manifestPath);
const extensionPath = path.dirname(path.join(root, manifestPath));
const extensionName = path.basename(extensionPath);
return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath };
})
const absoluteManifestPath = path.join(root, manifestPath);
const extensionPath = path.dirname(path.join(root, manifestPath));
const extensionName = path.basename(extensionPath);
return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath };
})
.filter(({ name }) => excludedExtensions.indexOf(name) === -1)
.filter(({ name }) => builtInExtensions.every(b => b.name !== name))
.filter(({ name }) => externalExtensions.indexOf(name) === -1) // {{SQL CARBON EDIT}} Remove external Extensions with separate package
@@ -359,15 +365,15 @@ function packageMarketplaceExtensionsStream(forWeb, galleryServiceUrl) {
];
const marketplaceExtensionsStream = minifyExtensionResources(es.merge(...marketplaceExtensionsDescriptions
.map(extension => {
const input = (galleryServiceUrl ? fromMarketplace(galleryServiceUrl, extension) : fromGithub(extension))
.pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`));
return updateExtensionPackageJSON(input, (data) => {
delete data.scripts;
delete data.dependencies;
delete data.devDependencies;
return data;
});
})));
const input = (galleryServiceUrl ? fromMarketplace(galleryServiceUrl, extension) : fromGithub(extension))
.pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`));
return updateExtensionPackageJSON(input, (data) => {
delete data.scripts;
delete data.dependencies;
delete data.devDependencies;
return data;
});
})));
return (marketplaceExtensionsStream
.pipe(util2.setExecutableBit(['**/*.sh'])));
}
@@ -412,10 +418,10 @@ exports.scanBuiltinExtensions = scanBuiltinExtensions;
function packageExternalExtensionsStream() {
const extenalExtensionDescriptions = glob.sync('extensions/*/package.json')
.map(manifestPath => {
const extensionPath = path.dirname(path.join(root, manifestPath));
const extensionName = path.basename(extensionPath);
return { name: extensionName, path: extensionPath };
})
const extensionPath = path.dirname(path.join(root, manifestPath));
const extensionName = path.basename(extensionPath);
return { name: extensionName, path: extensionPath };
})
.filter(({ name }) => externalExtensions.indexOf(name) >= 0 || exports.vscodeExternalExtensions.indexOf(name) >= 0);
const builtExtensions = extenalExtensionDescriptions.map(extension => {
return fromLocal(extension.path, false)
@@ -433,10 +439,10 @@ exports.cleanRebuildExtensions = cleanRebuildExtensions;
function packageRebuildExtensionsStream() {
const extenalExtensionDescriptions = glob.sync('extensions/*/package.json')
.map(manifestPath => {
const extensionPath = path.dirname(path.join(root, manifestPath));
const extensionName = path.basename(extensionPath);
return { name: extensionName, path: extensionPath };
})
const extensionPath = path.dirname(path.join(root, manifestPath));
const extensionName = path.basename(extensionPath);
return { name: extensionName, path: extensionPath };
})
.filter(({ name }) => rebuildExtensions.indexOf(name) >= 0);
const builtExtensions = extenalExtensionDescriptions.map(extension => {
return fromLocal(extension.path, false)
@@ -530,7 +536,7 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
reject();
}
else {
reporter(stats === null || stats === void 0 ? void 0 : stats.toJson());
reporter(stats?.toJson());
}
});
}
@@ -541,7 +547,7 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
reject();
}
else {
reporter(stats === null || stats === void 0 ? void 0 : stats.toJson());
reporter(stats?.toJson());
resolve();
}
});

View File

@@ -1012,7 +1012,7 @@ function prepareI18nFiles() {
}
exports.prepareI18nFiles = prepareI18nFiles;
function createI18nFile(originalFilePath, messages) {
const result = Object.create(null);
let result = Object.create(null);
result[''] = [
'--------------------------------------------------------------------------------------------',
'Copyright (c) Microsoft Corporation. All rights reserved.',
@@ -1035,16 +1035,16 @@ function createI18nFile(originalFilePath, messages) {
exports.createI18nFile = createI18nFile;
exports.i18nPackVersion = '1.0.0'; // {{SQL CARBON EDIT}} Needed in locfunc.
function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pseudo = false) {
let parsePromises = [];
let mainPack = { version: exports.i18nPackVersion, contents: {} };
let extensionsPacks = {};
let errors = [];
const parsePromises = [];
const mainPack = { version: exports.i18nPackVersion, contents: {} };
const extensionsPacks = {};
const errors = [];
return (0, event_stream_1.through)(function (xlf) {
let project = path.basename(path.dirname(path.dirname(xlf.relative)));
let resource = path.basename(xlf.relative, '.xlf');
let contents = xlf.contents.toString();
const project = path.basename(path.dirname(path.dirname(xlf.relative)));
const resource = path.basename(xlf.relative, '.xlf');
const contents = xlf.contents.toString();
log(`Found ${project}: ${resource}`);
let parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents);
const parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents);
parsePromises.push(parsePromise);
parsePromise.then(resolvedFiles => {
resolvedFiles.forEach(file => {

View File

@@ -113,7 +113,7 @@ const RULES = [
},
// Common: vs/platform/native/common/native.ts
{
target: '**/vs/platform/native/common/native.ts',
target: '**/{vs,sql}/platform/native/common/native.ts',
allowedTypes: CORE_TYPES,
disallowedTypes: [ /* Ignore native types that are defined from here */],
disallowedDefinitions: [
@@ -209,7 +209,6 @@ let hasErrors = false;
function checkFile(program, sourceFile, rule) {
checkNode(sourceFile);
function checkNode(node) {
var _a, _b;
if (node.kind !== ts.SyntaxKind.Identifier) {
return ts.forEachChild(node, checkNode); // recurse down
}
@@ -224,10 +223,10 @@ function checkFile(program, sourceFile, rule) {
}
const parentSymbol = _parentSymbol;
const text = parentSymbol.getName();
if ((_a = rule.allowedTypes) === null || _a === void 0 ? void 0 : _a.some(allowed => allowed === text)) {
if (rule.allowedTypes?.some(allowed => allowed === text)) {
return; // override
}
if ((_b = rule.disallowedTypes) === null || _b === void 0 ? void 0 : _b.some(disallowed => disallowed === text)) {
if (rule.disallowedTypes?.some(disallowed => disallowed === text)) {
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
console.log(`[build/lib/layersChecker.ts]: Reference to type '${text}' violates layer '${rule.target}' (${sourceFile.fileName} (${line + 1},${character + 1})`);
hasErrors = true;

View File

@@ -77,6 +77,12 @@ const RULES: IRule[] = [
skip: true // -> skip all test files
},
// TODO@bpasero remove me once electron utility process has landed
{
target: '**/vs/workbench/services/extensions/electron-sandbox/nativeLocalProcessExtensionHost.ts',
skip: true
},
// Common: vs/base/common/platform.ts
{
target: '**/{vs,sql}/base/common/platform.ts',
@@ -117,7 +123,7 @@ const RULES: IRule[] = [
// Common: vs/platform/native/common/native.ts
{
target: '**/vs/platform/native/common/native.ts',
target: '**/{vs,sql}/platform/native/common/native.ts',
allowedTypes: CORE_TYPES,
disallowedTypes: [/* Ignore native types that are defined from here */],
disallowedDefinitions: [

View File

@@ -71,7 +71,7 @@ function updateMainI18nFile(existingTranslationFilePath, originalFilePath, messa
delete objectContents[`${contentKey}`];
}
}
messages.contents = Object.assign(Object.assign({}, objectContents), messages.contents);
messages.contents = { ...objectContents, ...messages.contents };
result[''] = [
'--------------------------------------------------------------------------------------------',
'Copyright (c) Microsoft Corporation. All rights reserved.',
@@ -141,9 +141,7 @@ function modifyI18nPackFiles(existingTranslationFolder, resultingTranslationPath
this.queue(translatedExtFile);
// exclude altered vscode extensions from having a new path even if we provide a new I18n file.
if (alteredVSCodeExtensions.indexOf(extension) === -1) {
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
//(Other extensions need to have publisher appended in front as their ID.)
let adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
let adsExtensionId = 'Microsoft.' + extension;
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
}
}
@@ -248,7 +246,7 @@ function refreshLangpacks() {
try {
fs.statSync(locExtFolder);
}
catch (_a) {
catch {
console.log('Language is not included in ADS yet: ' + langId);
continue;
}
@@ -311,7 +309,7 @@ function refreshLangpacks() {
}
fs.statSync(path.join(translationDataFolder, curr.path.replace('./translations', '')));
}
catch (_a) {
catch {
nonExistantExtensions.push(curr);
}
}
@@ -365,7 +363,7 @@ function renameVscodeLangpacks() {
try {
fs.statSync(locVSCODEFolder);
}
catch (_a) {
catch {
console.log('vscode pack is not in ADS yet: ' + langId);
continue;
}

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
@@ -95,10 +95,6 @@ class BooleanPolicy extends BasePolicy {
}
}
class IntPolicy extends BasePolicy {
constructor(name, category, minimumVersion, description, moduleName, defaultValue) {
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
this.defaultValue = defaultValue;
}
static from(name, category, minimumVersion, description, moduleName, settingNode) {
const type = getStringProperty(settingNode, 'type');
if (type !== 'number') {
@@ -110,6 +106,10 @@ class IntPolicy extends BasePolicy {
}
return new IntPolicy(name, category, minimumVersion, description, moduleName, defaultValue);
}
constructor(name, category, minimumVersion, description, moduleName, defaultValue) {
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
this.defaultValue = defaultValue;
}
renderADMXElements() {
return [
`<decimal id="${this.name}" valueName="${this.name}" />`
@@ -139,11 +139,6 @@ class StringPolicy extends BasePolicy {
}
}
class StringEnumPolicy extends BasePolicy {
constructor(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions) {
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
this.enum_ = enum_;
this.enumDescriptions = enumDescriptions;
}
static from(name, category, minimumVersion, description, moduleName, settingNode) {
const type = getStringProperty(settingNode, 'type');
if (type !== 'string') {
@@ -165,6 +160,11 @@ class StringEnumPolicy extends BasePolicy {
}
return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions);
}
constructor(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions) {
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
this.enum_ = enum_;
this.enumDescriptions = enumDescriptions;
}
renderADMXElements() {
return [
`<enum id="${this.name}" valueName="${this.name}">`,

View File

@@ -13,7 +13,7 @@ const rootDir = path.resolve(__dirname, '..', '..');
function runProcess(command, args = []) {
return new Promise((resolve, reject) => {
const child = (0, child_process_1.spawn)(command, args, { cwd: rootDir, stdio: 'inherit', env: process.env });
child.on('exit', err => !err ? resolve() : process.exit(err !== null && err !== void 0 ? err : 1));
child.on('exit', err => !err ? resolve() : process.exit(err ?? 1));
child.on('error', reject);
});
}
@@ -22,7 +22,7 @@ async function exists(subdir) {
await fs_1.promises.stat(path.join(rootDir, subdir));
return true;
}
catch (_a) {
catch {
return false;
}
}

View File

@@ -27,7 +27,6 @@ function writeFile(filePath, contents) {
fs.writeFileSync(filePath, contents);
}
function extractEditor(options) {
var _a;
const ts = require('typescript');
const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString());
let compilerOptions;
@@ -49,7 +48,7 @@ function extractEditor(options) {
// Take the extra included .d.ts files from `tsconfig.monaco.json`
options.typings = tsConfig.include.filter(includedFile => /\.d\.ts$/.test(includedFile));
// Add extra .d.ts files from `node_modules/@types/`
if (Array.isArray((_a = options.compilerOptions) === null || _a === void 0 ? void 0 : _a.types)) {
if (Array.isArray(options.compilerOptions?.types)) {
options.compilerOptions.types.forEach((type) => {
options.typings.push(`../node_modules/@types/${type}/index.d.ts`);
});

View File

@@ -16,11 +16,11 @@ var ShakeLevel;
})(ShakeLevel = exports.ShakeLevel || (exports.ShakeLevel = {}));
function toStringShakeLevel(shakeLevel) {
switch (shakeLevel) {
case 0 /* Files */:
case 0 /* ShakeLevel.Files */:
return 'Files (0)';
case 1 /* InnerFile */:
case 1 /* ShakeLevel.InnerFile */:
return 'InnerFile (1)';
case 2 /* ClassMembers */:
case 2 /* ShakeLevel.ClassMembers */:
return 'ClassMembers (2)';
}
}
@@ -223,7 +223,7 @@ var NodeColor;
NodeColor[NodeColor["Black"] = 2] = "Black";
})(NodeColor || (NodeColor = {}));
function getColor(node) {
return node.$$$color || 0 /* White */;
return node.$$$color || 0 /* NodeColor.White */;
}
function setColor(node, color) {
node.$$$color = color;
@@ -237,7 +237,7 @@ function isNeededSourceFile(node) {
function nodeOrParentIsBlack(node) {
while (node) {
const color = getColor(node);
if (color === 2 /* Black */) {
if (color === 2 /* NodeColor.Black */) {
return true;
}
node = node.parent;
@@ -245,7 +245,7 @@ function nodeOrParentIsBlack(node) {
return false;
}
function nodeOrChildIsBlack(node) {
if (getColor(node) === 2 /* Black */) {
if (getColor(node) === 2 /* NodeColor.Black */) {
return true;
}
for (const child of node.getChildren()) {
@@ -309,10 +309,10 @@ function markNodes(ts, languageService, options) {
if (!program) {
throw new Error('Could not get program from language service');
}
if (options.shakeLevel === 0 /* Files */) {
if (options.shakeLevel === 0 /* ShakeLevel.Files */) {
// Mark all source files Black
program.getSourceFiles().forEach((sourceFile) => {
setColor(sourceFile, 2 /* Black */);
setColor(sourceFile, 2 /* NodeColor.Black */);
});
return;
}
@@ -324,7 +324,7 @@ function markNodes(ts, languageService, options) {
sourceFile.forEachChild((node) => {
if (ts.isImportDeclaration(node)) {
if (!node.importClause && ts.isStringLiteral(node.moduleSpecifier)) {
setColor(node, 2 /* Black */);
setColor(node, 2 /* NodeColor.Black */);
enqueueImport(node, node.moduleSpecifier.text);
}
return;
@@ -332,7 +332,7 @@ function markNodes(ts, languageService, options) {
if (ts.isExportDeclaration(node)) {
if (!node.exportClause && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
// export * from "foo";
setColor(node, 2 /* Black */);
setColor(node, 2 /* NodeColor.Black */);
enqueueImport(node, node.moduleSpecifier.text);
}
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
@@ -373,21 +373,21 @@ function markNodes(ts, languageService, options) {
return null;
}
function enqueue_gray(node) {
if (nodeOrParentIsBlack(node) || getColor(node) === 1 /* Gray */) {
if (nodeOrParentIsBlack(node) || getColor(node) === 1 /* NodeColor.Gray */) {
return;
}
setColor(node, 1 /* Gray */);
setColor(node, 1 /* NodeColor.Gray */);
gray_queue.push(node);
}
function enqueue_black(node) {
const previousColor = getColor(node);
if (previousColor === 2 /* Black */) {
if (previousColor === 2 /* NodeColor.Black */) {
return;
}
if (previousColor === 1 /* Gray */) {
if (previousColor === 1 /* NodeColor.Gray */) {
// remove from gray queue
gray_queue.splice(gray_queue.indexOf(node), 1);
setColor(node, 0 /* White */);
setColor(node, 0 /* NodeColor.White */);
// add to black queue
enqueue_black(node);
// move from one queue to the other
@@ -400,7 +400,7 @@ function markNodes(ts, languageService, options) {
}
const fileName = node.getSourceFile().fileName;
if (/^defaultLib:/.test(fileName) || /\.d\.ts$/.test(fileName)) {
setColor(node, 2 /* Black */);
setColor(node, 2 /* NodeColor.Black */);
return;
}
const sourceFile = node.getSourceFile();
@@ -411,9 +411,9 @@ function markNodes(ts, languageService, options) {
if (ts.isSourceFile(node)) {
return;
}
setColor(node, 2 /* Black */);
setColor(node, 2 /* NodeColor.Black */);
black_queue.push(node);
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
if (options.shakeLevel === 2 /* ShakeLevel.ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth());
if (references) {
for (let i = 0, len = references.length; i < len; i++) {
@@ -476,7 +476,7 @@ function markNodes(ts, languageService, options) {
if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) {
gray_queue.splice(i, 1);
black_queue.push(node);
setColor(node, 2 /* Black */);
setColor(node, 2 /* NodeColor.Black */);
i--;
}
}
@@ -506,7 +506,7 @@ function markNodes(ts, languageService, options) {
// (they can be the declaration of a module import)
continue;
}
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) {
if (options.shakeLevel === 2 /* ShakeLevel.ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) {
enqueue_black(declaration.name);
for (let j = 0; j < declaration.members.length; j++) {
const member = declaration.members[j];
@@ -556,7 +556,7 @@ function markNodes(ts, languageService, options) {
const aliased = checker.getAliasedSymbol(symbol);
if (aliased.declarations && aliased.declarations.length > 0) {
if (nodeOrParentIsBlack(aliased.declarations[0]) || nodeOrChildIsBlack(aliased.declarations[0])) {
setColor(node, 2 /* Black */);
setColor(node, 2 /* NodeColor.Black */);
}
}
}
@@ -603,7 +603,7 @@ function generateResult(ts, languageService, shakeLevel) {
result += data;
}
function writeMarkedNodes(node) {
if (getColor(node) === 2 /* Black */) {
if (getColor(node) === 2 /* NodeColor.Black */) {
return keep(node);
}
// Always keep certain top-level statements
@@ -619,34 +619,34 @@ function generateResult(ts, languageService, shakeLevel) {
if (ts.isImportDeclaration(node)) {
if (node.importClause && node.importClause.namedBindings) {
if (ts.isNamespaceImport(node.importClause.namedBindings)) {
if (getColor(node.importClause.namedBindings) === 2 /* Black */) {
if (getColor(node.importClause.namedBindings) === 2 /* NodeColor.Black */) {
return keep(node);
}
}
else {
const survivingImports = [];
for (const importNode of node.importClause.namedBindings.elements) {
if (getColor(importNode) === 2 /* Black */) {
if (getColor(importNode) === 2 /* NodeColor.Black */) {
survivingImports.push(importNode.getFullText(sourceFile));
}
}
const leadingTriviaWidth = node.getLeadingTriviaWidth();
const leadingTrivia = sourceFile.text.substr(node.pos, leadingTriviaWidth);
if (survivingImports.length > 0) {
if (node.importClause && node.importClause.name && getColor(node.importClause) === 2 /* Black */) {
if (node.importClause && node.importClause.name && getColor(node.importClause) === 2 /* NodeColor.Black */) {
return write(`${leadingTrivia}import ${node.importClause.name.text}, {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`);
}
return write(`${leadingTrivia}import {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`);
}
else {
if (node.importClause && node.importClause.name && getColor(node.importClause) === 2 /* Black */) {
if (node.importClause && node.importClause.name && getColor(node.importClause) === 2 /* NodeColor.Black */) {
return write(`${leadingTrivia}import ${node.importClause.name.text} from${node.moduleSpecifier.getFullText(sourceFile)};`);
}
}
}
}
else {
if (node.importClause && getColor(node.importClause) === 2 /* Black */) {
if (node.importClause && getColor(node.importClause) === 2 /* NodeColor.Black */) {
return keep(node);
}
}
@@ -655,7 +655,7 @@ function generateResult(ts, languageService, shakeLevel) {
if (node.exportClause && node.moduleSpecifier && ts.isNamedExports(node.exportClause)) {
const survivingExports = [];
for (const exportSpecifier of node.exportClause.elements) {
if (getColor(exportSpecifier) === 2 /* Black */) {
if (getColor(exportSpecifier) === 2 /* NodeColor.Black */) {
survivingExports.push(exportSpecifier.getFullText(sourceFile));
}
}
@@ -666,11 +666,11 @@ function generateResult(ts, languageService, shakeLevel) {
}
}
}
if (shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) {
if (shakeLevel === 2 /* ShakeLevel.ClassMembers */ && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) {
let toWrite = node.getFullText();
for (let i = node.members.length - 1; i >= 0; i--) {
const member = node.members[i];
if (getColor(member) === 2 /* Black */ || !member.name) {
if (getColor(member) === 2 /* NodeColor.Black */ || !member.name) {
// keep method
continue;
}
@@ -686,7 +686,7 @@ function generateResult(ts, languageService, shakeLevel) {
}
node.forEachChild(writeMarkedNodes);
}
if (getColor(sourceFile) !== 2 /* Black */) {
if (getColor(sourceFile) !== 2 /* NodeColor.Black */) {
if (!nodeOrChildIsBlack(sourceFile)) {
// none of the elements are reachable
if (isNeededSourceFile(sourceFile)) {

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTypeScriptBuilder = exports.CancellationToken = void 0;

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.create = void 0;

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transpiler = void 0;

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.graph = exports.strings = exports.collections = void 0;

View File

@@ -304,7 +304,6 @@ function getElectronVersion() {
}
exports.getElectronVersion = getElectronVersion;
function acquireWebNodePaths() {
var _a;
const root = path.join(__dirname, '..', '..');
const webPackageJSON = path.join(root, '/remote/web', 'package.json');
const webPackages = JSON.parse(fs.readFileSync(webPackageJSON, 'utf8')).dependencies;
@@ -312,7 +311,7 @@ function acquireWebNodePaths() {
for (const key of Object.keys(webPackages)) {
const packageJSON = path.join(root, 'node_modules', key, 'package.json');
const packageData = JSON.parse(fs.readFileSync(packageJSON, 'utf8'));
let entryPoint = typeof packageData.browser === 'string' ? packageData.browser : (_a = packageData.main) !== null && _a !== void 0 ? _a : packageData.main; // {{SQL CARBON EDIT}} Some packages (like Turndown) have objects in this field instead of the entry point, fall back to main in that case
let entryPoint = typeof packageData.browser === 'string' ? packageData.browser : packageData.main ?? packageData.main; // {{SQL CARBON EDIT}} Some packages (like Turndown) have objects in this field instead of the entry point, fall back to main in that case
// On rare cases a package doesn't have an entrypoint so we assume it has a dist folder with a min.js
if (!entryPoint) {
// TODO @lramos15 remove this when jschardet adds an entrypoint so we can warn on all packages w/out entrypoint

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.referenceGeneratedDepsByArch = exports.bundledDeps = exports.recommendedDeps = exports.additionalDeps = void 0;

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
@@ -9,7 +9,7 @@ const child_process_1 = require("child_process");
const fs_1 = require("fs");
const os_1 = require("os");
const path = require("path");
const dep_lists_1 = require("./dep-lists");
const dep_lists_1 = require("./dep-lists"); // {{SQL CARBON EDIT}} Not needed
// A flag that can easily be toggled.
// Make sure to compile the build directory after toggling the value.
// If false, we warn about new dependencies if they show up
@@ -17,7 +17,7 @@ const dep_lists_1 = require("./dep-lists");
// If true, we fail the build if there are new dependencies found during that task.
// The reference dependencies, which one has to update when the new dependencies
// are valid, are in dep-lists.ts
const FAIL_BUILD_FOR_NEW_DEPENDENCIES = true;
// const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; {{ SQL CARBON EDIT}} Not needed
function getDependencies(buildDir, applicationName, arch, sysroot) {
// Get the files for which we want to find dependencies.
const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked');
@@ -49,18 +49,19 @@ function getDependencies(buildDir, applicationName, arch, sysroot) {
sortedDependencies = sortedDependencies.filter(dependency => {
return !dep_lists_1.bundledDeps.some(bundledDep => dependency.startsWith(bundledDep));
});
const referenceGeneratedDeps = dep_lists_1.referenceGeneratedDepsByArch[arch];
/* {{SQL CARBON EDIT}} Not needed
const referenceGeneratedDeps = referenceGeneratedDepsByArch[arch];
if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) {
const failMessage = 'The dependencies list has changed.'
+ '\nOld:\n' + referenceGeneratedDeps.join('\n')
+ '\nNew:\n' + sortedDependencies.join('\n');
if (FAIL_BUILD_FOR_NEW_DEPENDENCIES) {
throw new Error(failMessage);
}
else {
} else {
console.warn(failMessage);
}
}
*/
return sortedDependencies;
}
exports.getDependencies = getDependencies;

View File

@@ -9,7 +9,7 @@ import { spawnSync } from 'child_process';
import { constants, statSync } from 'fs';
import { tmpdir } from 'os';
import path = require('path');
import { additionalDeps, bundledDeps, referenceGeneratedDepsByArch } from './dep-lists';
import { additionalDeps, bundledDeps/*, referenceGeneratedDepsByArch*/ } from './dep-lists'; // {{SQL CARBON EDIT}} Not needed
import { ArchString } from './types';
// A flag that can easily be toggled.
@@ -19,7 +19,7 @@ import { ArchString } from './types';
// If true, we fail the build if there are new dependencies found during that task.
// The reference dependencies, which one has to update when the new dependencies
// are valid, are in dep-lists.ts
const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true;
// const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; {{ SQL CARBON EDIT}} Not needed
export function getDependencies(buildDir: string, applicationName: string, arch: ArchString, sysroot: string): string[] {
// Get the files for which we want to find dependencies.
@@ -59,6 +59,7 @@ export function getDependencies(buildDir: string, applicationName: string, arch:
return !bundledDeps.some(bundledDep => dependency.startsWith(bundledDep));
});
/* {{SQL CARBON EDIT}} Not needed
const referenceGeneratedDeps = referenceGeneratedDepsByArch[arch];
if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) {
const failMessage = 'The dependencies list has changed.'
@@ -70,6 +71,7 @@ export function getDependencies(buildDir: string, applicationName: string, arch:
console.warn(failMessage);
}
}
*/
return sortedDependencies;
}

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSysroot = void 0;

View File

@@ -1,7 +1,7 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.sysrootInfo = void 0;

View File

@@ -1,6 +1,6 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -8,7 +8,7 @@ exports.getDependencies = void 0;
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const path = require("path");
const dep_lists_1 = require("./dep-lists");
const dep_lists_1 = require("./dep-lists"); // {{SQL CARBON EDIT}} Not needed
// A flag that can easily be toggled.
// Make sure to compile the build directory after toggling the value.
// If false, we warn about new dependencies if they show up
@@ -16,13 +16,13 @@ const dep_lists_1 = require("./dep-lists");
// If true, we fail the build if there are new dependencies found during that task.
// The reference dependencies, which one has to update when the new dependencies
// are valid, are in dep-lists.ts
const FAIL_BUILD_FOR_NEW_DEPENDENCIES = false;
// const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = false; // {{SQL CARBON EDIT}} Not needed
function getDependencies(buildDir, applicationName, arch) {
// Get the files for which we want to find dependencies.
const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked');
const findResult = (0, child_process_1.spawnSync)('find', [nativeModulesPath, '-name', '*.node']);
if (findResult.status) {
console.error('Error finding files:');
console.error(`Error finding files for ${arch}:`);
console.error(findResult.stderr.toString());
return [];
}
@@ -48,18 +48,19 @@ function getDependencies(buildDir, applicationName, arch) {
sortedDependencies = sortedDependencies.filter(dependency => {
return !dep_lists_1.bundledDeps.some(bundledDep => dependency.startsWith(bundledDep));
});
const referenceGeneratedDeps = dep_lists_1.referenceGeneratedDepsByArch[arch];
/* {{SQL CARBON EDIT}} Not needed
const referenceGeneratedDeps = referenceGeneratedDepsByArch[arch];
if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) {
const failMessage = 'The dependencies list has changed. '
+ 'Printing newer dependencies list that one can use to compare against referenceGeneratedDeps:\n'
+ sortedDependencies.join('\n');
if (FAIL_BUILD_FOR_NEW_DEPENDENCIES) {
throw new Error(failMessage);
}
else {
} else {
console.warn(failMessage);
}
}
*/
return sortedDependencies;
}
exports.getDependencies = getDependencies;

View File

@@ -6,7 +6,7 @@
import { spawnSync } from 'child_process';
import { constants, statSync } from 'fs';
import path = require('path');
import { additionalDeps, bundledDeps, referenceGeneratedDepsByArch } from './dep-lists';
import { additionalDeps, bundledDeps/*, referenceGeneratedDepsByArch*/ } from './dep-lists'; // {{SQL CARBON EDIT}} Not needed
import { ArchString } from './types';
// A flag that can easily be toggled.
@@ -16,14 +16,14 @@ import { ArchString } from './types';
// If true, we fail the build if there are new dependencies found during that task.
// The reference dependencies, which one has to update when the new dependencies
// are valid, are in dep-lists.ts
const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = false;
// const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = false; // {{SQL CARBON EDIT}} Not needed
export function getDependencies(buildDir: string, applicationName: string, arch: ArchString): string[] {
// Get the files for which we want to find dependencies.
const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked');
const findResult = spawnSync('find', [nativeModulesPath, '-name', '*.node']);
if (findResult.status) {
console.error('Error finding files:');
console.error(`Error finding files for ${arch}:`);
console.error(findResult.stderr.toString());
return [];
}
@@ -57,6 +57,7 @@ export function getDependencies(buildDir: string, applicationName: string, arch:
return !bundledDeps.some(bundledDep => dependency.startsWith(bundledDep));
});
/* {{SQL CARBON EDIT}} Not needed
const referenceGeneratedDeps = referenceGeneratedDepsByArch[arch];
if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) {
const failMessage = 'The dependencies list has changed. '
@@ -68,6 +69,7 @@ export function getDependencies(buildDir: string, applicationName: string, arch:
console.warn(failMessage);
}
}
*/
return sortedDependencies;
}

View File

@@ -1,634 +0,0 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.execute = exports.run3 = exports.DeclarationResolver = exports.FSProvider = exports.RECIPE_PATH = void 0;
const fs = require("fs");
const ts = require("typescript");
const path = require("path");
const fancyLog = require("fancy-log");
const ansiColors = require("ansi-colors");
const dtsv = '3';
const tsfmt = require('../../tsfmt.json');
const SRC = path.join(__dirname, '../../src');
exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe');
const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts');
function logErr(message, ...rest) {
fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest);
}
function isDeclaration(a) {
return (a.kind === ts.SyntaxKind.InterfaceDeclaration
|| a.kind === ts.SyntaxKind.EnumDeclaration
|| a.kind === ts.SyntaxKind.ClassDeclaration
|| a.kind === ts.SyntaxKind.TypeAliasDeclaration
|| a.kind === ts.SyntaxKind.FunctionDeclaration
|| a.kind === ts.SyntaxKind.ModuleDeclaration);
}
function visitTopLevelDeclarations(sourceFile, visitor) {
let stop = false;
let visit = (node) => {
if (stop) {
return;
}
switch (node.kind) {
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.VariableStatement:
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.ModuleDeclaration:
stop = visitor(node);
}
if (stop) {
return;
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
}
function getAllTopLevelDeclarations(sourceFile) {
let all = [];
visitTopLevelDeclarations(sourceFile, (node) => {
if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) {
let interfaceDeclaration = node;
let triviaStart = interfaceDeclaration.pos;
let triviaEnd = interfaceDeclaration.name.pos;
let triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd });
if (triviaText.indexOf('@internal') === -1) {
all.push(node);
}
}
else {
let nodeText = getNodeText(sourceFile, node);
if (nodeText.indexOf('@internal') === -1) {
all.push(node);
}
}
return false /*continue*/;
});
return all;
}
function getTopLevelDeclaration(sourceFile, typeName) {
let result = null;
visitTopLevelDeclarations(sourceFile, (node) => {
if (isDeclaration(node) && node.name) {
if (node.name.text === typeName) {
result = node;
return true /*stop*/;
}
return false /*continue*/;
}
// node is ts.VariableStatement
if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) {
result = node;
return true /*stop*/;
}
return false /*continue*/;
});
return result;
}
function getNodeText(sourceFile, node) {
return sourceFile.getFullText().substring(node.pos, node.end);
}
function hasModifier(modifiers, kind) {
if (modifiers) {
for (let i = 0; i < modifiers.length; i++) {
let mod = modifiers[i];
if (mod.kind === kind) {
return true;
}
}
}
return false;
}
function isStatic(member) {
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
}
function isDefaultExport(declaration) {
return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword)
&& hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword));
}
function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums) {
let result = getNodeText(sourceFile, declaration);
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
let interfaceDeclaration = declaration;
const staticTypeName = (isDefaultExport(interfaceDeclaration)
? `${importName}.default`
: `${importName}.${declaration.name.text}`);
let instanceTypeName = staticTypeName;
const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0);
if (typeParametersCnt > 0) {
let arr = [];
for (let i = 0; i < typeParametersCnt; i++) {
arr.push('any');
}
instanceTypeName = `${instanceTypeName}<${arr.join(',')}>`;
}
const members = interfaceDeclaration.members;
members.forEach((member) => {
try {
let memberText = getNodeText(sourceFile, member);
if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) {
result = result.replace(memberText, '');
}
else {
const memberName = member.name.text;
const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`);
if (isStatic(member)) {
usage.push(`a = ${staticTypeName}${memberAccess};`);
}
else {
usage.push(`a = (<${instanceTypeName}>b)${memberAccess};`);
}
}
}
catch (err) {
// life..
}
});
}
else if (declaration.kind === ts.SyntaxKind.VariableStatement) {
const jsDoc = result.substr(0, declaration.getLeadingTriviaWidth(sourceFile));
if (jsDoc.indexOf('@monacodtsreplace') >= 0) {
const jsDocLines = jsDoc.split(/\r\n|\r|\n/);
let directives = [];
for (const jsDocLine of jsDocLines) {
const m = jsDocLine.match(/^\s*\* \/([^/]+)\/([^/]+)\/$/);
if (m) {
directives.push([new RegExp(m[1], 'g'), m[2]]);
}
}
// remove the jsdoc
result = result.substr(jsDoc.length);
if (directives.length > 0) {
// apply replace directives
const replacer = createReplacerFromDirectives(directives);
result = replacer(result);
}
}
}
result = result.replace(/export default /g, 'export ');
result = result.replace(/export declare /g, 'export ');
result = result.replace(/declare /g, '');
let lines = result.split(/\r\n|\r|\n/);
for (let i = 0; i < lines.length; i++) {
if (/\s*\*/.test(lines[i])) {
// very likely a comment
continue;
}
lines[i] = lines[i].replace(/"/g, '\'');
}
result = lines.join('\n');
if (declaration.kind === ts.SyntaxKind.EnumDeclaration) {
result = result.replace(/const enum/, 'enum');
enums.push({
enumName: declaration.name.getText(sourceFile),
text: result
});
}
return result;
}
function format(text, endl) {
const REALLY_FORMAT = false;
text = preformat(text, endl);
if (!REALLY_FORMAT) {
return text;
}
// Parse the source text
let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true);
// Get the formatting edits on the input sources
let edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt);
// Apply the edits on the input code
return applyEdits(text, edits);
function countParensCurly(text) {
let cnt = 0;
for (let i = 0; i < text.length; i++) {
if (text.charAt(i) === '(' || text.charAt(i) === '{') {
cnt++;
}
if (text.charAt(i) === ')' || text.charAt(i) === '}') {
cnt--;
}
}
return cnt;
}
function repeatStr(s, cnt) {
let r = '';
for (let i = 0; i < cnt; i++) {
r += s;
}
return r;
}
function preformat(text, endl) {
let lines = text.split(endl);
let inComment = false;
let inCommentDeltaIndent = 0;
let indent = 0;
for (let i = 0; i < lines.length; i++) {
let line = lines[i].replace(/\s$/, '');
let repeat = false;
let lineIndent = 0;
do {
repeat = false;
if (line.substring(0, 4) === ' ') {
line = line.substring(4);
lineIndent++;
repeat = true;
}
if (line.charAt(0) === '\t') {
line = line.substring(1);
lineIndent++;
repeat = true;
}
} while (repeat);
if (line.length === 0) {
continue;
}
if (inComment) {
if (/\*\//.test(line)) {
inComment = false;
}
lines[i] = repeatStr('\t', lineIndent + inCommentDeltaIndent) + line;
continue;
}
if (/\/\*/.test(line)) {
inComment = true;
inCommentDeltaIndent = indent - lineIndent;
lines[i] = repeatStr('\t', indent) + line;
continue;
}
const cnt = countParensCurly(line);
let shouldUnindentAfter = false;
let shouldUnindentBefore = false;
if (cnt < 0) {
if (/[({]/.test(line)) {
shouldUnindentAfter = true;
}
else {
shouldUnindentBefore = true;
}
}
else if (cnt === 0) {
shouldUnindentBefore = /^\}/.test(line);
}
let shouldIndentAfter = false;
if (cnt > 0) {
shouldIndentAfter = true;
}
else if (cnt === 0) {
shouldIndentAfter = /{$/.test(line);
}
if (shouldUnindentBefore) {
indent--;
}
lines[i] = repeatStr('\t', indent) + line;
if (shouldUnindentAfter) {
indent--;
}
if (shouldIndentAfter) {
indent++;
}
}
return lines.join(endl);
}
function getRuleProvider(options) {
// Share this between multiple formatters using the same options.
// This represents the bulk of the space the formatter uses.
return ts.formatting.getFormatContext(options);
}
function applyEdits(text, edits) {
// Apply edits in reverse on the existing text
let result = text;
for (let i = edits.length - 1; i >= 0; i--) {
let change = edits[i];
let head = result.slice(0, change.span.start);
let tail = result.slice(change.span.start + change.span.length);
result = head + change.newText + tail;
}
return result;
}
}
function createReplacerFromDirectives(directives) {
return (str) => {
for (let i = 0; i < directives.length; i++) {
str = str.replace(directives[i][0], directives[i][1]);
}
return str;
};
}
function createReplacer(data) {
data = data || '';
let rawDirectives = data.split(';');
let directives = [];
rawDirectives.forEach((rawDirective) => {
if (rawDirective.length === 0) {
return;
}
let pieces = rawDirective.split('=>');
let findStr = pieces[0];
let replaceStr = pieces[1];
findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&');
findStr = '\\b' + findStr + '\\b';
directives.push([new RegExp(findStr, 'g'), replaceStr]);
});
return createReplacerFromDirectives(directives);
}
function generateDeclarationFile(recipe, sourceFileGetter) {
const endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
let lines = recipe.split(endl);
let result = [];
let usageCounter = 0;
let usageImports = [];
let usage = [];
let failed = false;
usage.push(`var a: any;`);
usage.push(`var b: any;`);
const generateUsageImport = (moduleId) => {
let importName = 'm' + (++usageCounter);
usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`);
return importName;
};
let enums = [];
let version = null;
lines.forEach(line => {
if (failed) {
return;
}
let m0 = line.match(/^\/\/dtsv=(\d+)$/);
if (m0) {
version = m0[1];
}
let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m1) {
let moduleId = m1[1];
const sourceFile = sourceFileGetter(moduleId);
if (!sourceFile) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${moduleId}`);
failed = true;
return;
}
const importName = generateUsageImport(moduleId);
let replacer = createReplacer(m1[2]);
let typeNames = m1[3].split(/,/);
typeNames.forEach((typeName) => {
typeName = typeName.trim();
if (typeName.length === 0) {
return;
}
let declaration = getTopLevelDeclaration(sourceFile, typeName);
if (!declaration) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${typeName}`);
failed = true;
return;
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
});
return;
}
let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m2) {
let moduleId = m2[1];
const sourceFile = sourceFileGetter(moduleId);
if (!sourceFile) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${moduleId}`);
failed = true;
return;
}
const importName = generateUsageImport(moduleId);
let replacer = createReplacer(m2[2]);
let typeNames = m2[3].split(/,/);
let typesToExcludeMap = {};
let typesToExcludeArr = [];
typeNames.forEach((typeName) => {
typeName = typeName.trim();
if (typeName.length === 0) {
return;
}
typesToExcludeMap[typeName] = true;
typesToExcludeArr.push(typeName);
});
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
if (isDeclaration(declaration) && declaration.name) {
if (typesToExcludeMap[declaration.name.text]) {
return;
}
}
else {
// node is ts.VariableStatement
let nodeText = getNodeText(sourceFile, declaration);
for (let i = 0; i < typesToExcludeArr.length; i++) {
if (nodeText.indexOf(typesToExcludeArr[i]) >= 0) {
return;
}
}
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
});
return;
}
result.push(line);
});
if (failed) {
return null;
}
if (version !== dtsv) {
if (!version) {
logErr(`gulp watch restart required. 'monaco.d.ts.recipe' is written before versioning was introduced.`);
}
else {
logErr(`gulp watch restart required. 'monaco.d.ts.recipe' v${version} does not match runtime v${dtsv}.`);
}
return null;
}
let resultTxt = result.join(endl);
resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri');
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
resultTxt = format(resultTxt, endl);
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
enums.sort((e1, e2) => {
if (e1.enumName < e2.enumName) {
return -1;
}
if (e1.enumName > e2.enumName) {
return 1;
}
return 0;
});
let resultEnums = [
'/*---------------------------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * Licensed under the Source EULA. See License.txt in the project root for license information.',
' *--------------------------------------------------------------------------------------------*/',
'',
'// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.',
''
].concat(enums.map(e => e.text)).join(endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
resultEnums = format(resultEnums, endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
return {
result: resultTxt,
usageContent: `${usageImports.join('\n')}\n\n${usage.join('\n')}`,
enums: resultEnums
};
}
function _run(sourceFileGetter) {
const recipe = fs.readFileSync(exports.RECIPE_PATH).toString();
const t = generateDeclarationFile(recipe, sourceFileGetter);
if (!t) {
return null;
}
const result = t.result;
const usageContent = t.usageContent;
const enums = t.enums;
const currentContent = fs.readFileSync(DECLARATION_PATH).toString();
const one = currentContent.replace(/\r\n/gm, '\n');
const other = result.replace(/\r\n/gm, '\n');
const isTheSame = (one === other);
return {
content: result,
usageContent: usageContent,
enums: enums,
filePath: DECLARATION_PATH,
isTheSame
};
}
class FSProvider {
existsSync(filePath) {
return fs.existsSync(filePath);
}
statSync(filePath) {
return fs.statSync(filePath);
}
readFileSync(_moduleId, filePath) {
return fs.readFileSync(filePath);
}
}
exports.FSProvider = FSProvider;
class CacheEntry {
constructor(sourceFile, mtime) {
this.sourceFile = sourceFile;
this.mtime = mtime;
}
}
class DeclarationResolver {
constructor(_fsProvider) {
this._fsProvider = _fsProvider;
this._sourceFileCache = Object.create(null);
}
invalidateCache(moduleId) {
this._sourceFileCache[moduleId] = null;
}
getDeclarationSourceFile(moduleId) {
if (this._sourceFileCache[moduleId]) {
// Since we cannot trust file watching to invalidate the cache, check also the mtime
const fileName = this._getFileName(moduleId);
const mtime = this._fsProvider.statSync(fileName).mtime.getTime();
if (this._sourceFileCache[moduleId].mtime !== mtime) {
this._sourceFileCache[moduleId] = null;
}
}
if (!this._sourceFileCache[moduleId]) {
this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId);
}
return this._sourceFileCache[moduleId] ? this._sourceFileCache[moduleId].sourceFile : null;
}
_getFileName(moduleId) {
if (/\.d\.ts$/.test(moduleId)) {
return path.join(SRC, moduleId);
}
return path.join(SRC, `${moduleId}.ts`);
}
_getDeclarationSourceFile(moduleId) {
const fileName = this._getFileName(moduleId);
if (!this._fsProvider.existsSync(fileName)) {
return null;
}
const mtime = this._fsProvider.statSync(fileName).mtime.getTime();
if (/\.d\.ts$/.test(moduleId)) {
// const mtime = this._fsProvider.statFileSync()
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
return new CacheEntry(ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), mtime);
}
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
const fileMap = {
'file.ts': fileContents
};
const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {}));
const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text;
return new CacheEntry(ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), mtime);
}
}
exports.DeclarationResolver = DeclarationResolver;
function run3(resolver) {
const sourceFileGetter = (moduleId) => resolver.getDeclarationSourceFile(moduleId);
return _run(sourceFileGetter);
}
exports.run3 = run3;
class TypeScriptLanguageServiceHost {
constructor(libs, files, compilerOptions) {
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
}
// {{SQL CARBON EDIT}} - provide missing methods
readFile() {
return undefined;
}
fileExists() {
return false;
}
// --- language service host ---------------
getCompilationSettings() {
return this._compilerOptions;
}
getScriptFileNames() {
return ([]
.concat(Object.keys(this._libs))
.concat(Object.keys(this._files)));
}
getScriptVersion(_fileName) {
return '1';
}
getProjectVersion() {
return '1';
}
getScriptSnapshot(fileName) {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
}
else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
}
else {
return ts.ScriptSnapshot.fromString('');
}
}
getScriptKind(_fileName) {
return ts.ScriptKind.TS;
}
getCurrentDirectory() {
return '';
}
getDefaultLibFileName(_options) {
return 'defaultLib:es5';
}
isDefaultLibFileName(fileName) {
return fileName === this.getDefaultLibFileName(this._compilerOptions);
}
}
function execute() {
let r = run3(new DeclarationResolver(new FSProvider()));
if (!r) {
throw new Error(`monaco.d.ts generation error - Cannot continue`);
}
return r;
}
exports.execute = execute;

View File

@@ -1,757 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as ts from 'typescript';
import * as path from 'path';
import * as fancyLog from 'fancy-log';
import * as ansiColors from 'ansi-colors';
const dtsv = '3';
const tsfmt = require('../../tsfmt.json');
const SRC = path.join(__dirname, '../../src');
export const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe');
const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts');
function logErr(message: any, ...rest: any[]): void {
fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest);
}
type SourceFileGetter = (moduleId: string) => ts.SourceFile | null;
type TSTopLevelDeclaration = ts.InterfaceDeclaration | ts.EnumDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ModuleDeclaration;
type TSTopLevelDeclare = TSTopLevelDeclaration | ts.VariableStatement;
function isDeclaration(a: TSTopLevelDeclare): a is TSTopLevelDeclaration {
return (
a.kind === ts.SyntaxKind.InterfaceDeclaration
|| a.kind === ts.SyntaxKind.EnumDeclaration
|| a.kind === ts.SyntaxKind.ClassDeclaration
|| a.kind === ts.SyntaxKind.TypeAliasDeclaration
|| a.kind === ts.SyntaxKind.FunctionDeclaration
|| a.kind === ts.SyntaxKind.ModuleDeclaration
);
}
function visitTopLevelDeclarations(sourceFile: ts.SourceFile, visitor: (node: TSTopLevelDeclare) => boolean): void {
let stop = false;
let visit = (node: ts.Node): void => {
if (stop) {
return;
}
switch (node.kind) {
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.VariableStatement:
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.ModuleDeclaration:
stop = visitor(<TSTopLevelDeclare>node);
}
if (stop) {
return;
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
}
function getAllTopLevelDeclarations(sourceFile: ts.SourceFile): TSTopLevelDeclare[] {
let all: TSTopLevelDeclare[] = [];
visitTopLevelDeclarations(sourceFile, (node) => {
if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) {
let interfaceDeclaration = <ts.InterfaceDeclaration>node;
let triviaStart = interfaceDeclaration.pos;
let triviaEnd = interfaceDeclaration.name.pos;
let triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd });
if (triviaText.indexOf('@internal') === -1) {
all.push(node);
}
} else {
let nodeText = getNodeText(sourceFile, node);
if (nodeText.indexOf('@internal') === -1) {
all.push(node);
}
}
return false /*continue*/;
});
return all;
}
function getTopLevelDeclaration(sourceFile: ts.SourceFile, typeName: string): TSTopLevelDeclare | null {
let result: TSTopLevelDeclare | null = null;
visitTopLevelDeclarations(sourceFile, (node) => {
if (isDeclaration(node) && node.name) {
if (node.name.text === typeName) {
result = node;
return true /*stop*/;
}
return false /*continue*/;
}
// node is ts.VariableStatement
if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) {
result = node;
return true /*stop*/;
}
return false /*continue*/;
});
return result;
}
function getNodeText(sourceFile: ts.SourceFile, node: { pos: number; end: number; }): string {
return sourceFile.getFullText().substring(node.pos, node.end);
}
function hasModifier(modifiers: ts.NodeArray<ts.Modifier> | undefined, kind: ts.SyntaxKind): boolean {
if (modifiers) {
for (let i = 0; i < modifiers.length; i++) {
let mod = modifiers[i];
if (mod.kind === kind) {
return true;
}
}
}
return false;
}
function isStatic(member: ts.ClassElement | ts.TypeElement): boolean {
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
}
function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {
return (
hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword)
&& hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword)
);
}
function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: IEnumEntry[]): string {
let result = getNodeText(sourceFile, declaration);
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
let interfaceDeclaration = <ts.InterfaceDeclaration | ts.ClassDeclaration>declaration;
const staticTypeName = (
isDefaultExport(interfaceDeclaration)
? `${importName}.default`
: `${importName}.${declaration.name!.text}`
);
let instanceTypeName = staticTypeName;
const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0);
if (typeParametersCnt > 0) {
let arr: string[] = [];
for (let i = 0; i < typeParametersCnt; i++) {
arr.push('any');
}
instanceTypeName = `${instanceTypeName}<${arr.join(',')}>`;
}
const members: ts.NodeArray<ts.ClassElement | ts.TypeElement> = interfaceDeclaration.members;
members.forEach((member) => {
try {
let memberText = getNodeText(sourceFile, member);
if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) {
result = result.replace(memberText, '');
} else {
const memberName = (<ts.Identifier | ts.StringLiteral>member.name).text;
const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`);
if (isStatic(member)) {
usage.push(`a = ${staticTypeName}${memberAccess};`);
} else {
usage.push(`a = (<${instanceTypeName}>b)${memberAccess};`);
}
}
} catch (err) {
// life..
}
});
} else if (declaration.kind === ts.SyntaxKind.VariableStatement) {
const jsDoc = result.substr(0, declaration.getLeadingTriviaWidth(sourceFile));
if (jsDoc.indexOf('@monacodtsreplace') >= 0) {
const jsDocLines = jsDoc.split(/\r\n|\r|\n/);
let directives: [RegExp, string][] = [];
for (const jsDocLine of jsDocLines) {
const m = jsDocLine.match(/^\s*\* \/([^/]+)\/([^/]+)\/$/);
if (m) {
directives.push([new RegExp(m[1], 'g'), m[2]]);
}
}
// remove the jsdoc
result = result.substr(jsDoc.length);
if (directives.length > 0) {
// apply replace directives
const replacer = createReplacerFromDirectives(directives);
result = replacer(result);
}
}
}
result = result.replace(/export default /g, 'export ');
result = result.replace(/export declare /g, 'export ');
result = result.replace(/declare /g, '');
let lines = result.split(/\r\n|\r|\n/);
for (let i = 0; i < lines.length; i++) {
if (/\s*\*/.test(lines[i])) {
// very likely a comment
continue;
}
lines[i] = lines[i].replace(/"/g, '\'');
}
result = lines.join('\n');
if (declaration.kind === ts.SyntaxKind.EnumDeclaration) {
result = result.replace(/const enum/, 'enum');
enums.push({
enumName: declaration.name.getText(sourceFile),
text: result
});
}
return result;
}
function format(text: string, endl: string): string {
const REALLY_FORMAT = false;
text = preformat(text, endl);
if (!REALLY_FORMAT) {
return text;
}
// Parse the source text
let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true);
// Get the formatting edits on the input sources
let edits = (<any>ts).formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt);
// Apply the edits on the input code
return applyEdits(text, edits);
function countParensCurly(text: string): number {
let cnt = 0;
for (let i = 0; i < text.length; i++) {
if (text.charAt(i) === '(' || text.charAt(i) === '{') {
cnt++;
}
if (text.charAt(i) === ')' || text.charAt(i) === '}') {
cnt--;
}
}
return cnt;
}
function repeatStr(s: string, cnt: number): string {
let r = '';
for (let i = 0; i < cnt; i++) {
r += s;
}
return r;
}
function preformat(text: string, endl: string): string {
let lines = text.split(endl);
let inComment = false;
let inCommentDeltaIndent = 0;
let indent = 0;
for (let i = 0; i < lines.length; i++) {
let line = lines[i].replace(/\s$/, '');
let repeat = false;
let lineIndent = 0;
do {
repeat = false;
if (line.substring(0, 4) === ' ') {
line = line.substring(4);
lineIndent++;
repeat = true;
}
if (line.charAt(0) === '\t') {
line = line.substring(1);
lineIndent++;
repeat = true;
}
} while (repeat);
if (line.length === 0) {
continue;
}
if (inComment) {
if (/\*\//.test(line)) {
inComment = false;
}
lines[i] = repeatStr('\t', lineIndent + inCommentDeltaIndent) + line;
continue;
}
if (/\/\*/.test(line)) {
inComment = true;
inCommentDeltaIndent = indent - lineIndent;
lines[i] = repeatStr('\t', indent) + line;
continue;
}
const cnt = countParensCurly(line);
let shouldUnindentAfter = false;
let shouldUnindentBefore = false;
if (cnt < 0) {
if (/[({]/.test(line)) {
shouldUnindentAfter = true;
} else {
shouldUnindentBefore = true;
}
} else if (cnt === 0) {
shouldUnindentBefore = /^\}/.test(line);
}
let shouldIndentAfter = false;
if (cnt > 0) {
shouldIndentAfter = true;
} else if (cnt === 0) {
shouldIndentAfter = /{$/.test(line);
}
if (shouldUnindentBefore) {
indent--;
}
lines[i] = repeatStr('\t', indent) + line;
if (shouldUnindentAfter) {
indent--;
}
if (shouldIndentAfter) {
indent++;
}
}
return lines.join(endl);
}
function getRuleProvider(options: ts.FormatCodeSettings) {
// Share this between multiple formatters using the same options.
// This represents the bulk of the space the formatter uses.
return (ts as any).formatting.getFormatContext(options);
}
function applyEdits(text: string, edits: ts.TextChange[]): string {
// Apply edits in reverse on the existing text
let result = text;
for (let i = edits.length - 1; i >= 0; i--) {
let change = edits[i];
let head = result.slice(0, change.span.start);
let tail = result.slice(change.span.start + change.span.length);
result = head + change.newText + tail;
}
return result;
}
}
function createReplacerFromDirectives(directives: [RegExp, string][]): (str: string) => string {
return (str: string) => {
for (let i = 0; i < directives.length; i++) {
str = str.replace(directives[i][0], directives[i][1]);
}
return str;
};
}
function createReplacer(data: string): (str: string) => string {
data = data || '';
let rawDirectives = data.split(';');
let directives: [RegExp, string][] = [];
rawDirectives.forEach((rawDirective) => {
if (rawDirective.length === 0) {
return;
}
let pieces = rawDirective.split('=>');
let findStr = pieces[0];
let replaceStr = pieces[1];
findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&');
findStr = '\\b' + findStr + '\\b';
directives.push([new RegExp(findStr, 'g'), replaceStr]);
});
return createReplacerFromDirectives(directives);
}
interface ITempResult {
result: string;
usageContent: string;
enums: string;
}
interface IEnumEntry {
enumName: string;
text: string;
}
function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null {
const endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
let lines = recipe.split(endl);
let result: string[] = [];
let usageCounter = 0;
let usageImports: string[] = [];
let usage: string[] = [];
let failed = false;
usage.push(`var a: any;`);
usage.push(`var b: any;`);
const generateUsageImport = (moduleId: string) => {
let importName = 'm' + (++usageCounter);
usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`);
return importName;
};
let enums: IEnumEntry[] = [];
let version: string | null = null;
lines.forEach(line => {
if (failed) {
return;
}
let m0 = line.match(/^\/\/dtsv=(\d+)$/);
if (m0) {
version = m0[1];
}
let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m1) {
let moduleId = m1[1];
const sourceFile = sourceFileGetter(moduleId);
if (!sourceFile) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${moduleId}`);
failed = true;
return;
}
const importName = generateUsageImport(moduleId);
let replacer = createReplacer(m1[2]);
let typeNames = m1[3].split(/,/);
typeNames.forEach((typeName) => {
typeName = typeName.trim();
if (typeName.length === 0) {
return;
}
let declaration = getTopLevelDeclaration(sourceFile, typeName);
if (!declaration) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${typeName}`);
failed = true;
return;
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
});
return;
}
let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m2) {
let moduleId = m2[1];
const sourceFile = sourceFileGetter(moduleId);
if (!sourceFile) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${moduleId}`);
failed = true;
return;
}
const importName = generateUsageImport(moduleId);
let replacer = createReplacer(m2[2]);
let typeNames = m2[3].split(/,/);
let typesToExcludeMap: { [typeName: string]: boolean; } = {};
let typesToExcludeArr: string[] = [];
typeNames.forEach((typeName) => {
typeName = typeName.trim();
if (typeName.length === 0) {
return;
}
typesToExcludeMap[typeName] = true;
typesToExcludeArr.push(typeName);
});
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
if (isDeclaration(declaration) && declaration.name) {
if (typesToExcludeMap[declaration.name.text]) {
return;
}
} else {
// node is ts.VariableStatement
let nodeText = getNodeText(sourceFile, declaration);
for (let i = 0; i < typesToExcludeArr.length; i++) {
if (nodeText.indexOf(typesToExcludeArr[i]) >= 0) {
return;
}
}
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
});
return;
}
result.push(line);
});
if (failed) {
return null;
}
if (version !== dtsv) {
if (!version) {
logErr(`gulp watch restart required. 'monaco.d.ts.recipe' is written before versioning was introduced.`);
} else {
logErr(`gulp watch restart required. 'monaco.d.ts.recipe' v${version} does not match runtime v${dtsv}.`);
}
return null;
}
let resultTxt = result.join(endl);
resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri');
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
resultTxt = format(resultTxt, endl);
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
enums.sort((e1, e2) => {
if (e1.enumName < e2.enumName) {
return -1;
}
if (e1.enumName > e2.enumName) {
return 1;
}
return 0;
});
let resultEnums = [
'/*---------------------------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * Licensed under the Source EULA. See License.txt in the project root for license information.',
' *--------------------------------------------------------------------------------------------*/',
'',
'// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.',
''
].concat(enums.map(e => e.text)).join(endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
resultEnums = format(resultEnums, endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
return {
result: resultTxt,
usageContent: `${usageImports.join('\n')}\n\n${usage.join('\n')}`,
enums: resultEnums
};
}
export interface IMonacoDeclarationResult {
content: string;
usageContent: string;
enums: string;
filePath: string;
isTheSame: boolean;
}
function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult | null {
const recipe = fs.readFileSync(RECIPE_PATH).toString();
const t = generateDeclarationFile(recipe, sourceFileGetter);
if (!t) {
return null;
}
const result = t.result;
const usageContent = t.usageContent;
const enums = t.enums;
const currentContent = fs.readFileSync(DECLARATION_PATH).toString();
const one = currentContent.replace(/\r\n/gm, '\n');
const other = result.replace(/\r\n/gm, '\n');
const isTheSame = (one === other);
return {
content: result,
usageContent: usageContent,
enums: enums,
filePath: DECLARATION_PATH,
isTheSame
};
}
export class FSProvider {
public existsSync(filePath: string): boolean {
return fs.existsSync(filePath);
}
public statSync(filePath: string): fs.Stats {
return fs.statSync(filePath);
}
public readFileSync(_moduleId: string, filePath: string): Buffer {
return fs.readFileSync(filePath);
}
}
class CacheEntry {
constructor(
public readonly sourceFile: ts.SourceFile,
public readonly mtime: number
) {}
}
export class DeclarationResolver {
private _sourceFileCache: { [moduleId: string]: CacheEntry | null; };
constructor(private readonly _fsProvider: FSProvider) {
this._sourceFileCache = Object.create(null);
}
public invalidateCache(moduleId: string): void {
this._sourceFileCache[moduleId] = null;
}
public getDeclarationSourceFile(moduleId: string): ts.SourceFile | null {
if (this._sourceFileCache[moduleId]) {
// Since we cannot trust file watching to invalidate the cache, check also the mtime
const fileName = this._getFileName(moduleId);
const mtime = this._fsProvider.statSync(fileName).mtime.getTime();
if (this._sourceFileCache[moduleId]!.mtime !== mtime) {
this._sourceFileCache[moduleId] = null;
}
}
if (!this._sourceFileCache[moduleId]) {
this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId);
}
return this._sourceFileCache[moduleId] ? this._sourceFileCache[moduleId]!.sourceFile : null;
}
private _getFileName(moduleId: string): string {
if (/\.d\.ts$/.test(moduleId)) {
return path.join(SRC, moduleId);
}
return path.join(SRC, `${moduleId}.ts`);
}
private _getDeclarationSourceFile(moduleId: string): CacheEntry | null {
const fileName = this._getFileName(moduleId);
if (!this._fsProvider.existsSync(fileName)) {
return null;
}
const mtime = this._fsProvider.statSync(fileName).mtime.getTime();
if (/\.d\.ts$/.test(moduleId)) {
// const mtime = this._fsProvider.statFileSync()
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
return new CacheEntry(
ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5),
mtime
);
}
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
const fileMap: IFileMap = {
'file.ts': fileContents
};
const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {}));
const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text;
return new CacheEntry(
ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5),
mtime
);
}
}
export function run3(resolver: DeclarationResolver): IMonacoDeclarationResult | null {
const sourceFileGetter = (moduleId: string) => resolver.getDeclarationSourceFile(moduleId);
return _run(sourceFileGetter);
}
interface ILibMap { [libName: string]: string; }
interface IFileMap { [fileName: string]: string; }
class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
private readonly _libs: ILibMap;
private readonly _files: IFileMap;
private readonly _compilerOptions: ts.CompilerOptions;
constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
}
// {{SQL CARBON EDIT}} - provide missing methods
readFile(): string | undefined {
return undefined;
}
fileExists(): boolean {
return false;
}
// --- language service host ---------------
getCompilationSettings(): ts.CompilerOptions {
return this._compilerOptions;
}
getScriptFileNames(): string[] {
return (
([] as string[])
.concat(Object.keys(this._libs))
.concat(Object.keys(this._files))
);
}
getScriptVersion(_fileName: string): string {
return '1';
}
getProjectVersion(): string {
return '1';
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
} else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
} else {
return ts.ScriptSnapshot.fromString('');
}
}
getScriptKind(_fileName: string): ts.ScriptKind {
return ts.ScriptKind.TS;
}
getCurrentDirectory(): string {
return '';
}
getDefaultLibFileName(_options: ts.CompilerOptions): string {
return 'defaultLib:es5';
}
isDefaultLibFileName(fileName: string): boolean {
return fileName === this.getDefaultLibFileName(this._compilerOptions);
}
}
export function execute(): IMonacoDeclarationResult {
let r = run3(new DeclarationResolver(new FSProvider()));
if (!r) {
throw new Error(`monaco.d.ts generation error - Cannot continue`);
}
return r;
}

View File

@@ -3,10 +3,11 @@
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"@actions/core": "1.2.6",
"@actions/github": "2.1.1",
"@azure/cosmos": "^3.14.1",
"@azure/identity": "^2.1.0",
"@azure/identity": "^3.1.4",
"@azure/storage-blob": "^12.13.0",
"@vscode/vsce": "2.16.0",
"@electron/get": "^1.12.4",
"@types/ansi-colors": "^3.2.0",
"@types/azure": "0.9.19",
@@ -17,12 +18,12 @@
"@types/documentdb": "^1.10.5",
"@types/eslint": "4.16.1",
"@types/eslint-visitor-keys": "^1.0.0",
"@types/fancy-log": "^1.3.1",
"@types/fancy-log": "^1.3.0",
"@types/fs-extra": "^9.0.12",
"@types/glob": "^7.1.1",
"@types/gulp": "^4.0.10",
"@types/gulp": "^4.0.5",
"@types/gulp-concat": "^0.0.32",
"@types/gulp-filter": "^3.0.35",
"@types/gulp-filter": "^3.0.32",
"@types/gulp-gzip": "^0.0.31",
"@types/gulp-json-editor": "^2.2.31",
"@types/gulp-postcss": "^8.0.0",
@@ -40,27 +41,29 @@
"@types/request": "^2.47.0",
"@types/rimraf": "^2.0.4",
"@types/through": "^0.0.29",
"@types/through2": "^2.0.34",
"@types/through2": "^2.0.36",
"@types/tmp": "^0.2.1",
"@types/underscore": "^1.8.9",
"@types/webpack": "^4.41.25",
"@types/xml2js": "0.4.11",
"@typescript-eslint/experimental-utils": "~2.13.0",
"@typescript-eslint/experimental-utils": "~5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/vsce": "2.16.0",
"applicationinsights": "1.0.8",
"axios": "0.21.4",
"byline": "^5.0.0",
"colors": "^1.4.0",
"commander": "^7.0.0",
"debug": "^4.3.2",
"documentdb": "1.13.0",
"electron-osx-sign": "^0.4.16",
"esbuild": "^0.12.6",
"esbuild": "^0.14.2",
"extract-zip": "^2.0.1",
"fs-extra": "^9.1.0",
"got": "11.8.5",
"gulp-merge-json": "^2.1.1",
"iconv-lite-umd": "0.6.8",
"gulp-shell": "^0.8.0",
"jsonc-parser": "^2.3.0",
"mime": "^1.4.1",
"mkdirp": "^1.0.4",
@@ -71,23 +74,18 @@
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"source-map": "0.6.1",
"through2": "^4.0.2",
"tmp": "^0.2.1",
"typescript": "^4.8.0-dev.20220518",
"vsce": "2.8.0",
"vscode-universal-bundler": "^0.0.2"
},
"scripts": {
"compile": "tsc -p tsconfig.build.json",
"watch": "tsc -p tsconfig.build.json --watch",
"npmCheckJs": "tsc --noEmit"
"compile": "../node_modules/.bin/tsc -p tsconfig.build.json",
"watch": "../node_modules/.bin/tsc -p tsconfig.build.json --watch",
"npmCheckJs": "../node_modules/.bin/tsc --noEmit"
},
"optionalDependencies": {
"tree-sitter": "https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz",
"tree-sitter-typescript": "^0.20.1",
"vscode-gulp-watch": "^5.0.3"
},
"resolutions": {
"json-schema": "0.4.0",
"jsonwebtoken": "9.0.0"
}
}

View File

@@ -2,6 +2,10 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"allowJs": false,
"checkJs": false
}
}
"checkJs": false,
"noEmit": false
},
"include": [
"**/*.ts"
]
}

View File

@@ -1,6 +1,7 @@
{
"compilerOptions": {
"target": "es2017",
"target": "es2020",
"lib": ["ES2020"],
"module": "commonjs",
"alwaysStrict": true,
"removeComments": false,
@@ -12,19 +13,19 @@
// use the tsconfig.build.json for compiling which disable JavaScript
// type checking so that JavaScript file are not transpiled
"allowJs": true,
"checkJs": true,
"strict": true,
"exactOptionalPropertyTypes": false,
"useUnknownInCatchVariables": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"newLine": "lf"
"newLine": "lf",
"noEmit": true
},
"include": [
"**/*.ts"
"**/*.ts",
"**/*.js"
],
"exclude": [
"node_modules/**",
"actions/**" // {{SQL CARBON EDIT}}
"node_modules/**"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -199,7 +199,8 @@ export function createViewContext(): ViewTestContext {
data: [] as any[][],
columns: [] as string[],
onRowSelected: onClick.event,
appendData: (data: any[][]) => undefined,
appendData: (_data: any[][]) => undefined,
setActiveCell: (_row: number, _column: number) => undefined
});
let loadingComponent: () => azdata.LoadingComponent = () => Object.assign({}, componentBase, {

View File

@@ -301,6 +301,9 @@ export class MockTableComponent extends MockUIComponent implements azdata.TableC
appendData(data: any[][]): Thenable<void> {
throw new Error('Method not implemented.');
}
setActiveCell(row: number, column: number): void {
throw new Error('Method not implemented.');
}
}
export class MockDeclarativeTableComponent extends MockUIComponent implements azdata.DeclarativeTableComponent {

View File

@@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
const mssqlExtensionConfigName = 'mssql';
const enableSqlAuthenticationProviderConfig = 'enableSqlAuthenticationProvider';
const azureExtensionConfigName = 'azure';
const azureAuthenticationLibraryConfig = 'authenticationLibrary';
const MSAL = 'MSAL';
/**
* @returns 'True' if MSAL auth library is in use and SQL Auth provider is enabled.
*/
export function isMssqlAuthProviderEnabled(): boolean {
return getAzureAuthenticationLibraryConfig() === MSAL && getEnableSqlAuthenticationProviderConfig();
}
export function getConfiguration(config: string): vscode.WorkspaceConfiguration {
return vscode.workspace.getConfiguration(config);
}
/**
* Reads setting 'azure.AuthenticationLibrary' and returns the library name enabled.
* @returns MSAL | ADAL
*/
export function getAzureAuthenticationLibraryConfig(): string {
const config = getConfiguration(azureExtensionConfigName);
if (config) {
return config.get<string>(azureAuthenticationLibraryConfig, MSAL); // default Auth library
}
else {
return MSAL; // default Auth library
}
}
/**
* Reads setting 'mssql.enableSqlAuthenticationProvider' and returns true if it's enabled.
* @returns True Sql Auth provider is enabled for MSSQL provider.
*/
export function getEnableSqlAuthenticationProviderConfig(): boolean {
const config = getConfiguration(mssqlExtensionConfigName);
if (config) {
return config.get<boolean>(enableSqlAuthenticationProviderConfig, true); // enabled by default
}
else {
return true; // enabled by default
}
}

View File

@@ -9,6 +9,7 @@ import { ImportPage } from '../api/importPage';
import { InsertDataResponse } from '../../services/contracts';
import * as constants from '../../common/constants';
import { EOL } from 'os';
import { isMssqlAuthProviderEnabled } from '../../common/utils';
export class SummaryPage extends ImportPage {
private _table: azdata.TableComponent;
@@ -137,10 +138,14 @@ export class SummaryPage extends ImportPage {
const currentServer = this.model.server;
const includePasswordInConnectionString = (currentServer.options.authenticationType === azdata.connection.AuthenticationType.Integrated) ? false : true;
const connectionString = await azdata.connection.getConnectionString(currentServer.connectionId, includePasswordInConnectionString);
let connectionString = await azdata.connection.getConnectionString(currentServer.connectionId, includePasswordInConnectionString);
let accessToken = undefined;
if (currentServer.options.authenticationType === azdata.connection.AuthenticationType.AzureMFA) {
// Remove authentication properties from connection string if SQL Authentication Provider is enabled
if (isMssqlAuthProviderEnabled()) {
connectionString = this.updateConnectionStringForAccessToken(connectionString);
}
const azureAccount = (await azdata.accounts.getAllAccounts()).filter(v => v.key.accountId === currentServer.options.azureAccount)[0];
accessToken = (await azdata.accounts.getAccountSecurityToken(azureAccount, currentServer.options.azureTenantId, azdata.AzureResource.Sql)).token;
}
@@ -178,6 +183,21 @@ export class SummaryPage extends ImportPage {
});
}
/**
* Removes authentication related properties from connection string as SQL Tools Service now supports
* 'EnableSqlAuthenticationProvider' which sets the below properties on connection string that conflict with access token:
* 1. User Id
* 2. Authentication
* Since we need to set access token, we cannot use the same connection string as is.
* @param connString Connection string to fix
* @returns Updated connection string
*/
private updateConnectionStringForAccessToken(connString: string): string {
return connString ? connString.split(';').filter(prop =>
!['user', 'uid', 'password', 'pwd', 'authentication'].some(prefix => prop.toLocaleLowerCase().startsWith(prefix))
).join(';') : connString;
}
// private async getCountRowsInserted(): Promise<Number> {
// let connectionUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
// let queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(this.model.server.providerName, azdata.DataProviderType.QueryProvider);

View File

@@ -16,7 +16,8 @@
"Programming Languages"
],
"enabledApiProposals": [
"documentPaste"
"documentPaste",
"notebookEditor"
],
"activationEvents": [
"onLanguage:markdown",

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "4.7.0.29",
"version": "4.7.1.4",
"downloadFileNames": {
"Windows_86": "win-x86-net7.0.zip",
"Windows_64": "win-x64-net7.0.zip",

View File

@@ -515,7 +515,7 @@ export interface DeployParams {
packageFilePath: string;
databaseName: string;
upgradeExisting: boolean;
sqlCommandVariableValues?: Map<string, string>;
sqlCommandVariableValues?: Record<string, string>;
deploymentOptions?: mssql.DeploymentOptions;
ownerUri: string;
taskExecutionMode: TaskExecutionMode;
@@ -524,7 +524,7 @@ export interface DeployParams {
export interface GenerateDeployScriptParams {
packageFilePath: string;
databaseName: string;
sqlCommandVariableValues?: Map<string, string>;
sqlCommandVariableValues?: Record<string, string>;
deploymentOptions?: mssql.DeploymentOptions
ownerUri: string;
taskExecutionMode: TaskExecutionMode;
@@ -555,7 +555,7 @@ export interface SavePublishProfileParams {
profilePath: string;
databaseName: string;
connectionString: string;
sqlCommandVariableValues?: Map<string, string>;
sqlCommandVariableValues?: Record<string, string>;
deploymentOptions?: mssql.DeploymentOptions;
}

View File

@@ -55,12 +55,12 @@ export class DacFxService extends BaseService implements mssql.IDacFxService {
}
public async deployDacpac(packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map<string, string>, deploymentOptions?: mssql.DeploymentOptions): Promise<mssql.DacFxResult> {
const params: contracts.DeployParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, upgradeExisting: upgradeExisting, sqlCommandVariableValues: sqlCommandVariableValues, deploymentOptions: deploymentOptions, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
const params: contracts.DeployParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, upgradeExisting: upgradeExisting, sqlCommandVariableValues: sqlCommandVariableValues ? Object.fromEntries(sqlCommandVariableValues) : undefined, deploymentOptions: deploymentOptions, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
return this.runWithErrorHandling(contracts.DeployRequest.type, params);
}
public async generateDeployScript(packageFilePath: string, targetDatabaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map<string, string>, deploymentOptions?: mssql.DeploymentOptions): Promise<mssql.DacFxResult> {
const params: contracts.GenerateDeployScriptParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, sqlCommandVariableValues: sqlCommandVariableValues, deploymentOptions: deploymentOptions, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
const params: contracts.GenerateDeployScriptParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, sqlCommandVariableValues: sqlCommandVariableValues ? Object.fromEntries(sqlCommandVariableValues) : undefined, deploymentOptions: deploymentOptions, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
return this.runWithErrorHandling(contracts.GenerateDeployScriptRequest.type, params);
}
@@ -85,7 +85,7 @@ export class DacFxService extends BaseService implements mssql.IDacFxService {
}
public async savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map<string, string>, deploymentOptions?: mssql.DeploymentOptions): Promise<azdata.ResultStatus> {
const params: contracts.SavePublishProfileParams = { profilePath, databaseName, connectionString, sqlCommandVariableValues, deploymentOptions };
const params: contracts.SavePublishProfileParams = { profilePath, databaseName, connectionString, sqlCommandVariableValues: sqlCommandVariableValues ? Object.fromEntries(sqlCommandVariableValues) : undefined, deploymentOptions };
return this.runWithErrorHandling(contracts.SavePublishProfileRequest.type, params);
}
}

View File

@@ -917,7 +917,14 @@ declare module 'mssql' {
}
/**
* Base interface for the object view information
* Base interface for all the security principal objects. e.g. Login, Server Role, Database Role...
*/
export interface SecurityPrincipalObject extends SqlObject {
securablePermissions: SecurablePermissions[];
}
/**
* Base interface for the object view information.
*/
export interface ObjectViewInfo<T extends SqlObject> {
/**
@@ -926,10 +933,52 @@ declare module 'mssql' {
objectInfo: T;
}
/**
* Securable type metadata.
*/
export interface SecurableTypeMetadata {
/**
* Name of the securable type.
*/
name: string;
/**
* Display name of the securable type.
*/
displayName: string;
/**
* Permissions supported by the securable type.
*/
permissions: PermissionMetadata[];
}
/**
* Permission metadata.
*/
export interface PermissionMetadata {
/**
* Name of the permission.
*/
name: string;
/**
* Display name of the permission.
*/
displayName: string;
}
/**
* Base interface for security principal object's view information.
*/
export interface SecurityPrincipalViewInfo<T extends SecurityPrincipalObject> extends ObjectViewInfo<T> {
/**
* The securable types that the security principal object can be granted permissions on.
*/
supportedSecurableTypes: SecurableTypeMetadata[];
}
/**
* Server level login.
*/
export interface Login extends SqlObject {
export interface Login extends SecurityPrincipalObject {
/**
* Authentication type.
*/
@@ -1025,7 +1074,7 @@ declare module 'mssql' {
/**
* The information required to render the login view.
*/
export interface LoginViewInfo extends ObjectViewInfo<Login> {
export interface LoginViewInfo extends SecurityPrincipalViewInfo<Login> {
/**
* The authentication types supported by the server.
*/
@@ -1062,20 +1111,24 @@ declare module 'mssql' {
/**
* The permission information a principal has on a securable.
*/
export interface Permission {
export interface SecurablePermissionItem {
/**
* Name of the permission.
* name of the permission.
*/
name: string;
permission: string;
/**
* Whether the permission is granted or denied.
* Name of the grantor.
*/
grant: boolean;
grantor: string;
/**
* Whether the permission is granted or denied. Undefined means not specified.
*/
grant?: boolean;
/**
* Whether the pincipal can grant this permission to other principals.
* The value will be ignored if the grant property is set to false.
*/
withGrant: boolean;
withGrant?: boolean;
}
/**
@@ -1083,13 +1136,25 @@ declare module 'mssql' {
*/
export interface SecurablePermissions {
/**
* The securable.
* The securable name.
*/
securable: SqlObject;
name: string;
/**
* The Permissions.
* The securable type.
*/
permissions: Permission[];
type: string;
/**
* The schema name of the object if applicable.
*/
schema?: string;
/**
* The permissions.
*/
permissions: SecurablePermissionItem[];
/**
* The effective permissions. Includes all permissions granted to the principal, including those granted through role memberships.
*/
effectivePermissions: string[];
}
/**
@@ -1135,7 +1200,7 @@ declare module 'mssql' {
/**
* Database user.
*/
export interface User extends SqlObject {
export interface User extends SecurityPrincipalObject {
/**
* Type of the user.
*/
@@ -1172,7 +1237,7 @@ declare module 'mssql' {
/**
* The information required to render the user view.
*/
export interface UserViewInfo extends ObjectViewInfo<User> {
export interface UserViewInfo extends SecurityPrincipalViewInfo<User> {
/**
* All user types supported by the database.
*/
@@ -1198,7 +1263,7 @@ declare module 'mssql' {
/**
* Interface representing the server role object.
*/
export interface ServerRoleInfo extends SqlObject {
export interface ServerRoleInfo extends SecurityPrincipalObject {
/**
* Name of the server principal that owns the server role.
*/
@@ -1216,7 +1281,7 @@ declare module 'mssql' {
/**
* Interface representing the information required to render the server role view.
*/
export interface ServerRoleViewInfo extends ObjectViewInfo<ServerRoleInfo> {
export interface ServerRoleViewInfo extends SecurityPrincipalViewInfo<ServerRoleInfo> {
/**
* Whether the server role is a fixed role.
*/
@@ -1230,7 +1295,7 @@ declare module 'mssql' {
/**
* Interface representing the application role object.
*/
export interface ApplicationRoleInfo extends SqlObject {
export interface ApplicationRoleInfo extends SecurityPrincipalObject {
/**
* Default schema of the application role.
*/
@@ -1248,7 +1313,7 @@ declare module 'mssql' {
/**
* Interface representing the information required to render the application role view.
*/
export interface ApplicationRoleViewInfo extends ObjectViewInfo<ApplicationRoleInfo> {
export interface ApplicationRoleViewInfo extends SecurityPrincipalViewInfo<ApplicationRoleInfo> {
/**
* List of all the schemas in the database.
*/
@@ -1258,7 +1323,7 @@ declare module 'mssql' {
/**
* Interface representing the database role object.
*/
export interface DatabaseRoleInfo extends SqlObject {
export interface DatabaseRoleInfo extends SecurityPrincipalObject {
/**
* Name of the database principal that owns the database role.
*/
@@ -1276,7 +1341,7 @@ declare module 'mssql' {
/**
* Interface representing the information required to render the database role view.
*/
export interface DatabaseRoleViewInfo extends ObjectViewInfo<DatabaseRoleInfo> {
export interface DatabaseRoleViewInfo extends SecurityPrincipalViewInfo<DatabaseRoleInfo> {
/**
* List of all the schemas in the database.
*/
@@ -1294,7 +1359,7 @@ declare module 'mssql' {
/**
* type of the object.
*/
type: NodeType;
type: string;
/**
* schema of the object.
*/
@@ -1369,7 +1434,7 @@ declare module 'mssql' {
* @param searchText Search text.
* @param schema Schema to search in.
*/
search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText?: string, schema?: string): Thenable<ObjectManagement.SearchResultItem[]>;
search(contextId: string, objectTypes: string[], searchText?: string, schema?: string): Thenable<ObjectManagement.SearchResultItem[]>;
}
// Object Management - End.
}

View File

@@ -30,9 +30,22 @@ export const RenameObjectDialogTitle: string = localize('objectManagement.rename
export const OwnerText: string = localize('objectManagement.ownerText', "Owner");
export const BrowseText = localize('objectManagement.browseText', "Browse…");
export const BrowseOwnerButtonAriaLabel = localize('objectManagement.browseForOwnerText', "Browse for an owner");
export const AddMemberAriaLabel = localize('objectManagement.addMemberText', "Add a member");
export const AddMemberAriaLabel = localize('objectManagement.addMembersText', "Add members");
export const RemoveMemberAriaLabel = localize('objectManagement.removeMemberText', "Remove selected member");
export const AddSecurableAriaLabel = localize('objectManagement.addSecurablesText', "Add securables");
export const RemoveSecurableAriaLabel = localize('objectManagement.removeSecurablesText', "Remove selected securable");
export const SecurablesText = localize('objectManagement.securablesText', "Securables");
export const ExplicitPermissionsTableLabel = localize('objectManagement.explicitPermissionsTableLabel', "Explicit permissions for selected securable");
export const EffectivePermissionsTableLabel = localize('objectManagement.effectivePermissionsTableLabel', "Effective permissions for selected securable");
export const PermissionColumnHeader = localize('objectManagement.permissionColumnHeader', "Permission");
export const GrantorColumnHeader = localize('objectManagement.grantorColumnHeader', "Grantor");
export const GrantColumnHeader = localize('objectManagement.grantColumnHeader', "Grant");
export const WithGrantColumnHeader = localize('objectManagement.withGrantColumnHeader', "With Grant");
export const DenyColumnHeader = localize('objectManagement.denyColumnHeader', "Deny");
export const SelectSecurablesDialogTitle = localize('objectManagement.selectSecurablesDialogTitle', "Select Securables");
export function ExplicitPermissionsTableLabelSelected(name: string): string { return localize('objectManagement.explicitPermissionsTableLabelSelected', "Explicit permissions for: {0}", name); }
export function EffectivePermissionsTableLabelSelected(name: string): string { return localize('objectManagement.effectivePermissionsTableLabelSelected', "Effective permissions for: {0}", name); }
export function RefreshObjectExplorerError(error: string): string {
return localize({
@@ -133,12 +146,15 @@ export const LoginNotSelectedError = localize('objectManagement.loginNotSelected
export const MembershipSectionHeader = localize('objectManagement.membershipLabel', "Membership");
export const MemberSectionHeader = localize('objectManagement.membersLabel', "Members");
export const SchemaText = localize('objectManagement.schemaLabel', "Schema");
// Database
export const DatabaseExistsError = (dbName: string) => localize('objectManagement.databaseExistsError', "Database '{0}' already exists. Choose a different database name.", dbName);
export const CollationText = localize('objectManagement.collationLabel', "Collation");
export const RecoveryModelText = localize('objectManagement.recoveryModelLabel', "Recovery Model");
export const CompatibilityLevelText = localize('objectManagement.compatibilityLevelLabel', "Compatibility Level");
export const ContainmentTypeText = localize('objectManagement.containmentTypeLabel', "Containment Type");
// Login
export const BlankPasswordConfirmationText: string = localize('objectManagement.blankPasswordConfirmation', "Creating a login with a blank password is a security risk. Are you sure you want to continue?");
export const DeleteLoginConfirmationText: string = localize('objectManagement.deleteLoginConfirmation', "Deleting server logins does not delete the database users associated with the logins. To complete the process, delete the users in each database. It may be necessary to first transfer the ownership of schemas to new users.");

View File

@@ -67,6 +67,126 @@ export class ObjectManagementService extends BaseService implements IObjectManag
}
}
const ServerLevelSecurableTypes: ObjectManagement.SecurableTypeMetadata[] = [
{
name: 'Server',
displayName: 'Server',
permissions: [{
name: 'CONNECT SQL',
displayName: 'CONNECT SQL'
}, {
name: 'VIEW ANY DATABASE',
displayName: 'VIEW ANY DATABASE'
}]
}, {
name: 'ServerRole',
displayName: 'Server Role',
permissions: [{
name: 'ALTER',
displayName: 'ALTER'
}, {
name: 'CONTROL',
displayName: 'CONTROL'
}, {
name: 'TAKE OWNERSHIP',
displayName: 'TAKE OWNERSHIP'
}]
}
];
const DatabaseLevelSecurableTypes: ObjectManagement.SecurableTypeMetadata[] = [
{
name: 'AggregateFunction',
displayName: 'Aggregate Function',
permissions: [{
name: 'EXECUTE',
displayName: 'EXECUTE'
}, {
name: 'ALTER',
displayName: 'ALTER'
}]
}, {
name: 'Table',
displayName: 'Table',
permissions: [{
name: 'SELECT',
displayName: 'SELECT'
}, {
name: 'ALTER',
displayName: 'ALTER'
}, {
name: 'CONTROL',
displayName: 'CONTROL'
}, {
name: 'TAKE OWNERSHIP',
displayName: 'TAKE OWNERSHIP'
}]
}, {
name: 'View',
displayName: 'View',
permissions: [{
name: 'ALTER',
displayName: 'ALTER'
}, {
name: 'CONTROL',
displayName: 'CONTROL'
}, {
name: 'TAKE OWNERSHIP',
displayName: 'TAKE OWNERSHIP'
}]
}
]
const ServerLevelPermissions: ObjectManagement.SecurablePermissions[] = [
{
name: 'Server',
type: 'Server',
permissions: [
{
permission: 'CONNECT SQL',
grant: true,
grantor: 'sa',
withGrant: undefined
}, {
permission: 'VIEW ANY DATABASE',
grant: false,
grantor: 'sa',
withGrant: undefined
}
],
effectivePermissions: ['CONNECT SQL', 'VIEW ANY DATABASE']
}
];
const DatabaseLevelPermissions: ObjectManagement.SecurablePermissions[] = [
{
name: 'table1',
type: 'Table',
schema: 'dbo',
permissions: [
{
permission: 'SELECT',
grant: true,
grantor: '',
withGrant: undefined
}
],
effectivePermissions: ['SELECT']
}, {
name: 'view1',
type: 'View',
schema: 'Sales',
permissions: [
{
permission: 'ALTER',
grant: true,
grantor: '',
withGrant: undefined
}
],
effectivePermissions: ['ALTER']
}
];
export class TestObjectManagementService implements IObjectManagementService {
initializeView(contextId: string, objectType: ObjectManagement.NodeType, connectionUri: string, database: string, isNewObject: boolean, parentUrn: string, objectUrn: string): Thenable<ObjectManagement.ObjectViewInfo<ObjectManagement.SqlObject>> {
let obj;
@@ -102,18 +222,18 @@ export class TestObjectManagementService implements IObjectManagementService {
return this.delayAndResolve();
}
async search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText: string, schema: string): Promise<ObjectManagement.SearchResultItem[]> {
async search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText?: string, schema?: string): Promise<ObjectManagement.SearchResultItem[]> {
const items: ObjectManagement.SearchResultItem[] = [];
objectTypes.forEach(type => {
items.push(...this.generateSearchResult(type, 15));
items.push(...this.generateSearchResult(type, schema, 15));
});
return this.delayAndResolve(items);
}
private generateSearchResult(objectType: ObjectManagement.NodeType, count: number): ObjectManagement.SearchResultItem[] {
private generateSearchResult(objectType: ObjectManagement.NodeType, schema: string | undefined, count: number): ObjectManagement.SearchResultItem[] {
let items: ObjectManagement.SearchResultItem[] = [];
for (let i = 0; i < count; i++) {
items.push(<ObjectManagement.SearchResultItem>{ name: `${objectType} ${i}`, type: objectType });
items.push(<ObjectManagement.SearchResultItem>{ name: `${objectType} ${i}`, schema: schema, type: objectType });
}
return items;
}
@@ -136,7 +256,8 @@ export class TestObjectManagementService implements IObjectManagementService {
serverRoles: ['public', 'bulkadmin'],
connectPermission: true,
isEnabled: true,
isLockedOut: false
isLockedOut: false,
securablePermissions: []
},
authenticationTypes: [ObjectManagement.AuthenticationType.Sql, ObjectManagement.AuthenticationType.Windows],
supportAdvancedOptions: true,
@@ -144,7 +265,8 @@ export class TestObjectManagementService implements IObjectManagementService {
canEditLockedOutState: false,
languages: languages,
databases: databases,
serverRoles: serverRoles
serverRoles: serverRoles,
supportedSecurableTypes: ServerLevelSecurableTypes
};
} else {
login = <ObjectManagement.LoginViewInfo>{
@@ -160,7 +282,8 @@ export class TestObjectManagementService implements IObjectManagementService {
connectPermission: true,
isEnabled: true,
isLockedOut: false,
password: '******************'
password: '******************',
securablePermissions: ServerLevelPermissions
},
authenticationTypes: [ObjectManagement.AuthenticationType.Sql, ObjectManagement.AuthenticationType.Windows],
supportAdvancedOptions: true,
@@ -168,7 +291,8 @@ export class TestObjectManagementService implements IObjectManagementService {
canEditLockedOutState: false,
languages: languages,
databases: databases,
serverRoles: serverRoles
serverRoles: serverRoles,
supportedSecurableTypes: ServerLevelSecurableTypes
};
}
return login;
@@ -192,7 +316,8 @@ export class TestObjectManagementService implements IObjectManagementService {
loginName: 'sa',
ownedSchemas: [],
databaseRoles: [],
password: ''
password: '',
securablePermissions: []
},
languages: languages,
schemas: schemas,
@@ -203,7 +328,8 @@ export class TestObjectManagementService implements IObjectManagementService {
ObjectManagement.UserType.AADAuthentication,
ObjectManagement.UserType.SqlAuthentication,
ObjectManagement.UserType.NoLoginAccess
]
],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
} else {
viewInfo = {
@@ -214,7 +340,8 @@ export class TestObjectManagementService implements IObjectManagementService {
defaultLanguage: '<default>',
loginName: 'sa',
ownedSchemas: ['dbo'],
databaseRoles: ['dbmanager', 'bulkadmin']
databaseRoles: ['dbmanager', 'bulkadmin'],
securablePermissions: DatabaseLevelPermissions
},
languages: languages,
schemas: schemas,
@@ -225,7 +352,8 @@ export class TestObjectManagementService implements IObjectManagementService {
ObjectManagement.UserType.AADAuthentication,
ObjectManagement.UserType.SqlAuthentication,
ObjectManagement.UserType.NoLoginAccess
]
],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
}
return viewInfo;
@@ -237,19 +365,23 @@ export class TestObjectManagementService implements IObjectManagementService {
name: '',
members: [],
owner: '',
memberships: []
memberships: [],
securablePermissions: []
},
isFixedRole: false,
serverRoles: ['ServerLevelServerRole 1', 'ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
supportedSecurableTypes: ServerLevelSecurableTypes
} : <ObjectManagement.ServerRoleViewInfo>{
objectInfo: {
name: 'ServerLevelServerRole 1',
members: ['ServerLevelLogin 1', 'ServerLevelServerRole 2'],
owner: 'ServerLevelLogin 2',
memberships: ['ServerLevelServerRole 3', 'ServerLevelServerRole 4']
memberships: ['ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
securablePermissions: ServerLevelPermissions
},
isFixedRole: false,
serverRoles: ['ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4']
serverRoles: ['ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
supportedSecurableTypes: ServerLevelSecurableTypes
};
}
@@ -259,16 +391,20 @@ export class TestObjectManagementService implements IObjectManagementService {
name: '',
defaultSchema: 'dbo',
ownedSchemas: [],
securablePermissions: []
},
schemas: ['dbo', 'sys', 'admin']
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: []
} : <ObjectManagement.ApplicationRoleViewInfo>{
objectInfo: {
name: 'app role1',
password: '******************',
defaultSchema: 'dbo',
ownedSchemas: ['dbo'],
securablePermissions: DatabaseLevelPermissions
},
schemas: ['dbo', 'sys', 'admin']
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
}
@@ -278,17 +414,21 @@ export class TestObjectManagementService implements IObjectManagementService {
name: '',
owner: '',
members: [],
ownedSchemas: []
ownedSchemas: [],
securablePermissions: []
},
schemas: ['dbo', 'sys', 'admin']
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: DatabaseLevelSecurableTypes
} : <ObjectManagement.DatabaseRoleViewInfo>{
objectInfo: {
name: 'db role1',
owner: '',
members: [],
ownedSchemas: ['dbo']
ownedSchemas: ['dbo'],
securablePermissions: DatabaseLevelPermissions
},
schemas: ['dbo', 'sys', 'admin']
schemas: ['dbo', 'sys', 'admin'],
supportedSecurableTypes: DatabaseLevelSecurableTypes
};
}

View File

@@ -3,14 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { IObjectManagementService, ObjectManagement } from 'mssql';
import * as localizedConstants from '../localizedConstants';
import { AlterApplicationRoleDocUrl, CreateApplicationRoleDocUrl } from '../constants';
import { isValidSQLPassword } from '../utils';
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
import { PrincipalDialogBase } from './principalDialogBase';
export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectManagement.ApplicationRoleInfo, ObjectManagement.ApplicationRoleViewInfo> {
export class ApplicationRoleDialog extends PrincipalDialogBase<ObjectManagement.ApplicationRoleInfo, ObjectManagement.ApplicationRoleViewInfo> {
// Sections
private generalSection: azdata.GroupContainer;
private ownedSchemasSection: azdata.GroupContainer;
@@ -25,7 +26,7 @@ export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectMana
private ownedSchemaTable: azdata.TableComponent;
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
super(objectManagementService, options);
super(objectManagementService, options, true, false);
}
protected override postInitializeData(): void {
@@ -51,10 +52,11 @@ export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectMana
return errors;
}
protected async initializeUI(): Promise<void> {
protected override async initializeUI(): Promise<void> {
await super.initializeUI();
this.initializeGeneralSection();
this.initializeOwnedSchemasSection();
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection]);
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection, this.securableSection], this.getSectionItemLayout());
}
private initializeGeneralSection(): void {
@@ -84,7 +86,7 @@ export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectMana
[localizedConstants.SchemaText],
this.viewInfo.schemas,
this.objectInfo.ownedSchemas,
DefaultMaxTableHeight,
DefaultMaxTableRowCount,
(item) => {
// It is not allowed to have unassigned schema.
return this.objectInfo.ownedSchemas.indexOf(item) === -1;

View File

@@ -3,14 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { IObjectManagementService, ObjectManagement } from 'mssql';
import * as localizedConstants from '../localizedConstants';
import { AlterDatabaseRoleDocUrl, CreateDatabaseRoleDocUrl } from '../constants';
import { FindObjectDialog } from './findObjectDialog';
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
import { PrincipalDialogBase } from './principalDialogBase';
export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagement.DatabaseRoleInfo, ObjectManagement.DatabaseRoleViewInfo> {
export class DatabaseRoleDialog extends PrincipalDialogBase<ObjectManagement.DatabaseRoleInfo, ObjectManagement.DatabaseRoleViewInfo> {
// Sections
private generalSection: azdata.GroupContainer;
private ownedSchemasSection: azdata.GroupContainer;
@@ -27,18 +28,19 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
private memberTable: azdata.TableComponent;
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
super(objectManagementService, options);
super(objectManagementService, options, true, false);
}
protected override get helpUrl(): string {
return this.options.isNewObject ? CreateDatabaseRoleDocUrl : AlterDatabaseRoleDocUrl;
}
protected async initializeUI(): Promise<void> {
protected override async initializeUI(): Promise<void> {
await super.initializeUI();
this.initializeGeneralSection();
this.initializeOwnedSchemasSection();
this.initializeMemberSection();
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection, this.memberSection]);
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection, this.memberSection, this.securableSection], this.getSectionItemLayout());
}
private initializeGeneralSection(): void {
@@ -53,9 +55,11 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
const browseOwnerButton = this.createButton(localizedConstants.BrowseText, localizedConstants.BrowseOwnerButtonAriaLabel, async () => {
const dialog = new FindObjectDialog(this.objectManagementService, {
objectTypes: [ObjectManagement.NodeType.ApplicationRole, ObjectManagement.NodeType.DatabaseRole, ObjectManagement.NodeType.User],
selectAllObjectTypes: true,
multiSelect: false,
contextId: this.contextId,
title: localizedConstants.SelectDatabaseRoleOwnerDialogTitle
title: localizedConstants.SelectDatabaseRoleOwnerDialogTitle,
showSchemaColumn: false
});
await dialog.open();
const result = await dialog.waitForClose();
@@ -70,48 +74,45 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
}
private initializeMemberSection(): void {
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [
{
type: azdata.ColumnType.text,
value: localizedConstants.NameText
}
], this.objectInfo.members.map(m => [m]));
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [localizedConstants.NameText], this.objectInfo.members.map(m => [m]));
const buttonContainer = this.addButtonsForTable(this.memberTable, localizedConstants.AddMemberAriaLabel, localizedConstants.RemoveMemberAriaLabel,
async () => {
const dialog = new FindObjectDialog(this.objectManagementService, {
objectTypes: [ObjectManagement.NodeType.DatabaseRole, ObjectManagement.NodeType.User],
selectAllObjectTypes: true,
multiSelect: true,
contextId: this.contextId,
title: localizedConstants.SelectDatabaseRoleMemberDialogTitle
title: localizedConstants.SelectDatabaseRoleMemberDialogTitle,
showSchemaColumn: false
});
await dialog.open();
const result = await dialog.waitForClose();
this.addMembers(result.selectedObjects.map(r => r.name));
await this.addMembers(result.selectedObjects.map(r => r.name));
},
async () => {
if (this.memberTable.selectedRows.length === 1) {
this.removeMember(this.memberTable.selectedRows[0]);
await this.removeMember(this.memberTable.selectedRows[0]);
}
});
this.memberSection = this.createGroup(localizedConstants.MemberSectionHeader, [this.memberTable, buttonContainer]);
}
private addMembers(names: string[]): void {
private async addMembers(names: string[]): Promise<void> {
names.forEach(n => {
if (this.objectInfo.members.indexOf(n) === -1) {
this.objectInfo.members.push(n);
}
});
this.updateMembersTable();
await this.updateMembersTable();
}
private removeMember(idx: number): void {
private async removeMember(idx: number): Promise<void> {
this.objectInfo.members.splice(idx, 1);
this.updateMembersTable();
await this.updateMembersTable();
}
private updateMembersTable(): void {
this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
private async updateMembersTable(): Promise<void> {
await this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
this.onFormFieldChange();
}
@@ -120,7 +121,7 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
[localizedConstants.SchemaText],
this.viewInfo.schemas,
this.objectInfo.ownedSchemas,
DefaultMaxTableHeight,
DefaultMaxTableRowCount,
(item) => {
// It is not allowed to have unassigned schema.
return this.objectInfo.ownedSchemas.indexOf(item) === -1;

View File

@@ -5,15 +5,19 @@
import * as azdata from 'azdata';
import * as mssql from 'mssql';
import { DefaultTableListItemEnabledStateGetter, DefaultMaxTableHeight, DialogBase, TableListItemComparer, TableListItemValueGetter } from '../../ui/dialogBase';
import { DefaultTableListItemEnabledStateGetter, DefaultMaxTableRowCount, DialogBase, TableListItemComparer } from '../../ui/dialogBase';
import * as localizedConstants from '../localizedConstants';
import { getErrorMessage } from '../../utils';
type ObjectType = string | { name: string, displayName: string };
export interface FindObjectDialogOptions {
objectTypes: mssql.ObjectManagement.NodeType[];
objectTypes: ObjectType[];
selectAllObjectTypes: boolean;
multiSelect: boolean;
contextId: string;
title: string;
showSchemaColumn?: boolean;
}
export interface FindObjectDialogResult {
@@ -22,15 +26,10 @@ export interface FindObjectDialogResult {
const ObjectComparer: TableListItemComparer<mssql.ObjectManagement.SearchResultItem> =
(item1, item2) => {
return item1.name === item2.name && item1.type === item2.type;
return item1.name === item2.name && item1.type === item2.type && item1.schema === item2.schema;
};
const ObjectRowValueGetter: TableListItemValueGetter<mssql.ObjectManagement.SearchResultItem> =
(item) => {
return [item.name, localizedConstants.getNodeTypeDisplayName(item.type, true)];
};
const ObjectsTableMaxHeight = 700;
const ObjectsTableMaxRowCount = 20;
export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
private objectTypesTable: azdata.TableComponent;
@@ -38,7 +37,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
private objectsTable: azdata.TableComponent;
private objectsLoadingComponent: azdata.LoadingComponent;
private result: FindObjectDialogResult;
private selectedObjectTypes: string[] = [];
private selectedObjectTypes: ObjectType[] = [];
private allObjects: mssql.ObjectManagement.SearchResultItem[] = [];
constructor(private readonly objectManagementService: mssql.IObjectManagementService, private readonly options: FindObjectDialogOptions) {
@@ -47,40 +46,52 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
this.result = {
selectedObjects: []
};
this.selectedObjectTypes = [...options.objectTypes];
this.selectedObjectTypes = options.selectAllObjectTypes ? [...options.objectTypes] : [];
}
private getObjectTypeName(objectType: ObjectType): string {
return typeof objectType === 'string' ? objectType : objectType.name;
}
private getObjectTypeDisplayName(objectType: ObjectType): string {
return typeof objectType === 'string' ? localizedConstants.getNodeTypeDisplayName(objectType, true) : objectType.displayName;
}
protected override async initialize(): Promise<void> {
this.dialogObject.okButton.enabled = false;
this.objectTypesTable = this.createTableList<string>(localizedConstants.ObjectTypeText,
this.objectTypesTable = this.createTableList<ObjectType>(localizedConstants.ObjectTypeText,
[localizedConstants.ObjectTypeText],
this.options.objectTypes,
this.selectedObjectTypes,
DefaultMaxTableHeight,
DefaultMaxTableRowCount,
DefaultTableListItemEnabledStateGetter, (item) => {
return [localizedConstants.getNodeTypeDisplayName(item, true)];
return [this.getObjectTypeDisplayName(item)];
}, (item1, item2) => {
return this.getObjectTypeName(item1) === this.getObjectTypeName(item2);
});
this.findButton = this.createButton(localizedConstants.FindText, localizedConstants.FindText, async () => {
await this.onFindObjectButtonClick();
});
}, this.options.selectAllObjectTypes);
const buttonContainer = this.createButtonContainer([this.findButton]);
const objectTypeSection = this.createGroup(localizedConstants.ObjectTypeText, [this.objectTypesTable, buttonContainer]);
const columns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
if (this.options.showSchemaColumn) {
columns.splice(1, 0, localizedConstants.SchemaText);
}
if (this.options.multiSelect) {
this.objectsTable = this.createTableList<mssql.ObjectManagement.SearchResultItem>(localizedConstants.ObjectsText,
[localizedConstants.NameText, localizedConstants.ObjectTypeText],
columns,
this.allObjects,
this.result.selectedObjects,
ObjectsTableMaxHeight,
ObjectsTableMaxRowCount,
DefaultTableListItemEnabledStateGetter,
ObjectRowValueGetter,
(item) => {
return this.getObjectRowValue(item);
},
ObjectComparer);
} else {
this.objectsTable = this.createTable(localizedConstants.ObjectsText, [{
value: localizedConstants.NameText,
}, {
value: localizedConstants.ObjectTypeText
}], []);
this.objectsTable = this.createTable(localizedConstants.ObjectsText, columns, [], ObjectsTableMaxRowCount);
this.disposables.push(this.objectsTable.onRowSelected(async () => {
if (this.objectsTable.selectedRows.length > 0) {
this.result.selectedObjects = [this.allObjects[this.objectsTable.selectedRows[0]]];
@@ -95,7 +106,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
}).component();
const objectsSection = this.createGroup(localizedConstants.ObjectsText, [this.objectsLoadingComponent]);
this.formContainer.addItems([objectTypeSection, objectsSection]);
this.formContainer.addItems([objectTypeSection, objectsSection], this.getSectionItemLayout());
}
protected override get dialogResult(): FindObjectDialogResult | undefined {
@@ -107,16 +118,18 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
this.objectsLoadingComponent.loading = true;
this.findButton.enabled = false;
try {
const results = await this.objectManagementService.search(this.options.contextId, <mssql.ObjectManagement.NodeType[]>this.selectedObjectTypes);
const results = await this.objectManagementService.search(this.options.contextId, this.selectedObjectTypes.map(item => this.getObjectTypeName(item)));
this.allObjects.splice(0, this.allObjects.length, ...results);
let data;
if (this.options.multiSelect) {
data = this.getDataForTableList(this.allObjects, this.result.selectedObjects, DefaultTableListItemEnabledStateGetter, ObjectRowValueGetter, ObjectComparer);
data = this.getDataForTableList(this.allObjects, this.result.selectedObjects, DefaultTableListItemEnabledStateGetter, (item) => {
return this.getObjectRowValue(item);
}, ObjectComparer);
}
else {
data = this.allObjects.map(item => ObjectRowValueGetter(item));
data = this.allObjects.map(item => { return this.getObjectRowValue(item); });
}
this.setTableData(this.objectsTable, data, ObjectsTableMaxHeight);
await this.setTableData(this.objectsTable, data, ObjectsTableMaxRowCount);
this.objectsLoadingComponent.loadingCompletedText = localizedConstants.LoadingObjectsCompletedText(results.length);
} catch (err) {
this.dialogObject.message = {
@@ -132,4 +145,12 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
this.findButton.enabled = this.selectedObjectTypes.length > 0;
this.dialogObject.okButton.enabled = this.result.selectedObjects.length > 0;
}
private getObjectRowValue(item: mssql.ObjectManagement.SearchResultItem): string[] {
const row = [item.name, this.getObjectTypeName(item.type)];
if (this.options.showSchemaColumn) {
row.splice(1, 0, item.schema);
}
return row;
}
}

View File

@@ -4,15 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { IObjectManagementService, ObjectManagement } from 'mssql';
import * as objectManagementLoc from '../localizedConstants';
import * as uiLoc from '../../ui/localizedConstants';
import { AlterLoginDocUrl, CreateLoginDocUrl, PublicServerRoleName } from '../constants';
import { isValidSQLPassword } from '../utils';
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
import { PrincipalDialogBase } from './principalDialogBase';
export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Login, ObjectManagement.LoginViewInfo> {
export class LoginDialog extends PrincipalDialogBase<ObjectManagement.Login, ObjectManagement.LoginViewInfo> {
private generalSection: azdata.GroupContainer;
private sqlAuthSection: azdata.GroupContainer;
private serverRoleSection: azdata.GroupContainer;
@@ -34,7 +35,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
private lockedOutCheckbox: azdata.CheckBoxComponent;
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
super(objectManagementService, options);
super(objectManagementService, options, false);
}
protected override get helpUrl(): string {
@@ -82,7 +83,8 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
this.objectInfo.password = this.objectInfo.password ?? '';
}
protected async initializeUI(): Promise<void> {
protected override async initializeUI(): Promise<void> {
await super.initializeUI();
const sections: azdata.Component[] = [];
this.initializeGeneralSection();
sections.push(this.generalSection);
@@ -94,12 +96,13 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
this.initializeServerRolesSection();
sections.push(this.serverRoleSection);
sections.push(this.securableSection);
if (this.viewInfo.supportAdvancedOptions) {
this.initializeAdvancedSection();
sections.push(this.advancedSection);
}
this.formContainer.addItems(sections);
this.formContainer.addItems(sections, this.getSectionItemLayout());
}
private initializeGeneralSection(): void {
@@ -203,7 +206,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
items.push(defaultDatabaseContainer, defaultLanguageContainer, this.connectPermissionCheckbox);
}
this.advancedSection = this.createGroup(objectManagementLoc.AdvancedSectionHeader, items);
this.advancedSection = this.createGroup(objectManagementLoc.AdvancedSectionHeader, items, true, true);
}
private initializeServerRolesSection(): void {
@@ -211,7 +214,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
[objectManagementLoc.ServerRoleTypeDisplayNameInTitle],
this.viewInfo.serverRoles,
this.objectInfo.serverRoles,
DefaultMaxTableHeight,
DefaultMaxTableRowCount,
(item) => {
return item !== PublicServerRoleName
});
@@ -220,7 +223,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
private setViewByAuthenticationType(): void {
if (this.authTypeDropdown.value === objectManagementLoc.SQLAuthenticationTypeDisplayText) {
this.addItem(this.formContainer, this.sqlAuthSection, 1);
this.addItem(this.formContainer, this.sqlAuthSection, this.getSectionItemLayout(), 1);
} else if (this.authTypeDropdown.value !== objectManagementLoc.SQLAuthenticationTypeDisplayText) {
this.removeItem(this.formContainer, this.sqlAuthSection);
}

View File

@@ -0,0 +1,228 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as mssql from 'mssql';
import * as localizedConstants from '../localizedConstants';
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { FindObjectDialog } from './findObjectDialog';
import { deepClone } from '../../util/objects';
import { DefaultTableWidth, getTableHeight } from '../../ui/dialogBase';
const GrantColumnIndex = 2;
const WithGrantColumnIndex = 3;
const DenyColumnIndex = 4;
/**
* Base class for security principal dialogs such as user, role, etc.
*/
export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectManagement.SecurityPrincipalObject, ViewInfoType extends mssql.ObjectManagement.SecurityPrincipalViewInfo<ObjectInfoType>> extends ObjectManagementDialogBase<ObjectInfoType, ViewInfoType> {
protected securableTable: azdata.TableComponent;
protected permissionTable: azdata.TableComponent;
protected effectivePermissionTable: azdata.TableComponent;
protected securableSection: azdata.GroupContainer;
protected explicitPermissionTableLabel: azdata.TextComponent;
protected effectivePermissionTableLabel: azdata.TextComponent;
private securablePermissions: mssql.ObjectManagement.SecurablePermissions[] = [];
constructor(objectManagementService: mssql.IObjectManagementService, options: ObjectManagementDialogOptions, private readonly showSchemaColumn: boolean, private readonly supportEffectivePermissions: boolean = true) {
super(objectManagementService, options);
}
protected override async initializeUI(): Promise<void> {
this.securablePermissions = deepClone(this.objectInfo.securablePermissions);
this.initializeSecurableSection();
}
private initializeSecurableSection(): void {
const items: azdata.Component[] = [];
const securableTableColumns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
if (this.showSchemaColumn) {
securableTableColumns.splice(1, 0, localizedConstants.SchemaText);
}
this.securableTable = this.createTable(localizedConstants.SecurablesText, securableTableColumns, this.getSecurableTableData());
const buttonContainer = this.addButtonsForTable(this.securableTable, localizedConstants.AddSecurableAriaLabel, localizedConstants.RemoveSecurableAriaLabel,
() => this.onAddSecurableButtonClicked(), () => this.onRemoveSecurableButtonClicked());
this.disposables.push(this.securableTable.onRowSelected(async () => {
await this.updatePermissionsTable();
}));
this.explicitPermissionTableLabel = this.modelView.modelBuilder.text().withProps({ value: localizedConstants.ExplicitPermissionsTableLabel }).component();
this.permissionTable = this.modelView.modelBuilder.table().withProps({
ariaLabel: localizedConstants.ExplicitPermissionsTableLabel,
columns:
[{
type: azdata.ColumnType.text,
value: localizedConstants.PermissionColumnHeader
}, {
type: azdata.ColumnType.text,
value: localizedConstants.GrantorColumnHeader
}, {
type: azdata.ColumnType.checkBox,
value: localizedConstants.GrantColumnHeader
}, {
type: azdata.ColumnType.checkBox,
value: localizedConstants.WithGrantColumnHeader
}, {
type: azdata.ColumnType.checkBox,
value: localizedConstants.DenyColumnHeader
}],
data: [],
height: getTableHeight(0),
width: DefaultTableWidth
}).component();
this.disposables.push(this.permissionTable.onCellAction(async (arg: azdata.ICheckboxCellActionEventArgs) => {
const permissionName = this.permissionTable.data[arg.row][0];
const securable = this.securablePermissions[this.securableTable.selectedRows[0]];
let permission: mssql.ObjectManagement.SecurablePermissionItem = securable.permissions.find(securablePermission => securablePermission.permission === permissionName);
if (!permission) {
permission = {
permission: permissionName,
grantor: ''
};
securable.permissions.push(permission);
}
if (arg.column === GrantColumnIndex) {
permission.grant = arg.checked ? true : undefined;
if (!arg.checked) {
permission.withGrant = undefined;
}
} else if (arg.column === WithGrantColumnIndex) {
permission.withGrant = arg.checked ? true : undefined;
if (arg.checked) {
permission.grant = true;
}
} else if (arg.column === DenyColumnIndex) {
permission.grant = arg.checked ? false : undefined;
if (arg.checked) {
permission.withGrant = undefined;
}
}
await this.updatePermissionsTable();
this.updateSecurablePermissions();
// Restore the focus to previously selected cell.
this.permissionTable.setActiveCell(arg.row, arg.column);
}));
items.push(this.securableTable, buttonContainer, this.explicitPermissionTableLabel, this.permissionTable);
if (this.showEffectivePermissions) {
this.effectivePermissionTableLabel = this.modelView.modelBuilder.text().withProps({ value: localizedConstants.EffectivePermissionsTableLabel }).component();
this.effectivePermissionTable = this.createTable(localizedConstants.EffectivePermissionsTableLabel, [localizedConstants.PermissionColumnHeader], []);
items.push(this.effectivePermissionTableLabel, this.effectivePermissionTable);
}
this.securableSection = this.createGroup(localizedConstants.SecurablesText, items, true, true);
}
private async onAddSecurableButtonClicked(): Promise<void> {
const dialog = new FindObjectDialog(this.objectManagementService, {
objectTypes: this.viewInfo.supportedSecurableTypes,
selectAllObjectTypes: false,
multiSelect: true,
contextId: this.contextId,
title: localizedConstants.SelectSecurablesDialogTitle,
showSchemaColumn: this.showSchemaColumn
});
await dialog.open();
const result = await dialog.waitForClose();
if (result && result.selectedObjects.length > 0) {
result.selectedObjects.forEach(obj => {
if (this.securablePermissions.find(securable => securable.type === obj.type && securable.name === obj.name && securable.schema === obj.schema)) {
return;
}
const securableTypeMetadata = this.viewInfo.supportedSecurableTypes.find(securableType => securableType.name === obj.type);
this.securablePermissions.push({
name: obj.name,
schema: obj.schema,
type: obj.type,
permissions: securableTypeMetadata.permissions.map(permission => {
return {
permission: permission.name,
grantor: '',
grant: undefined,
withGrant: undefined
};
}),
effectivePermissions: []
});
});
const data = this.getSecurableTableData();
await this.setTableData(this.securableTable, data);
}
}
private async onRemoveSecurableButtonClicked(): Promise<void> {
if (this.securableTable.selectedRows.length === 1) {
this.securablePermissions.splice(this.securableTable.selectedRows[0], 1);
const data = this.getSecurableTableData();
await this.setTableData(this.securableTable, data);
this.updateSecurablePermissions();
}
}
private getSecurableTableData(): string[][] {
return this.securablePermissions.map(securable => {
const row = [securable.name, this.getSecurableTypeDisplayName(securable.type)];
if (this.showSchemaColumn) {
row.splice(1, 0, securable.schema);
}
return row;
});
}
private async updatePermissionsTable(): Promise<void> {
let permissionsTableData: any[][] = [];
let effectivePermissionsTableData: any[][] = [];
let explicitPermissionsLabel = localizedConstants.ExplicitPermissionsTableLabel;
let effectivePermissionsLabel = localizedConstants.EffectivePermissionsTableLabel;
if (this.securableTable.selectedRows.length === 1) {
const securable = this.securablePermissions[this.securableTable.selectedRows[0]];
if (securable) {
const securableDisplayName = securable.schema ? `${securable.schema}.${securable.name}` : securable.name;
explicitPermissionsLabel = localizedConstants.ExplicitPermissionsTableLabelSelected(securableDisplayName);
effectivePermissionsLabel = localizedConstants.EffectivePermissionsTableLabelSelected(securableDisplayName);
const securableTypeMetadata = this.viewInfo.supportedSecurableTypes.find(securableType => securableType.name === securable.type);
permissionsTableData = securable.permissions.map(permission => {
return [permission.permission, permission.grantor, { checked: permission.grant === true }, { checked: permission.withGrant === true }, { checked: permission.grant === false }];
});
permissionsTableData = securableTypeMetadata.permissions.map(permissionMetadata => {
const permission = securable.permissions.find(securablePermission => securablePermission.permission === permissionMetadata.name);
return [
permissionMetadata.name,
permission?.grantor ?? '',
{ checked: permission?.grant === true },
{ checked: permission?.withGrant === true },
{ checked: permission?.grant === false }];
});
effectivePermissionsTableData = securable.effectivePermissions.map(permission => [permission]);
}
}
this.explicitPermissionTableLabel.value = explicitPermissionsLabel;
await this.setTableData(this.permissionTable, permissionsTableData);
if (this.showEffectivePermissions) {
this.effectivePermissionTableLabel.value = effectivePermissionsLabel;
await this.setTableData(this.effectivePermissionTable, effectivePermissionsTableData);
}
}
private updateSecurablePermissions(): void {
// Only save securable permissions that have grant or deny value.
this.objectInfo.securablePermissions = deepClone(this.securablePermissions.filter((securablePermissions) => {
return securablePermissions.permissions.some(permission => permission.grant !== undefined);
}));
this.objectInfo.securablePermissions.forEach(securablePermissions => {
securablePermissions.permissions = securablePermissions.permissions.filter(permission => permission.grant !== undefined);
});
this.onFormFieldChange();
}
private getSecurableTypeDisplayName(securableType: string): string {
const securableTypeMetadata = this.viewInfo.supportedSecurableTypes.find(securableTypeMetadata => securableTypeMetadata.name === securableType);
return securableTypeMetadata ? securableTypeMetadata.displayName : securableType;
}
private get showEffectivePermissions(): boolean {
return !this.options.isNewObject && this.supportEffectivePermissions;
}
}

View File

@@ -3,13 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { IObjectManagementService, ObjectManagement } from 'mssql';
import * as localizedConstants from '../localizedConstants';
import { AlterServerRoleDocUrl, CreateServerRoleDocUrl } from '../constants';
import { FindObjectDialog } from './findObjectDialog';
import { PrincipalDialogBase } from './principalDialogBase';
export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagement.ServerRoleInfo, ObjectManagement.ServerRoleViewInfo> {
export class ServerRoleDialog extends PrincipalDialogBase<ObjectManagement.ServerRoleInfo, ObjectManagement.ServerRoleViewInfo> {
// Sections
private generalSection: azdata.GroupContainer;
private membershipSection: azdata.GroupContainer;
@@ -27,22 +28,24 @@ export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagemen
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
super(objectManagementService, options);
super(objectManagementService, options, false, false);
}
protected override get helpUrl(): string {
return this.options.isNewObject ? CreateServerRoleDocUrl : AlterServerRoleDocUrl;
}
protected async initializeUI(): Promise<void> {
protected override async initializeUI(): Promise<void> {
await super.initializeUI();
this.initializeGeneralSection();
this.initializeMemberSection();
const sections: azdata.Component[] = [this.generalSection, this.memberSection];
if (!this.viewInfo.isFixedRole) {
this.initializeMembershipSection();
sections.push(this.membershipSection);
sections.push(this.securableSection);
}
this.formContainer.addItems(sections);
this.formContainer.addItems(sections, this.getSectionItemLayout());
}
private initializeGeneralSection(): void {
@@ -57,6 +60,7 @@ export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagemen
const browseOwnerButton = this.createButton(localizedConstants.BrowseText, localizedConstants.BrowseOwnerButtonAriaLabel, async () => {
const dialog = new FindObjectDialog(this.objectManagementService, {
objectTypes: [ObjectManagement.NodeType.ServerLevelLogin, ObjectManagement.NodeType.ServerLevelServerRole],
selectAllObjectTypes: true,
multiSelect: false,
contextId: this.contextId,
title: localizedConstants.SelectServerRoleOwnerDialogTitle
@@ -76,48 +80,44 @@ export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagemen
}
private initializeMemberSection(): void {
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [
{
type: azdata.ColumnType.text,
value: localizedConstants.NameText
}
], this.objectInfo.members.map(m => [m]));
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [localizedConstants.NameText], this.objectInfo.members.map(m => [m]));
const buttonContainer = this.addButtonsForTable(this.memberTable, localizedConstants.AddMemberAriaLabel, localizedConstants.RemoveMemberAriaLabel,
async () => {
const dialog = new FindObjectDialog(this.objectManagementService, {
objectTypes: [ObjectManagement.NodeType.ServerLevelLogin, ObjectManagement.NodeType.ServerLevelServerRole],
selectAllObjectTypes: true,
multiSelect: true,
contextId: this.contextId,
title: localizedConstants.SelectServerRoleMemberDialogTitle
});
await dialog.open();
const result = await dialog.waitForClose();
this.addMembers(result.selectedObjects.map(r => r.name));
await this.addMembers(result.selectedObjects.map(r => r.name));
},
async () => {
if (this.memberTable.selectedRows.length === 1) {
this.removeMember(this.memberTable.selectedRows[0]);
await this.removeMember(this.memberTable.selectedRows[0]);
}
});
this.memberSection = this.createGroup(localizedConstants.MemberSectionHeader, [this.memberTable, buttonContainer]);
}
private addMembers(names: string[]): void {
private async addMembers(names: string[]): Promise<void> {
names.forEach(n => {
if (this.objectInfo.members.indexOf(n) === -1) {
this.objectInfo.members.push(n);
}
});
this.updateMembersTable();
await this.updateMembersTable();
}
private removeMember(idx: number): void {
private async removeMember(idx: number): Promise<void> {
this.objectInfo.members.splice(idx, 1);
this.updateMembersTable();
await this.updateMembersTable();
}
private updateMembersTable(): void {
this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
private async updateMembersTable(): Promise<void> {
await this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
this.onFormFieldChange();
}

View File

@@ -3,14 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
import { IObjectManagementService, ObjectManagement } from 'mssql';
import * as localizedConstants from '../localizedConstants';
import { AlterUserDocUrl, CreateUserDocUrl } from '../constants';
import { isValidSQLPassword } from '../utils';
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
import { PrincipalDialogBase } from './principalDialogBase';
export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User, ObjectManagement.UserViewInfo> {
export class UserDialog extends PrincipalDialogBase<ObjectManagement.User, ObjectManagement.UserViewInfo> {
private generalSection: azdata.GroupContainer;
private ownedSchemaSection: azdata.GroupContainer;
private membershipSection: azdata.GroupContainer;
@@ -31,7 +32,7 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
private membershipTable: azdata.TableComponent;
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
super(objectManagementService, options);
super(objectManagementService, options, true);
}
protected override get helpUrl(): string {
@@ -61,12 +62,13 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
return errors;
}
protected async initializeUI(): Promise<void> {
protected override async initializeUI(): Promise<void> {
await super.initializeUI();
this.initializeGeneralSection();
this.initializeOwnedSchemaSection();
this.initializeMembershipSection();
this.initializeAdvancedSection();
this.formContainer.addItems([this.generalSection, this.ownedSchemaSection, this.membershipSection, this.advancedSection]);
this.formContainer.addItems([this.generalSection, this.ownedSchemaSection, this.membershipSection, this.securableSection, this.advancedSection], this.getSectionItemLayout());
setTimeout(() => {
this.setViewByUserType();
}, 100);
@@ -94,7 +96,7 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
this.loginDropdown = this.createDropdown(localizedConstants.LoginText, async (newValue) => {
this.objectInfo.loginName = newValue;
}, this.viewInfo.logins, this.objectInfo.loginName, this.options.isNewObject);
}, this.options.isNewObject ? this.viewInfo.logins : [this.objectInfo.loginName], this.objectInfo.loginName, this.options.isNewObject);
this.loginContainer = this.createLabelInputContainer(localizedConstants.LoginText, this.loginDropdown);
this.passwordInput = this.createPasswordInputBox(localizedConstants.PasswordText, async (newValue) => {
@@ -119,7 +121,7 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
[localizedConstants.SchemaText],
this.viewInfo.schemas,
this.objectInfo.ownedSchemas,
DefaultMaxTableHeight,
DefaultMaxTableRowCount,
(item) => {
// It is not allowed to have unassigned schema.
return this.objectInfo.ownedSchemas.indexOf(item) === -1;
@@ -157,6 +159,11 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
this.addItem(this.generalSection, this.confirmPasswordContainer);
this.addItem(this.formContainer, this.advancedSection);
break;
case ObjectManagement.UserType.WindowsUser:
if (this.objectInfo.loginName) {
this.addItem(this.generalSection, this.loginContainer);
}
break;
default:
break;
}

View File

@@ -11,13 +11,13 @@ import * as uiLoc from '../ui/localizedConstants';
export const DefaultLabelWidth = 150;
export const DefaultInputWidth = 300;
export const DefaultTableWidth = DefaultInputWidth + DefaultLabelWidth;
export const DefaultMaxTableHeight = 400;
export const DefaultMaxTableRowCount = 10;
export const DefaultMinTableRowCount = 1;
export const TableRowHeight = 25;
export const TableColumnHeaderHeight = 30;
const TableRowHeight = 25;
const TableColumnHeaderHeight = 30;
export function getTableHeight(rowCount: number, minRowCount: number = DefaultMinTableRowCount, maxHeight: number = DefaultMaxTableHeight): number {
return Math.min(Math.max(rowCount, minRowCount) * TableRowHeight + TableColumnHeaderHeight, maxHeight);
export function getTableHeight(rowCount: number, minRowCount: number = DefaultMinTableRowCount, maxRowCount: number = DefaultMaxTableRowCount): number {
return Math.min(Math.max(rowCount, minRowCount), maxRowCount) * TableRowHeight + TableColumnHeaderHeight;
}
export type TableListItemEnabledStateGetter<T> = (item: T) => boolean;
@@ -168,7 +168,7 @@ export abstract class DialogBase<DialogResult> {
columnNames: string[],
allItems: T[],
selectedItems: T[],
maxHeight: number = DefaultMaxTableHeight,
maxRowCount: number = DefaultMaxTableRowCount,
enabledStateGetter: TableListItemEnabledStateGetter<T> = DefaultTableListItemEnabledStateGetter,
rowValueGetter: TableListItemValueGetter<T> = DefaultTableListItemValueGetter,
itemComparer: TableListItemComparer<T> = DefaultTableListItemComparer): azdata.TableComponent {
@@ -179,7 +179,7 @@ export abstract class DialogBase<DialogResult> {
data: data,
columns: [
{
value: uiLoc.SelectedText,
value: uiLoc.SelectText,
type: azdata.ColumnType.checkBox,
options: { actionOnCheckbox: azdata.ActionOnCellCheckboxCheck.customAction }
}, ...columnNames.map(name => {
@@ -187,7 +187,7 @@ export abstract class DialogBase<DialogResult> {
})
],
width: DefaultTableWidth,
height: getTableHeight(data.length, DefaultMinTableRowCount, maxHeight)
height: getTableHeight(data.length, DefaultMinTableRowCount, maxRowCount)
}
).component();
this.disposables.push(table.onCellAction!((arg: azdata.ICheckboxCellActionEventArgs) => {
@@ -203,9 +203,11 @@ export abstract class DialogBase<DialogResult> {
return table;
}
protected setTableData(table: azdata.TableComponent, data: any[][], maxHeight: number = DefaultMaxTableHeight) {
table.data = data;
table.height = getTableHeight(data.length, DefaultMinTableRowCount, maxHeight);
protected async setTableData(table: azdata.TableComponent, data: any[][], maxRowCount: number = DefaultMaxTableRowCount) {
await table.updateProperties({
data: data,
height: getTableHeight(data.length, DefaultMinTableRowCount, maxRowCount)
});
}
protected getDataForTableList<T>(
@@ -221,14 +223,14 @@ export abstract class DialogBase<DialogResult> {
});
}
protected createTable(ariaLabel: string, columns: azdata.TableColumn[], data: any[][], maxHeight: number = DefaultMaxTableHeight): azdata.TableComponent {
protected createTable(ariaLabel: string, columns: string[], data: any[][], maxRowCount: number = DefaultMaxTableRowCount): azdata.TableComponent {
const table = this.modelView.modelBuilder.table().withProps(
{
ariaLabel: ariaLabel,
data: data,
columns: columns,
width: DefaultTableWidth,
height: getTableHeight(data.length, DefaultMinTableRowCount, maxHeight)
height: getTableHeight(data.length, DefaultMinTableRowCount, maxRowCount)
}
).component();
return table;
@@ -238,7 +240,8 @@ export abstract class DialogBase<DialogResult> {
let addButton: azdata.ButtonComponent;
let removeButton: azdata.ButtonComponent;
const updateButtons = () => {
removeButton.enabled = table.selectedRows.length > 0;
this.onFormFieldChange();
removeButton.enabled = table.selectedRows?.length === 1 && table.selectedRows[0] !== -1 && table.selectedRows[0] < table.data.length;
}
addButton = this.createButton(uiLoc.AddText, addButtonAriaLabel, async () => {
await addHandler();
@@ -246,6 +249,9 @@ export abstract class DialogBase<DialogResult> {
});
removeButton = this.createButton(uiLoc.RemoveText, removeButtonAriaLabel, async () => {
await removeHandler();
if (table.selectedRows.length === 1 && table.selectedRows[0] >= table.data.length) {
table.selectedRows = [table.data.length - 1];
}
updateButtons();
}, false);
this.disposables.push(table.onRowSelected(() => {
@@ -308,12 +314,12 @@ export abstract class DialogBase<DialogResult> {
}
}
protected addItem(container: azdata.DivContainer | azdata.FlexContainer, item: azdata.Component, index?: number): void {
protected addItem(container: azdata.DivContainer | azdata.FlexContainer, item: azdata.Component, itemLayout?: azdata.FlexItemLayout, index?: number): void {
if (container.items.indexOf(item) === -1) {
if (index === undefined) {
container.addItem(item);
container.addItem(item, itemLayout);
} else {
container.insertItem(item, index);
container.insertItem(item, index, itemLayout);
}
}
}
@@ -327,6 +333,10 @@ export abstract class DialogBase<DialogResult> {
private createFormContainer(items: azdata.Component[]): azdata.DivContainer {
return this.modelView.modelBuilder.divContainer().withLayout({ width: 'calc(100% - 20px)', height: 'calc(100% - 20px)' }).withProps({
CSSStyles: { 'padding': '10px' }
}).withItems(items, { CSSStyles: { 'margin-block-end': '10px' } }).component();
}).withItems(items, this.getSectionItemLayout()).component();
}
protected getSectionItemLayout(): azdata.FlexItemLayout {
return { CSSStyles: { 'margin-block-end': '5px' } };
}
}

View File

@@ -11,7 +11,7 @@ export const YesText: string = localize('mssql.ui.yesText', "Yes");
export const OkText: string = localize('mssql.ui.OkText', "OK");
export const LoadingDialogText: string = localize('mssql.ui.loadingDialog', "Loading dialog...");
export const ScriptText: string = localize('mssql.ui.scriptText', "Script");
export const SelectedText = localize('objectManagement.selectedLabel', "Selected");
export const SelectText = localize('objectManagement.selectLabel', "Select");
export const AddText = localize('objectManagement.addText', "Add…");
export const RemoveText = localize('objectManagement.removeText', "Remove");
export const NoActionScriptedMessage: string = localize('mssql.ui.noActionScriptedMessage', "There is no action to be scripted.");

View File

@@ -163,11 +163,6 @@
"command": "jupyter.reinstallDependencies",
"title": "%title.reinstallNotebookDependencies%"
},
{
"command": "books.sqlserver2019",
"title": "%title.SQL19PreviewBook%",
"category": "%books-preview-category%"
},
{
"command": "books.command.openLocalizedBooks",
"title": "%title.PreviewLocalizedBook%",
@@ -380,10 +375,6 @@
"command": "jupyter.cmd.managePackages",
"when": "false"
},
{
"command": "books.sqlserver2019",
"when": "sqlserver2019"
},
{
"command": "notebook.command.saveBook",
"when": "false"
@@ -542,10 +533,6 @@
"when": "view == bookTreeView",
"group": "navigation"
},
{
"command": "books.sqlserver2019",
"when": "view == providedBooksView"
},
{
"command": "notebook.command.openNotebookFolder",
"when": "view == bookTreeView",

View File

@@ -295,7 +295,8 @@ export function createViewContext(): ViewTestContext {
columns: [] as string[],
onRowSelected: onClick.event,
onCellAction: onClick.event,
appendData: (_data: any[][]) => undefined
appendData: (_data: any[][]) => undefined,
setActiveCell: (_row: number, _column: number) => undefined
});
let tableBuilder: azdata.ComponentBuilder<azdata.TableComponent, azdata.TableComponentProperties> = {
component: () => table(),

View File

@@ -2,7 +2,7 @@
"name": "sql-database-projects",
"displayName": "SQL Database Projects",
"description": "Enables users to develop and publish database schemas for MSSQL Databases",
"version": "1.1.0",
"version": "1.1.1",
"publisher": "Microsoft",
"preview": false,
"engines": {

View File

@@ -20,6 +20,7 @@ import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/t
import { Deferred } from '../common/promise';
import { PublishOptionsDialog } from './publishOptionsDialog';
import { IPublishToDockerSettings, ISqlProjectPublishSettings } from '../models/deploy/publishSettings';
import { PublishProfile } from '../models/publishProfile/publishProfile';
interface DataSourceDropdownValue extends azdataType.CategoryValue {
dataSource: SqlConnectionDataSource;
@@ -69,7 +70,7 @@ export class PublishDatabaseDialog {
public publish: ((proj: Project, profile: ISqlProjectPublishSettings) => any) | undefined;
public publishToContainer: ((proj: Project, profile: IPublishToDockerSettings) => any) | undefined;
public generateScript: ((proj: Project, profile: ISqlProjectPublishSettings) => any) | undefined;
public readPublishProfile: ((profileUri: vscode.Uri) => any) | undefined;
public readPublishProfile: ((profileUri: vscode.Uri) => Promise<PublishProfile>) | undefined;
public savePublishProfile: ((profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map<string, string>, deploymentOptions?: DeploymentOptions) => any) | undefined;
constructor(private project: Project) {
@@ -814,8 +815,8 @@ export class PublishDatabaseDialog {
this.formBuilder?.removeFormItem(<azdataType.FormComponentGroup>this.sqlCmdVariablesFormComponentGroup);
}
for (let key in result.sqlCmdVariables) {
this.sqlCmdVars?.set(key, result.sqlCmdVariableColumn.get(key));
for (let key of result.sqlCmdVariables.keys()) {
this.sqlCmdVars?.set(key, result.sqlCmdVariables.get(key)!);
}
this.updateRevertSqlCmdVarsButtonState();
@@ -983,7 +984,7 @@ export class PublishDatabaseDialog {
/*
* Sets the default deployment options to deployment options model object
*/
public setDeploymentOptions(deploymentOptions: DeploymentOptions): void {
public setDeploymentOptions(deploymentOptions: DeploymentOptions | undefined): void {
this.deploymentOptions = deploymentOptions;
}
}

View File

@@ -159,9 +159,7 @@ export async function getPublishDatabaseSettings(project: ISqlProject, promptFor
}
// 4. Modify sqlcmd vars
// If a publish profile is provided then the values from there will overwrite the ones in the
// project file (if they exist)
let sqlCmdVariables = Object.assign({}, project.sqlCmdVariables, publishProfile?.sqlCmdVariables);
let sqlCmdVariables: Map<string, string> = getInitialSqlCmdVariables(project, publishProfile);
if (sqlCmdVariables.size > 0) {
// Continually loop here, allowing the user to modify SQLCMD variables one
@@ -170,13 +168,14 @@ export async function getPublishDatabaseSettings(project: ISqlProject, promptFor
// as many times as they wish - with an option to reset all the variables
// to their starting values being provided as well.
while (true) {
const quickPickItems = Object.keys(sqlCmdVariables).map(key => {
return {
let quickPickItems = [];
for (const key of sqlCmdVariables.keys()) {
quickPickItems.push({
label: key,
description: sqlCmdVariables.get(key),
key: key
} as vscode.QuickPickItem & { key?: string, isResetAllVars?: boolean, isDone?: boolean };
});
} as vscode.QuickPickItem & { key?: string, isResetAllVars?: boolean, isDone?: boolean })
}
quickPickItems.push({ label: `$(refresh) ${constants.resetAllVars}`, isResetAllVars: true });
quickPickItems.unshift({ label: `$(check) ${constants.done}`, isDone: true });
const sqlCmd = await vscode.window.showQuickPick(
@@ -200,7 +199,7 @@ export async function getPublishDatabaseSettings(project: ISqlProject, promptFor
sqlCmdVariables.set(sqlCmd.key, newValue);
}
} else if (sqlCmd.isResetAllVars) {
sqlCmdVariables = Object.assign({}, project.sqlCmdVariables, publishProfile?.sqlCmdVariables);
sqlCmdVariables = getInitialSqlCmdVariables(project, publishProfile);
} else if (sqlCmd.isDone) {
break;
}
@@ -220,6 +219,24 @@ export async function getPublishDatabaseSettings(project: ISqlProject, promptFor
return settings;
}
/**
* Loads the sqlcmd variables from a sql projects. If a publish profile is provided then the values from there will overwrite the ones in the project file (if they exist)
* @param project
* @param publishProfile
* @returns Map of sqlcmd variables
*/
function getInitialSqlCmdVariables(project: ISqlProject, publishProfile?: PublishProfile): Map<string, string> {
// create a copy of the sqlcmd variable map so that the original ones don't get overwritten
let sqlCmdVariables = new Map(project.sqlCmdVariables);
if (publishProfile?.sqlCmdVariables) {
for (const [key, value] of publishProfile.sqlCmdVariables) {
sqlCmdVariables.set(key, value);
}
}
return sqlCmdVariables;
}
export async function launchPublishTargetOption(project: Project): Promise<constants.PublishTargetType | undefined> {
// Show options to user for deploy to existing server or docker
const target = project.getProjectTargetVersion();

View File

@@ -108,7 +108,7 @@
"tas-client-umd": "0.1.6",
"turndown": "^7.0.0",
"turndown-plugin-gfm": "^1.0.2",
"v8-inspect-profiler": "^0.0.22",
"v8-inspect-profiler": "^0.1.0",
"vscode-oniguruma": "1.6.1",
"vscode-policy-watcher": "^1.1.1",
"vscode-proxy-agent": "^0.12.0",
@@ -145,7 +145,7 @@
"@types/node": "16.x",
"@types/plotly.js": "^1.44.9",
"@types/sanitize-html": "^1.18.2",
"@types/sinon": "^10.0.2",
"@types/sinon": "10.0.2",
"@types/sinon-test": "^2.4.2",
"@types/trusted-types": "^1.0.6",
"@types/vscode-notebook-renderer": "1.60.0",
@@ -187,7 +187,7 @@
"gulp-bom": "^3.0.0",
"gulp-buffer": "0.0.2",
"gulp-concat": "^2.6.1",
"gulp-eslint": "^6.0.0",
"gulp-eslint": "^5.0.0",
"gulp-filter": "^5.1.0",
"gulp-flatmap": "^1.0.2",
"gulp-gunzip": "^1.0.0",
@@ -198,7 +198,6 @@
"gulp-remote-retry-src": "^0.8.0",
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.4",
"gulp-shell": "^0.6.5",
"gulp-sourcemaps": "^3.0.0",
"gulp-svgmin": "^4.1.0",
"gulp-untar": "^0.0.7",
@@ -230,16 +229,16 @@
"rcedit": "^1.1.0",
"request": "^2.85.0",
"rimraf": "^2.2.8",
"sinon": "^11.1.1",
"sinon": "11.1.1",
"sinon-test": "^3.1.3",
"source-map": "0.6.1",
"source-map-support": "^0.3.2",
"style-loader": "^1.0.0",
"style-loader": "^1.3.0",
"temp-write": "^3.4.0",
"ts-loader": "^9.2.7",
"tsec": "0.1.4",
"typemoq": "^0.3.2",
"typescript": "^4.8.0-dev.20220518",
"typescript": "4.8.0-dev.20220719",
"typescript-formatter": "7.1.0",
"underscore": "^1.12.1",
"util": "^0.12.4",

View File

@@ -1797,6 +1797,10 @@ declare module 'azdata' {
}
export interface NodeFilterProperty {
/**
* The non-localized name of the filter property
*/
name: string;
/**
* The name of the filter property displayed to the user
*/
@@ -1818,14 +1822,27 @@ declare module 'azdata' {
/**
* The list of choices for the filter property if the type is choice
*/
choices: string[];
choices: NodeFilterChoicePropertyValue[];
}
export interface NodeFilterChoicePropertyValue {
/**
* The value of the choice
*/
value: string;
/**
* The display name of the choice
* If not specified, the value will be used as the display name
* If specified, the display name will be used in the dropdown
*/
displayName?: string;
}
export interface NodeFilter {
/**
* The name of the filter property
*/
name: string;
displayName: string;
/**
* The operator of the filter property
*/
@@ -1833,7 +1850,7 @@ declare module 'azdata' {
/**
* The applied values of the filter property
*/
value: string | string[] | number | boolean | undefined;
value: string | string[] | number | number[] | boolean | undefined;
}
export enum NodeFilterPropertyDataType {
@@ -1930,4 +1947,11 @@ declare module 'azdata' {
isPrimary: boolean;
}
}
export interface TableComponent {
/**
* Set active cell.
*/
setActiveCell(row: number, column: number): void;
}
}

View File

@@ -41,7 +41,7 @@ export class TableCellEditorFactory {
};
}
public getTextEditorClass(context: any, inputType: 'text' | 'number' = 'text'): any {
public getTextEditorClass(context: any, inputType: 'text' | 'number' | 'date' = 'text', presetValue?: string): any {
const self = this;
class TextEditor extends Disposable {
private _originalValue: string;
@@ -76,6 +76,8 @@ export class TableCellEditorFactory {
this._register(self._options.onStyleChange(() => {
self._options.editorStyler(this._input);
}));
this._input.value = presetValue ?? '';
}
private async commitEdit(): Promise<void> {
@@ -96,11 +98,21 @@ export class TableCellEditorFactory {
public loadValue(item: Slick.SlickData): void {
this._originalValue = self._options.valueGetter(item, this._args.column) ?? '';
this._input.value = this._originalValue;
if (inputType === 'date') {
this._input.inputElement.valueAsDate = new Date(this._originalValue);
} else {
this._input.value = this._originalValue;
}
}
public applyValue(item: Slick.SlickData, state: string): void {
const activeCell = this._args.grid.getActiveCell();
if (inputType === 'date') {
// Usually, the date picker will return the date in the local time zone and change the date to the previous day.
// We need to convert the date to UTC time zone to avoid this behavior so that the date will be the same as the
// date picked in the date picker.
state = new Date(state).toLocaleDateString(window.navigator.language, { timeZone: 'UTC' });
}
self._options.valueSetter(context, activeCell.row, item, this._args.column, state);
}

View File

@@ -97,10 +97,24 @@ export class TestCapabilitiesService implements ICapabilitiesService {
valueType: ServiceOptionType.string
}
];
let mssqlAdvancedOptions: azdata.ConnectionOption[] = [
{
name: 'trustServerCertificate',
displayName: undefined!,
description: undefined!,
groupName: undefined!,
categoryValues: undefined!,
defaultValue: 'false',
isIdentity: false,
isRequired: false,
specialValueType: undefined!,
valueType: ServiceOptionType.boolean
}
];
let msSQLCapabilities = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: connectionProvider,
connectionOptions: connectionProvider.concat(mssqlAdvancedOptions),
};
let pgSQLCapabilities = {
providerId: this.pgsqlProviderName,

View File

@@ -9,7 +9,7 @@ import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfi
import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { AuthenticationType } from 'sql/platform/connection/common/constants';
import { AuthenticationType, mssqlProviderName } from 'sql/platform/connection/common/constants';
import { IConnectionProfile, ProfileMatcher } from 'sql/platform/connection/common/interfaces';
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
import { isDisposable } from 'vs/base/common/lifecycle';
@@ -84,10 +84,28 @@ export class ConnectionStore {
if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem) && !credentialsItem.password) {
const credentialId = this.formatCredentialId(credentialsItem, CRED_PROFILE_USER);
return this.credentialService.readCredential(credentialId)
.then(savedCred => {
if (savedCred) {
.then(async savedCred => {
if (savedCred?.password) {
credentialsItem.password = savedCred.password;
credentialsItem.options['password'] = savedCred.password;
} else if (credentialsItem.providerName === mssqlProviderName) {
// Special handling for MSSQL provider as "applicationName:azdata" is no longer included
// in credential string starting with MAY 2023 release.
// We will try to read credential including applicationName and if it is found,
// we will update the saved credential with new credential key.
// This special case handling should be removed in a future release.
let credParts = credentialId.split('|');
credParts.splice(3, 0, 'applicationName:azdata');
const oldCredentialId = credParts.join('|');
const savedMssqlCred = await this.credentialService.readCredential(oldCredentialId);
if (savedMssqlCred?.password) {
credentialsItem.password = savedMssqlCred.password;
credentialsItem.options['password'] = savedMssqlCred.password;
// Update credential in credential store.
await this.credentialService.deleteCredential(oldCredentialId);
await this.credentialService.saveCredential(credentialId, savedMssqlCred.password);
savedCred.password = savedMssqlCred.password;
}
}
return { profile: credentialsItem, savedCred: !!savedCred };
});

View File

@@ -202,6 +202,27 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
* Example: "providerName:MSSQL|authenticationType:|databaseName:database|serverName:server3|userName:user|group:testid"
*/
public getOptionsKey(): string {
let idNames = this.getOptionKeyIdNames();
idNames = idNames.filter(x => x !== undefined);
//Sort to make sure using names in the same order every time otherwise the ids would be different
idNames.sort();
let idValues: string[] = [];
for (let index = 0; index < idNames.length; index++) {
let value = this.options[idNames[index]!];
value = value ? value : '';
idValues.push(`${idNames[index]}${ProviderConnectionInfo.nameValueSeparator}${value}`);
}
return ProviderConnectionInfo.ProviderPropertyName + ProviderConnectionInfo.nameValueSeparator +
this.providerName + ProviderConnectionInfo.idSeparator + idValues.join(ProviderConnectionInfo.idSeparator);
}
/**
* @returns Array of option key names
*/
public getOptionKeyIdNames(): string[] {
let idNames = [];
if (this.serverCapabilities) {
idNames = this.serverCapabilities.connectionOptions.map(o => {
@@ -217,21 +238,7 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
// This should never happen but just incase the serverCapabilities was not ready at this time
idNames = ['authenticationType', 'database', 'server', 'user'];
}
idNames = idNames.filter(x => x !== undefined);
//Sort to make sure using names in the same order every time otherwise the ids would be different
idNames.sort();
let idValues: string[] = [];
for (let index = 0; index < idNames.length; index++) {
let value = this.options[idNames[index]!];
value = value ? value : '';
idValues.push(`${idNames[index]}${ProviderConnectionInfo.nameValueSeparator}${value}`);
}
return ProviderConnectionInfo.ProviderPropertyName + ProviderConnectionInfo.nameValueSeparator +
this.providerName + ProviderConnectionInfo.idSeparator + idValues.join(ProviderConnectionInfo.idSeparator);
return idNames;
}
public static getProviderFromOptionsKey(optionsKey: string) {

View File

@@ -32,7 +32,8 @@ export enum ComponentEventType {
export enum ModelViewAction {
SelectTab = 'selectTab',
AppendData = 'appendData',
Filter = 'filter'
Filter = 'filter',
SetActiveCell = 'setActiveCell'
}
/**

View File

@@ -50,7 +50,8 @@ export const enum TelemetryView {
ResultsPanel = 'ResultsPanel',
Shell = 'Shell',
SqlAssessment = 'SqlAssessment',
TableDesigner = 'TableDesigner'
TableDesigner = 'TableDesigner',
ObjectExplorer = 'ObjectExplorer'
}
export const enum TelemetryError {
@@ -98,6 +99,8 @@ export const enum TelemetryAction {
MoveServerGroup = 'MoveServerGroup',
NewQuery = 'NewQuery',
ObjectExplorerExpand = 'ObjectExplorerExpand',
ObjectExplorerFilter = 'ObjectExplorerFilter',
ObjectExplorerRemoveFilter = 'ObjectExplorerRemoveFilter',
Open = 'Open',
OpenQuery = 'OpenQuery',
OpenExecutionPlanProperties = 'OpenExecutionPlanProperties',

View File

@@ -1503,6 +1503,10 @@ class TableComponentWrapper extends ComponentWrapper implements azdata.TableComp
public appendData(v: any[][]): Thenable<void> {
return this.doAction(ModelViewAction.AppendData, v);
}
public setActiveCell(row: number, column: number): void {
this.doAction(ModelViewAction.SetActiveCell, row, column);
}
}
class DropDownWrapper extends ComponentWrapper implements azdata.DropDownComponent {

View File

@@ -186,7 +186,8 @@ export enum ModelComponentTypes {
export enum ModelViewAction {
SelectTab = 'selectTab',
AppendData = 'appendData',
Filter = 'filter'
Filter = 'filter',
SetActiveCell = 'setActiveCell'
}
export enum ColumnSizingMode {

View File

@@ -40,6 +40,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { ITableService } from 'sql/workbench/services/table/browser/tableService';
import { deepClone, equals } from 'vs/base/common/objects';
export enum ColumnSizingMode {
ForceFit = 0, // all columns will be sized to fit in viewable space, no horiz scroll bar
@@ -358,24 +359,27 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
}
public override setProperties(properties: { [key: string]: any; }): void {
const oldColumns = deepClone(this.columns);
super.setProperties(properties);
this._tableData.clear();
this._tableData.push(this.transformData(this.data, this.columns));
this._tableColumns = this.transformColumns(this.columns);
this._table.columns = this._tableColumns;
if (!equals(oldColumns, this.columns)) {
this._tableColumns = this.transformColumns(this.columns);
this._table.columns = this._tableColumns;
this._checkboxColumns.forEach((column, columnName) => { this.registerPlugins(columnName, column); })
Object.keys(this._buttonColumns).forEach(col => this.registerPlugins(col, this._buttonColumns[col]));
Object.keys(this._hyperlinkColumns).forEach(col => this.registerPlugins(col, this._hyperlinkColumns[col]));
Object.keys(this._contextMenuColumns).forEach(col => this.registerPlugins(col, this._contextMenuColumns[col]));
this._table.columns = this._tableColumns;
this._table.autosizeColumns();
}
this._table.setData(this._tableData);
this._table.setTableTitle(this.title);
if (this.selectedRows) {
this._table.setSelectedRows(this.selectedRows);
}
this._checkboxColumns.forEach((column, columnName) => {
this.registerPlugins(columnName, column);
})
Object.keys(this._buttonColumns).forEach(col => this.registerPlugins(col, this._buttonColumns[col]));
Object.keys(this._hyperlinkColumns).forEach(col => this.registerPlugins(col, this._hyperlinkColumns[col]));
Object.keys(this._contextMenuColumns).forEach(col => this.registerPlugins(col, this._contextMenuColumns[col]));
if (this.headerFilter === true) {
this.registerFilterPlugin();
this._tableData.clearFilter();
@@ -433,7 +437,8 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
width: col.width,
cssClass: col.cssClass,
headerCssClass: col.headerCssClass,
actionOnCheck: checkboxAction
actionOnCheck: checkboxAction,
columnId: `checkbox-column-${index}`,
}, index));
this._register(this._checkboxColumns.get(col.value).onChange((state) => {
@@ -589,7 +594,6 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
private registerPlugins(col: string, plugin: CheckboxSelectColumn<{}> | ButtonColumn<{}> | HyperlinkColumn<{}> | ContextMenuColumn<{}>): void {
const index = 'index' in plugin ? plugin.index : this.columns?.findIndex(x => x === col || ('value' in x && x['value'] === col));
if (index >= 0) {
this._tableColumns.splice(index, 0, plugin.definition);
@@ -598,10 +602,6 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
this._pluginsRegisterStatus[col] = true;
}
}
this._table.columns = this._tableColumns;
this._table.autosizeColumns();
}
@@ -723,6 +723,10 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
switch (action) {
case ModelViewAction.AppendData:
this.appendData(args[0]);
break;
case ModelViewAction.SetActiveCell:
this._table.grid.setActiveCell(args[0], args[1]);
break;
}
}

View File

@@ -31,17 +31,61 @@ import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/envi
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
export interface SqlArgs {
/**
* Used to determine file paths to be opened with SQL Editor.
* If provided, we connect the given profile to to it.
* More than one files can be passed to connect to provided profile.
*/
_?: string[];
/**
* Provide authenticationType to be used.
* accepted values: AzureMFA, SqlLogin, Integrated, etc.
*/
authenticationType?: string
/**
* Name of database
*/
database?: string;
/**
* Name of server
*/
server?: string;
/**
* User name/email address
*/
user?: string;
/**
* Operation to perform:
* accepted values: connect, openConnectionDialog
*/
command?: string;
/**
* Name of connection provider,
* accepted values: mssql (by default), pgsql, etc.
*/
provider?: string;
aad?: boolean; // deprecated - used by SSMS - authenticationType should be used instead
integrated?: boolean; // deprecated - used by SSMS - authenticationType should be used instead.
/**
* Deprecated - used by SSMS - authenticationType should be used instead
*/
aad?: boolean;
/**
* Deprecated - used by SSMS - authenticationType should be used instead.
*/
integrated?: boolean;
/**
* Whether or not to show dashboard
* accepted values: true, false (by default).
*/
showDashboard?: boolean;
/**
* Supports providing applicationName that will be used for connection profile app name.
*/
applicationName?: string;
/**
* Supports providing advanced connection properties that providers support.
* Value must be a json object containing key-value pairs in format: '{"key1":"value1","key2":"value2",...}'
*/
connectionProperties?: string;
}
//#region decorators
@@ -324,9 +368,33 @@ export class CommandLineWorkbenchContribution implements IWorkbenchContribution,
profile.setOptionValue('applicationName', applicationName);
profile.setOptionValue('databaseDisplayName', profile.databaseName);
profile.setOptionValue('groupId', profile.groupId);
// Set all advanced options
let advancedOptions = this.getAdvancedOptions(args.connectionProperties, profile.getOptionKeyIdNames());
advancedOptions.forEach((v, k) => {
profile.setOptionValue(k, v);
});
return this._connectionManagementService ? this.tryMatchSavedProfile(profile) : profile;
}
private getAdvancedOptions(options: string, idNames: string[]): Map<string, string> {
const ignoredProperties = idNames.concat(['password', 'azureAccountToken']);
let advancedOptionsMap = new Map<string, string>();
if (options) {
try {
// Decode options if they contain any encoded URL characters
options = decodeURI(options);
JSON.parse(options, (k, v) => {
if (!(k in ignoredProperties)) {
advancedOptionsMap.set(k, v);
}
});
} catch (e) {
throw new Error(localize('commandline.propertiesFormatError', 'Advanced connection properties could not be parsed as JSON, error occurred: {0} Received properties value: {1}', e, options));
}
}
return advancedOptionsMap;
}
private tryMatchSavedProfile(profile: ConnectionProfile) {
let match: ConnectionProfile = undefined;
// If we can find a saved mssql provider connection that matches the args, use it

View File

@@ -96,6 +96,7 @@ class TestParsedArgs implements NativeParsedArgs, SqlArgs {
waitMarkerFilePath?: string;
authenticationType?: string;
applicationName?: string;
connectionProperties?: string;
}
suite('commandLineService tests', () => {
@@ -219,6 +220,42 @@ suite('commandLineService tests', () => {
connectionManagementService.verifyAll();
});
test('processCommandLine loads advanced options in args', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const args: TestParsedArgs = new TestParsedArgs();
args.server = 'myserver';
args.database = 'mydatabase';
args.user = 'myuser';
args.authenticationType = Constants.AuthenticationType.SqlLogin;
args.applicationName = 'myapplication';
// Pass advanced connection properties
args.connectionProperties = `{"trustServerCertificate":"true"}`;
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(
p => p.serverName === 'myserver'
&& p.authenticationType === Constants.AuthenticationType.SqlLogin
&& p.options['applicationName'] === 'myapplication-azdata'), 'connection', true))
.returns((conn) => {
originalProfile = conn;
return Promise.resolve('unused');
})
.verifiable(TypeMoq.Times.once());
connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile);
const configurationService = getConfigurationServiceMock(true);
const logService = new NullLogService();
let contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService);
await contribution.processCommandLine(args);
assert.equal(originalProfile.options['applicationName'], 'myapplication-azdata', 'Application Name not received as expected.');
assert.equal(originalProfile.options['trustServerCertificate'], 'true', 'Advanced option not received as expected.');
connectionManagementService.verifyAll();
});
test('processCommandLine invokes a command without a profile parameter when no server is passed', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Loose);
@@ -568,7 +605,5 @@ suite('commandLineService tests', () => {
notificationService.verifyAll();
connectionManagementService.verifyAll();
});
});
});

View File

@@ -27,7 +27,7 @@ export class LinkHandlerDirective {
@Inject(INotebookService) private readonly notebookService: INotebookService,
@Inject(IFileService) private readonly fileService: IFileService
) {
this.workbenchFilePath = URI.parse(require.toUrl('vs/code/electron-browser/workbench/workbench.html'));
this.workbenchFilePath = URI.parse(require.toUrl('vs/code/electron-sandbox/workbench/workbench.html'));
}
@HostListener('click', ['$event'])

View File

@@ -47,6 +47,9 @@ import { ActionRunner } from 'vs/base/common/actions';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { USE_ASYNC_SERVER_TREE_CONFIG } from 'sql/workbench/contrib/objectExplorer/common/serverGroup.contribution';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { FilterDialog } from 'sql/workbench/services/objectExplorer/browser/filterDialog/filterDialog';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
export const CONTEXT_SERVER_TREE_VIEW = new RawContextKey<ServerTreeViewView>('serverTreeView.view', ServerTreeViewView.all);
export const CONTEXT_SERVER_TREE_HAS_CONNECTIONS = new RawContextKey<boolean>('serverTreeView.hasConnections', false);
@@ -78,7 +81,8 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
@IKeybindingService private _keybindingService: IKeybindingService,
@IContextKeyService contextKeyService: IContextKeyService,
@IHostService private _hostService: IHostService,
@INotificationService private _notificationService: INotificationService
@INotificationService private _notificationService: INotificationService,
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService
) {
super();
this._hasConnectionsKey = CONTEXT_SERVER_TREE_HAS_CONNECTIONS.bindTo(contextKeyService);
@@ -588,6 +592,50 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
}
}
public async filterElementChildren(node: TreeNode): Promise<void> {
await FilterDialog.getFiltersForProperties(
node.filterProperties,
localize('objectExplorer.filterDialogTitle', "(Preview) Filter Settings: {0}", node.getConnectionProfile().title),
localize('objectExplorer.nodePath', "Node Path: {0}", node.nodePath),
node.filters,
async (filters) => {
let errorListener;
try {
let expansionError = undefined;
errorListener = this._objectExplorerService.onUpdateObjectExplorerNodes(e => {
if (e.errorMessage) {
expansionError = e.errorMessage;
}
errorListener.dispose();
});
node.forceRefresh = true;
node.filters = filters || [];
if (this._tree instanceof AsyncServerTree) {
await this._tree.rerender(node);
}
await this.refreshElement(node);
await this._tree.expand(node);
if (expansionError) {
throw new Error(expansionError);
}
} finally {
if (errorListener) {
errorListener.dispose();
}
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.ObjectExplorer, TelemetryKeys.TelemetryAction.ObjectExplorerFilter)
.withAdditionalProperties({
filterPropertyNames: JSON.stringify(filters.map(f => node.filterProperties.find(p => f.displayName === p.displayName)?.name)),
filterCount: filters.length,
objectType: node.objectType
}).send();
}
return;
},
this._instantiationService
);
}
/**
* Filter connections based on view (recent/active)
*/

View File

@@ -49,7 +49,7 @@ const serverTreeConfig: IConfigurationNode = {
'properties': {
'serverTree.useAsyncServerTree': {
'type': 'boolean',
'default': true,
'default': false,
'description': localize('serverTree.useAsyncServerTree', "Use the new async server tree for the Servers view and Connection Dialog with support for new features such as dynamic node filtering. Requires a restart to take effect.")
}
}

View File

@@ -39,7 +39,7 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
);
mockConnectionManagementService.setup(x => x.getConnectionGroups()).returns(x => []);
mockConnectionManagementService.setup(x => x.hasRegisteredServers()).returns(() => true);
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, new TestConfigurationService(), capabilitiesService, undefined, undefined, new MockContextKeyService(), undefined, undefined);
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, new TestConfigurationService(), capabilitiesService, undefined, undefined, new MockContextKeyService(), undefined, undefined, undefined);
mockTree = TypeMoq.Mock.ofType<ITree>(TestTree);
(serverTreeView as any)._tree = mockTree.object;
mockRefreshTreeMethod = TypeMoq.Mock.ofType(Function);

View File

@@ -21,6 +21,8 @@ import { ILogService } from 'vs/platform/log/common/log';
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
import { SqlIconId } from 'sql/base/common/codicons';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
export interface IServerView {
showFilteredTree(filter: string): void;
@@ -310,3 +312,64 @@ export class DeleteConnectionAction extends Action {
}
}
}
export class FilterChildrenAction extends Action {
public static ID = 'objectExplorer.filterChildren';
public static LABEL = localize('objectExplorer.filterChildren', "Filter (Preview)");
constructor(
id: string,
label: string,
private _node: TreeNode,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService) {
super(id, label);
}
public override async run(): Promise<void> {
await this._objectExplorerService.getServerTreeView().filterElementChildren(this._node);
}
}
export class RemoveFilterAction extends Action {
public static ID = 'objectExplorer.removeFilter';
public static LABEL = localize('objectExplorer.removeFilter', "Remove Filter");
constructor(
id: string,
label: string,
private _node: TreeNode,
private _tree: AsyncServerTree | ITree,
private _profile: ConnectionProfile | undefined,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService
) {
super(id, label);
}
public override async run(): Promise<void> {
let node = this._node;
let nodeToRefresh: ServerTreeElement = this._node;
if (this._profile) {
node = this._objectExplorerService.getObjectExplorerNode(this._profile);
nodeToRefresh = this._profile;
}
node.filters = [];
if (nodeToRefresh instanceof TreeNode) {
nodeToRefresh.forceRefresh = true;
}
if (this._tree instanceof AsyncServerTree) {
await this._tree.rerender(nodeToRefresh);
await this._tree.updateChildren(nodeToRefresh);
await this._tree.expand(nodeToRefresh);
} else {
await this._tree.refresh(nodeToRefresh);
await this._tree.expand(nodeToRefresh);
}
this._telemetryService.createActionEvent(
TelemetryKeys.TelemetryView.ObjectExplorer,
TelemetryKeys.TelemetryAction.ObjectExplorerRemoveFilter
).withAdditionalProperties({
objectType: node.objectType
}).send();
}
}

View File

@@ -0,0 +1,726 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./../media/filterDialog';
import { Button } from 'sql/base/browser/ui/button/button';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import { Modal } from 'sql/workbench/browser/modal/modal'
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { ILogService } from 'vs/platform/log/common/log';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { localize } from 'vs/nls';
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import * as DOM from 'vs/base/browser/dom';
import * as azdata from 'azdata';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { NodeFilterPropertyDataType, NodeFilterOperator } from 'sql/workbench/api/common/sqlExtHostTypes';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { Table } from 'sql/base/browser/ui/table/table';
import { TableCellEditorFactory } from 'sql/base/browser/ui/table/tableCellEditorFactory';
import { Emitter } from 'vs/base/common/event';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { TableHeaderRowHeight, TableRowHeight } from 'sql/workbench/browser/designer/designerTableUtil';
import { textFormatter } from 'sql/base/browser/ui/table/formatters';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/browser/dropdown';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { TabbedPanel } from 'sql/base/browser/ui/panel/panel';
import { attachTableStyler } from 'sql/platform/theme/common/styler';
import { ButtonColumn } from 'sql/base/browser/ui/table/plugins/buttonColumn.plugin';
import Severity from 'vs/base/common/severity';
import { status } from 'vs/base/browser/ui/aria/aria';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// strings for filter dialog
const OkButtonText = localize('objectExplorer.okButtonText', "OK");
const CancelButtonText = localize('objectExplorer.cancelButtonText', "Cancel");
const ClearAllButtonText = localize('objectExplorer.clearAllButtonText', "Clear All");
const TitleIconClass: string = 'icon filterLabel';
// strings for filter operator select box
const EQUALS_SELECT_BOX = localize('objectExplorer.equalsSelectBox', "Equals");
const NOT_EQUALS_SELECT_BOX = localize('objectExplorer.notEqualsSelectBox', "Not Equals");
const LESS_THAN_SELECT_BOX = localize('objectExplorer.lessThanSelectBox', "Less Than");
const LESS_THAN_OR_EQUALS_SELECT_BOX = localize('objectExplorer.lessThanOrEqualsSelectBox', "Less Than Or Equals");
const GREATER_THAN_SELECT_BOX = localize('objectExplorer.greaterThanSelectBox', "Greater Than");
const GREATER_THAN_OR_EQUALS_SELECT_BOX = localize('objectExplorer.greaterThanOrEqualsSelectBox', "Greater Than Or Equals");
const BETWEEN_SELECT_BOX = localize('objectExplorer.betweenSelectBox', "Between");
const NOT_BETWEEN_SELECT_BOX = localize('objectExplorer.notBetweenSelectBox', "Not Between");
const CONTAINS_SELECT_BOX = localize('objectExplorer.containsSelectBox', "Contains");
const NOT_CONTAINS_SELECT_BOX = localize('objectExplorer.notContainsSelectBox', "Not Contains");
const AND_SELECT_BOX = localize('objectExplorer.andSelectBox', "And");
const IS_NULL_SELECT_BOX = localize('objectExplorer.isNullSelectBox', "Is Null");
const IS_NOT_NULL_SELECT_BOX = localize('objectExplorer.isNotNullSelectBox', "Is Not Null");
// strings for filter table column headers
const PROPERTY_NAME_COLUMN_HEADER = localize('objectExplorer.propertyNameColumnHeader', "Property");
const OPERATOR_COLUMN_HEADER = localize('objectExplorer.operatorColumnHeader', "Operator");
const VALUE_COLUMN_HEADER = localize('objectExplorer.valueColumnHeader', "Value");
const CLEAR_COLUMN_HEADER = localize('objectExplorer.clearColumnHeader', "Clear");
// strings for value select box for boolean type filters
const TRUE_SELECT_BOX = localize('objectExplorer.trueSelectBox', "True");
const FALSE_SELECT_BOX = localize('objectExplorer.falseSelectBox', "False");
function nodePathDisplayString(nodepath: string): string { return localize('objectExplorer.nodePath', "Node Path: {0}", nodepath) }
const PROPERTY_COLUMN_ID = 'property';
const OPERATOR_COLUMN_ID = 'operator';
const VALUE_COLUMN_ID = 'value';
const CLEAR_COLUMN_ID = 'clear';
export class FilterDialog extends Modal {
private _okButton?: Button;
private _cancelButton?: Button;
private _clearAllButton?: Button;
private filterTable: Table<Slick.SlickData>;
private _tableCellEditorFactory: TableCellEditorFactory;
private _onStyleChangeEventEmitter = new Emitter<void>();
private _description: HTMLElement;
private _onFilterApplied = new Emitter<azdata.NodeFilter[]>();
public readonly onFilterApplied = this._onFilterApplied.event;
private _onCloseEvent = new Emitter<void>();
public readonly onDialogClose = this._onCloseEvent.event;
constructor(
private _properties: azdata.NodeFilterProperty[],
private _filterDialogTitle: string,
private _filterDialogSubtitle: string,
private _appliedFilters: azdata.NodeFilter[],
private applyFilterAction: (filters: azdata.NodeFilter[]) => Promise<void> | undefined,
@IThemeService themeService: IThemeService,
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
@ILayoutService layoutService: ILayoutService,
@IClipboardService clipboardService: IClipboardService,
@ILogService logService: ILogService,
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
@IContextKeyService contextKeyService: IContextKeyService,
@IContextViewService private readonly _contextViewProvider: IContextViewService,
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
@IQuickInputService private readonly _quickInputService: IQuickInputService,
@IErrorMessageService private _errorMessageService: IErrorMessageService,
) {
super(
'ObjectExplorerServiceDialog',
'Object Explorer Service Dialog',
telemetryService,
layoutService,
clipboardService,
themeService,
logService,
textResourcePropertiesService,
contextKeyService,
{
dialogStyle: 'normal',
hasTitleIcon: true,
hasSpinner: true
}
);
}
public open(): void {
this.render();
this.show();
this._okButton.focus();
}
public override render() {
super.render();
this.title = this._filterDialogTitle;
this.titleIconClassName = TitleIconClass;
this._register(attachModalDialogStyler(this, this._themeService));
this._okButton = this.addFooterButton(OkButtonText, async () => { await this.onApply() });
this._cancelButton = this.addFooterButton(CancelButtonText, () => { this.onClose() });
this._clearAllButton = this.addFooterButton(ClearAllButtonText, () => { this.onClearAll() }, 'left', true);
this._register(attachButtonStyler(this._okButton, this._themeService));
this._register(attachButtonStyler(this._cancelButton, this._themeService));
this._register(attachButtonStyler(this._clearAllButton, this._themeService));
}
protected renderBody(container: HTMLElement): void {
const body = DOM.append(container, DOM.$('.filter-dialog-body'));
const subtitle = DOM.append(body, DOM.$('.filter-dialog-node-path'));
subtitle.innerText = nodePathDisplayString(this._filterDialogSubtitle);
const clauseTableContainer = DOM.append(body, DOM.$('.filter-table-container'));
const filter = DOM.append(clauseTableContainer, DOM.$('.filter-table'));
this._tableCellEditorFactory = new TableCellEditorFactory(
{
valueGetter: (item, column): string => {
// if the operator is And and the operator is date, we need to get the date from the previous
// row to make it more user friendly for the user to enter the next value.
if (column.field === VALUE_COLUMN_ID && item[OPERATOR_COLUMN_ID].value === AND_SELECT_BOX) {
const index = item.filterPropertyIndex;
const tableData = this.filterTable.getData().getItems();
if (this._properties[index].type === NodeFilterPropertyDataType.Date) {
let value1 = '';
for (let i = 0; i < tableData.length; i++) {
if (tableData[i].filterPropertyIndex === index) {
value1 = tableData[i].value.value;
break;
}
}
const value2 = item[column.field].value;
return value2 === '' ? value1 : value2;
}
}
return item[column.field].value;
},
valueSetter: (context: any, row: number, item: any, column: Slick.Column<Slick.SlickData>, value: string): void => {
item[column.field].value = value;
if (column.field === 'operator') {
const index = item.filterPropertyIndex;
const nodeOperator = this._properties[index].type;
if (nodeOperator === NodeFilterPropertyDataType.Date || nodeOperator === NodeFilterPropertyDataType.Number) {
if (value === BETWEEN_SELECT_BOX || value === NOT_BETWEEN_SELECT_BOX) {
const tableData = this.filterTable.getData().getItems();
if (tableData.length > row + 1) {
if (tableData[row + 1].operator.value === AND_SELECT_BOX) {
return;
}
}
const newRow: Slick.SlickData = {
property: {
value: ''
},
operator: {
value: AND_SELECT_BOX,
values: [AND_SELECT_BOX]
},
value: {
value: '',
values: []
},
filterPropertyIndex: tableData[row].filterPropertyIndex
};
const activeElement = this.filterTable.activeCell;
tableData.splice(row + 1, 0, newRow);
dataProvider.clear();
dataProvider.push(tableData);
this.filterTable.rerenderGrid();
this.filterTable.layout(new DOM.Dimension(600, (dataProvider.getItems().length + 2) * TableRowHeight));
this.filterTable.setActiveCell(activeElement.row, activeElement.cell);
} else {
const tableData = this.filterTable.getData().getItems();
if (tableData.length > row + 1) {
if (tableData[row + 1].operator.value === AND_SELECT_BOX) {
const activeElement = this.filterTable.activeCell;
tableData.splice(row + 1, 1);
dataProvider.clear();
dataProvider.push(tableData);
this.filterTable.rerenderGrid();
this.filterTable.layout(new DOM.Dimension(600, (dataProvider.getItems().length + 2) * TableRowHeight));
this.filterTable.setActiveCell(activeElement.row, activeElement.cell);
}
}
}
}
}
},
optionsGetter: (item, column): string[] => {
return item[column.field].values;
},
editorStyler: (component) => {
this.styleComponent(component);
},
onStyleChange: this._onStyleChangeEventEmitter.event
}, this._contextViewProvider
);
const columns: Slick.Column<Slick.SlickData>[] = [
{
id: PROPERTY_COLUMN_ID,
name: PROPERTY_NAME_COLUMN_HEADER,
field: PROPERTY_COLUMN_ID,
formatter: textFormatter,
width: 180,
},
{
id: OPERATOR_COLUMN_ID,
name: OPERATOR_COLUMN_HEADER,
editor: this._tableCellEditorFactory.getDropdownEditorClass(this, [], false),
field: OPERATOR_COLUMN_ID,
formatter: textFormatter,
width: 180
},
{
id: VALUE_COLUMN_ID,
name: VALUE_COLUMN_HEADER,
width: 180,
formatter: textFormatter,
field: VALUE_COLUMN_ID
}
];
const clearValueColumn = new ButtonColumn({
id: CLEAR_COLUMN_ID,
iconCssClass: 'icon erase',
name: CLEAR_COLUMN_HEADER,
title: CLEAR_COLUMN_HEADER,
width: 60,
resizable: true,
isFontIcon: true
});
this._register(clearValueColumn.onClick(e => {
const row = e.row;
const data = this.filterTable.getData().getItems();
data[row][VALUE_COLUMN_ID].value = '';
dataProvider.clear();
dataProvider.push(data);
this.filterTable.rerenderGrid();
}));
columns.push(clearValueColumn.definition);
const tableData: Slick.SlickData[] = [];
if (!this._appliedFilters) {
this._appliedFilters = [];
}
this._properties.forEach((f, i) => {
const appliedFilter = this._appliedFilters.find(filter => filter.displayName === f.displayName);
const filterOperators = this.getOperatorsForType(f.type);
const row: Slick.SlickData = {
property: {
value: f.displayName
},
operator: {
value: appliedFilter ? this.getFilterOperatorString(appliedFilter.operator) : filterOperators[0],
values: filterOperators
},
value: {
value: appliedFilter ? this.getStringValueForFilter(f, appliedFilter.value) : '',
values: this.getChoiceValuesForFilterProperties(f)
},
filterPropertyIndex: i
};
tableData.push(row);
if (appliedFilter?.operator === NodeFilterOperator.Between || appliedFilter?.operator === NodeFilterOperator.NotBetween) {
row.value.value = this.getStringValueForFilter(f, appliedFilter.value[0]);
const andRow: Slick.SlickData = {
property: {
value: ''
},
operator: {
value: AND_SELECT_BOX,
values: [AND_SELECT_BOX]
},
value: {
value: this.getStringValueForFilter(f, appliedFilter.value[1]),
values: []
},
datatype: f.type,
filterPropertyIndex: i
};
tableData.push(andRow);
}
});
const dataProvider = new TableDataView<Slick.SlickData>();
dataProvider.push(tableData);
// Sets up the editor for the value column
(<any>dataProvider).getItemMetadata = (row: number) => {
const rowData = dataProvider.getItem(row);
const filterProperty = this._properties[rowData.filterPropertyIndex];
let editor;
if (rowData.operator.value === AND_SELECT_BOX) {
if (filterProperty.type === NodeFilterPropertyDataType.Number) {
editor = this._tableCellEditorFactory.getTextEditorClass(this, 'number');
} else if (filterProperty.type === NodeFilterPropertyDataType.Date) {
editor = this._tableCellEditorFactory.getTextEditorClass(this, 'date');
}
} else {
if (filterProperty.type === NodeFilterPropertyDataType.String) {
editor = this._tableCellEditorFactory.getTextEditorClass(this, 'text');
} else if (filterProperty.type === NodeFilterPropertyDataType.Date) {
editor = this._tableCellEditorFactory.getTextEditorClass(this, 'date');
} else if (filterProperty.type === NodeFilterPropertyDataType.Boolean) {
editor = this._tableCellEditorFactory.getDropdownEditorClass(this, [TRUE_SELECT_BOX, FALSE_SELECT_BOX], false);
} else if (filterProperty.type === NodeFilterPropertyDataType.Number) {
editor = this._tableCellEditorFactory.getTextEditorClass(this, 'number');
} else if (filterProperty.type === NodeFilterPropertyDataType.Choice) {
editor = this._tableCellEditorFactory.getDropdownEditorClass(this, this.getDropdownOptionsForChoiceProperty(<azdata.NodeFilterChoiceProperty>filterProperty), false);
}
}
return {
columns: {
value: {
editor: editor
}
}
};
}
this.filterTable = new Table(filter, this._accessibilityService, this._quickInputService, {
dataProvider: dataProvider!,
columns: columns,
}, {
editable: true,
autoEdit: true,
dataItemColumnValueExtractor: (data: any, column: Slick.Column<Slick.SlickData>): string => {
if (column.field) {
return data[column.field]?.value;
} else {
return undefined;
}
},
rowHeight: TableRowHeight,
headerRowHeight: TableHeaderRowHeight,
editorLock: new Slick.EditorLock(),
autoHeight: true,
});
this.filterTable.grid.onActiveCellChanged.subscribe((e, any) => {
if (this.filterTable.grid.getActiveCell()) {
const row = this.filterTable.grid.getActiveCell().row;
const data = this.filterTable.getData().getItems()[row];
let index = data.filterPropertyIndex;
const filterPropertyDescription = this._properties[index].description;
this._description.innerText = filterPropertyDescription;
// Announcing the filter property description for screen reader users
status(filterPropertyDescription);
}
});
this.filterTable.registerPlugin(clearValueColumn);
this.filterTable.layout(new DOM.Dimension(600, (tableData.length + 2) * TableRowHeight));
this._register(attachTableStyler(this.filterTable, this._themeService));
this._description = DOM.append(body, DOM.$('.filter-dialog-description'));
this._description.innerHTML = this._properties[0].description;
}
protected layout(height?: number): void {
// noop
}
protected override onClose() {
this.hide('close');
this._onCloseEvent.fire();
}
protected onClearAll() {
const tableAllData = this.filterTable.getData().getItems();
tableAllData.forEach((row) => {
row.value.value = '';
});
this.filterTable.rerenderGrid();
}
// This method is called when the ok button is pressed
private async onApply(): Promise<void> {
const tableData = this.filterTable.getData().getItems();
this._appliedFilters = [];
for (let i = 0; i < tableData.length; i++) {
const row = tableData[i];
let filterProperty = this._properties[row.filterPropertyIndex]
let filter: azdata.NodeFilter = {
displayName: row.property.value,
operator: this.getFilterOperatorEnum(row.operator.value),
value: this.getFilterValue(filterProperty.type, row.value.value, filterProperty),
};
const isMultipleValueFilter = filter.operator === NodeFilterOperator.Between || filter.operator === NodeFilterOperator.NotBetween;
if (isMultipleValueFilter) {
i++;
const row2 = tableData[i];
var value1 = this.getFilterValue(filterProperty.type, row.value.value, filterProperty);
var value2 = this.getFilterValue(filterProperty.type, row2.value.value, filterProperty);
filter.value = <string[] | number[]>[value1, value2];
if (filterProperty.type === NodeFilterPropertyDataType.Date) {
if (filter.value[0] === '' && filter.value[1] !== '') {
// start date not specified.
this._errorMessageService.showDialog(Severity.Error, '', localize('filterDialog.errorStartDate', "Start date is not specified."));
return;
} else if (filter.value[0] !== '' && filter.value[1] === '') {
// end date not specified.
this._errorMessageService.showDialog(Severity.Error, '', localize('filterDialog.errorEndDate', "End date is not specified."));
return;
} else if (new Date(filter.value[0]) > new Date(filter.value[1])) {
// start date is greater than end date.
this._errorMessageService.showDialog(Severity.Error, '', localize('filterDialog.errorDateRange', "Start date cannot be greater than end date."));
return;
}
} else if (filterProperty.type === NodeFilterPropertyDataType.Number) {
if (filter.value[0] === '' && filter.value[1] !== '') {
// start number not specified.
this._errorMessageService.showDialog(Severity.Error, '', localize('filterDialog.errorStartNumber', "Start number is not specified."));
return;
} else if (filter.value[0] !== '' && filter.value[1] === '') {
// end number not specified.
this._errorMessageService.showDialog(Severity.Error, '', localize('filterDialog.errorEndNumber', "End number is not specified."));
return;
} else if (Number(filter.value[0]) > Number(filter.value[1])) {
// start number is greater than end number.
this._errorMessageService.showDialog(Severity.Error, '', localize('filterDialog.errorNumberRange', "Start number cannot be greater than end number."));
return;
}
}
if (value1 !== '' && value2 !== '') {
this._appliedFilters.push(filter);
}
} else {
if (filter.value !== '') {
this._appliedFilters.push(filter);
}
}
}
this.spinner = true;
try {
if (this.applyFilterAction) {
await this.applyFilterAction(this._appliedFilters);
}
this._onFilterApplied.fire(this._appliedFilters);
this.hide('ok');
}
catch (e) {
this.spinner = false;
throw e;
}
}
// This method is called by modal when the enter button is pressed
// We override it to do nothing so that the enter button doesn't close the dialog
protected override async onAccept() {
// noop
}
private getFilterValue(
filterType: NodeFilterPropertyDataType,
value: string,
filterProperty: azdata.NodeFilterProperty
): string | number | boolean {
if (value === '') {
return '';
}
switch (filterType) {
case NodeFilterPropertyDataType.Boolean:
if (value === TRUE_SELECT_BOX) {
return true;
} else if (value === FALSE_SELECT_BOX) {
return false;
}
case NodeFilterPropertyDataType.Number:
return Number(value);
case NodeFilterPropertyDataType.Choice:
const choice = ((<azdata.NodeFilterChoiceProperty>filterProperty).choices.find(c => c.displayName === value));
if (choice) {
return choice.value;
} else {
return value;
}
case NodeFilterPropertyDataType.Date:
case NodeFilterPropertyDataType.String:
return value;
}
}
private getStringValueForFilter(filter: azdata.NodeFilterProperty, value: string | number | boolean | number[] | string[]): string {
switch (filter.type) {
case NodeFilterPropertyDataType.Boolean:
if (value === true) {
return TRUE_SELECT_BOX;
} else if (value === false) {
return FALSE_SELECT_BOX;
}
break;
case NodeFilterPropertyDataType.Number:
return value.toString();
case NodeFilterPropertyDataType.Choice:
return (<azdata.NodeFilterChoiceProperty>filter).choices.find(c => c.value === value).displayName;
case NodeFilterPropertyDataType.Date:
case NodeFilterPropertyDataType.String:
return value as string;
}
return '';
}
private getOperatorsForType(type: NodeFilterPropertyDataType): string[] {
switch (type) {
case NodeFilterPropertyDataType.String:
return [
CONTAINS_SELECT_BOX,
NOT_CONTAINS_SELECT_BOX,
EQUALS_SELECT_BOX,
NOT_EQUALS_SELECT_BOX
];
case NodeFilterPropertyDataType.Number:
return [
EQUALS_SELECT_BOX,
NOT_EQUALS_SELECT_BOX,
GREATER_THAN_SELECT_BOX,
GREATER_THAN_OR_EQUALS_SELECT_BOX,
LESS_THAN_SELECT_BOX,
LESS_THAN_OR_EQUALS_SELECT_BOX,
BETWEEN_SELECT_BOX,
NOT_BETWEEN_SELECT_BOX
];
case NodeFilterPropertyDataType.Boolean:
return [
EQUALS_SELECT_BOX,
NOT_EQUALS_SELECT_BOX
];
case NodeFilterPropertyDataType.Choice:
return [
EQUALS_SELECT_BOX,
NOT_EQUALS_SELECT_BOX
];
case NodeFilterPropertyDataType.Date:
return [
EQUALS_SELECT_BOX,
NOT_EQUALS_SELECT_BOX,
GREATER_THAN_SELECT_BOX,
GREATER_THAN_OR_EQUALS_SELECT_BOX,
LESS_THAN_SELECT_BOX,
LESS_THAN_OR_EQUALS_SELECT_BOX,
BETWEEN_SELECT_BOX,
NOT_BETWEEN_SELECT_BOX
];
}
}
private getFilterOperatorString(operator: NodeFilterOperator): string {
switch (operator) {
case NodeFilterOperator.Contains:
return CONTAINS_SELECT_BOX;
case NodeFilterOperator.NotContains:
return NOT_CONTAINS_SELECT_BOX;
case NodeFilterOperator.Equals:
return EQUALS_SELECT_BOX;
case NodeFilterOperator.NotEquals:
return NOT_EQUALS_SELECT_BOX;
case NodeFilterOperator.GreaterThan:
return GREATER_THAN_SELECT_BOX;
case NodeFilterOperator.GreaterThanOrEquals:
return GREATER_THAN_OR_EQUALS_SELECT_BOX;
case NodeFilterOperator.LessThan:
return LESS_THAN_SELECT_BOX;
case NodeFilterOperator.LessThanOrEquals:
return LESS_THAN_OR_EQUALS_SELECT_BOX;
case NodeFilterOperator.Between:
return BETWEEN_SELECT_BOX;
case NodeFilterOperator.NotBetween:
return NOT_BETWEEN_SELECT_BOX;
case NodeFilterOperator.IsNull:
return IS_NULL_SELECT_BOX;
case NodeFilterOperator.IsNotNull:
return IS_NOT_NULL_SELECT_BOX;
default:
return '';
}
}
private getFilterOperatorEnum(operator: string): NodeFilterOperator {
switch (operator) {
case CONTAINS_SELECT_BOX:
return NodeFilterOperator.Contains;
case NOT_CONTAINS_SELECT_BOX:
return NodeFilterOperator.NotContains;
case EQUALS_SELECT_BOX:
return NodeFilterOperator.Equals;
case NOT_EQUALS_SELECT_BOX:
return NodeFilterOperator.NotEquals;
case GREATER_THAN_SELECT_BOX:
return NodeFilterOperator.GreaterThan;
case GREATER_THAN_OR_EQUALS_SELECT_BOX:
return NodeFilterOperator.GreaterThanOrEquals;
case LESS_THAN_SELECT_BOX:
return NodeFilterOperator.LessThan;
case LESS_THAN_OR_EQUALS_SELECT_BOX:
return NodeFilterOperator.LessThanOrEquals;
case BETWEEN_SELECT_BOX:
return NodeFilterOperator.Between;
case NOT_BETWEEN_SELECT_BOX:
return NodeFilterOperator.NotBetween;
case TRUE_SELECT_BOX:
return NodeFilterOperator.Equals;
case FALSE_SELECT_BOX:
return NodeFilterOperator.NotEquals;
default:
return undefined;
}
}
private getChoiceValuesForFilterProperties(f: azdata.NodeFilterProperty): string[] {
switch (f.type) {
case NodeFilterPropertyDataType.Boolean:
return ['', TRUE_SELECT_BOX, FALSE_SELECT_BOX];
case NodeFilterPropertyDataType.Choice:
return ['', ...this.getDropdownOptionsForChoiceProperty(<azdata.NodeFilterChoiceProperty>f)];
default:
return [];
}
}
private getDropdownOptionsForChoiceProperty(f: azdata.NodeFilterChoiceProperty): string[] {
return f.choices.map(choice => {
return choice.displayName ?? choice.value;
});
}
private styleComponent(component: TabbedPanel | InputBox | Checkbox | Table<Slick.SlickData> | SelectBox | Button | Dropdown): void {
if (component instanceof InputBox) {
this._register(attachInputBoxStyler(component, this._themeService));
} else if (component instanceof SelectBox) {
this._register(attachSelectBoxStyler(component, this._themeService));
} else if (component instanceof Table) {
this._register(attachTableStyler(component, this._themeService));
}
}
/**
* This method is used to let user apply filters on the given filters properties.
* @param properties Properties on which user can apply filters.
* @param filterDialogTitle Title of the filter dialog.
* @param filterDialogSubtile Subtitle of the filter dialog.
* @param appliedFilters Filters that are already applied so that we can prepopulate the filter dialog values.
* @param applyFilterAction Action to be performed when user clicks on apply button. We should pass this so that we can handle the spinner and error message within the dialog.
* @param instantiationService Instantiation service to create the filter dialog.
* @returns
*/
public static async getFiltersForProperties(
properties: azdata.NodeFilterProperty[],
filterDialogTitle: string,
filterDialogSubtile: string,
appliedFilters: azdata.NodeFilter[] | undefined,
applyFilterAction: (filters: azdata.NodeFilter[]) => Promise<void> | undefined,
instantiationService: IInstantiationService,
): Promise<azdata.NodeFilter[]> {
const dialog = instantiationService.createInstance(FilterDialog, properties, filterDialogTitle, filterDialogSubtile, appliedFilters, applyFilterAction);
dialog.open();
return new Promise<azdata.NodeFilter[]>((resolve, reject) => {
dialog.onFilterApplied(filters => {
resolve(filters);
});
dialog.onDialogClose(() => {
reject();
});
});
}
}

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048">
<path fill="#fff" d="M1115 1792h421v128H453L50 1516q-24-24-37-56t-13-68q0-35 13-67t38-58L1248 69l794 795-927 928zm133-1542L538 960l614 613 709-709-613-614zM933 1792l128-128-613-614-306 307q-14 14-14 35t14 35l364 365h427z" />
</svg>

After

Width:  |  Height:  |  Size: 299 B

Some files were not shown because too many files have changed in this diff Show More