Dev/sda/adds prettier updates eslint (#2311)

chore: Updates linter
- Eslint has been updated
- Prettier has been added
- Code has been updated using new rules
- Fixes rule suggestions
This commit is contained in:
Dmitry Shilov 2024-09-16 09:52:51 +02:00 коммит произвёл GitHub
Родитель 519ed5e697
Коммит 4ca6fd6de0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
217 изменённых файлов: 8568 добавлений и 9671 удалений

Просмотреть файл

@ -2,3 +2,5 @@ gulpfile.ts
.eslintrc.js
**/*.d.ts
test/
webpack.config.js
webpack.config.*.js

Просмотреть файл

@ -1,8 +1,43 @@
module.exports = {
"extends": "@microsoft/eslint-config-azuretools",
"rules": {
"import/no-internal-modules": [ "error", {
"allow": [ "antlr4ts/**" ]
} ]
}
env: {
es6: true,
node: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'import'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
rules: {
'@typescript-eslint/no-restricted-types': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/prefer-regexp-exec': 'off',
'@typescript-eslint/require-await': 'warn',
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/unbound-method': 'warn',
eqeqeq: ['error', 'always'],
'import/consistent-type-specifier-style': ['error', 'prefer-inline'],
'import/no-internal-modules': [
'error',
{
allow: ['antlr4ts/**', 'yaml/types'],
},
],
'no-case-declarations': 'error',
'no-constant-condition': 'error',
'no-inner-declarations': 'error',
'no-restricted-imports': ['error', { patterns: ['**/*/extension.bundle'] }],
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'no-useless-escape': 'error',
},
};

12
.prettierrc Normal file
Просмотреть файл

@ -0,0 +1,12 @@
{
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": true,
"arrowParens": "always",
"plugins": ["prettier-plugin-organize-imports"]
}

Просмотреть файл

@ -13,16 +13,18 @@ declare let exports: { [key: string]: unknown };
async function prepareForWebpack(): Promise<void> {
const mainJsPath: string = path.join(__dirname, 'main.js');
let contents: string = (await fse.readFile(mainJsPath)).toString();
contents = contents
.replace('out/src/extension', 'dist/extension.bundle')
.replace(', true /* ignoreBundle */', '');
contents = contents.replace('out/src/extension', 'dist/extension.bundle').replace(', true /* ignoreBundle */', '');
await fse.writeFile(mainJsPath, contents);
}
async function cleanReadme(): Promise<void> {
const readmePath: string = path.join(__dirname, 'README.md');
let data: string = (await fse.readFile(readmePath)).toString();
data = data.replace(/<!-- region exclude-from-marketplace -->.*?<!-- endregion exclude-from-marketplace -->/gis, '');
data = data.replace(
// @ts-ignore
/<!-- region exclude-from-marketplace -->.*?<!-- endregion exclude-from-marketplace -->/gis,
'',
);
await fse.writeFile(readmePath, data);
}

8043
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1086,8 +1086,8 @@
"compile": "tsc -watch",
"package": "vsce package --githubBranch main",
"package-local": "vsce package",
"lint": "eslint --ext .ts .",
"lint-fix": "eslint --ext .ts . --fix",
"lint": "eslint --quiet --ext .ts --ext .tsx .",
"lint-fix": "eslint --ext .ts --ext .tsx . --fix",
"pretest": "npm run webpack-prod",
"test": "node ./out/test/runTest.js",
"unittest": "mocha ./out/test/unit/**/*.js",
@ -1100,7 +1100,6 @@
},
"devDependencies": {
"@azure/arm-resources": "^4.0.0",
"@microsoft/eslint-config-azuretools": "^0.1.0",
"@microsoft/vscode-azext-dev": "^2.0.4",
"@types/copy-webpack-plugin": "^6.4.0",
"@types/documentdb": "^1.10.2",
@ -1110,20 +1109,23 @@
"@types/node": "^14.0.0",
"@types/pg": "^8.10.2",
"@types/vscode": "1.82.0",
"@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/eslint-plugin": "^8.5.0",
"@typescript-eslint/parser": "^8.5.0",
"@vscode/test-electron": "^2.3.8",
"antlr4ts-cli": "^0.4.0-alpha.4",
"copy-webpack-plugin": "^6.4.0",
"eslint": "^7.19.0",
"eslint-plugin-import": "^2.22.1",
"eslint": "^8.57.0",
"eslint-plugin-import": "^2.30.0",
"glob": "^7.1.6",
"gulp": "^4.0.0",
"husky": "^7.0.2",
"mocha": "^10.2.0",
"mocha-junit-reporter": "^1.18.0",
"mocha-multi-reporters": "^1.1.7",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.0.0",
"ts-node": "^7.0.1",
"typescript": "^4.4.3",
"typescript": "^5.5.4",
"vsce": "^1.87.0",
"webpack": "^5.76.0",
"webpack-cli": "^4.6.0",

Просмотреть файл

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DatabaseAccountGetResults } from '@azure/arm-cosmosdb/src/models';
import { IAzureQuickPickItem } from '@microsoft/vscode-azext-utils';
import { type DatabaseAccountGetResults } from '@azure/arm-cosmosdb/src/models';
import { type IAzureQuickPickItem } from '@microsoft/vscode-azext-utils';
import { nonNullProp } from './utils/nonNull';
export enum API {
@ -13,12 +13,12 @@ export enum API {
Table = 'Table',
Core = 'Core',
PostgresSingle = 'PostgresSingle',
PostgresFlexible = 'PostgresFlexible'
PostgresFlexible = 'PostgresFlexible',
}
export enum DBAccountKind {
MongoDB = 'MongoDB',
GlobalDocumentDB = 'GlobalDocumentDB'
GlobalDocumentDB = 'GlobalDocumentDB',
}
export type CapabilityName = 'EnableGremlin' | 'EnableTable';
@ -32,26 +32,26 @@ export function getExperienceFromApi(api: API): Experience {
}
export function getExperienceLabel(databaseAccount: DatabaseAccountGetResults): string {
const experience: Experience | undefined = tryGetExperience(databaseAccount);
if (experience) {
return experience.shortName;
}
// Must be some new kind of resource that we aren't aware of. Try to get a decent label
const defaultExperience: string = <API>(databaseAccount && databaseAccount.tags && databaseAccount.tags.defaultExperience);
const defaultExperience: string = <API>(
(databaseAccount && databaseAccount.tags && databaseAccount.tags.defaultExperience)
);
const firstCapability = databaseAccount.capabilities && databaseAccount.capabilities[0];
const firstCapabilityName = firstCapability?.name?.replace(/^Enable/, '');
return defaultExperience || firstCapabilityName || nonNullProp(databaseAccount, 'kind');
}
export function tryGetExperience(resource: DatabaseAccountGetResults): Experience | undefined {
// defaultExperience in the resource doesn't really mean anything, we can't depend on its value for determining resource type
if (resource.kind === DBAccountKind.MongoDB) {
return MongoExperience;
} else if (resource.capabilities?.find(cap => cap.name === 'EnableGremlin')) {
} else if (resource.capabilities?.find((cap) => cap.name === 'EnableGremlin')) {
return GremlinExperience;
} else if (resource.capabilities?.find(cap => cap.name === 'EnableTable')) {
} else if (resource.capabilities?.find((cap) => cap.name === 'EnableTable')) {
return TableExperience;
} else if (resource.capabilities?.length === 0) {
return CoreExperience;
@ -80,17 +80,17 @@ export interface Experience {
export function getExperienceQuickPicks(attached?: boolean): IAzureQuickPickItem<Experience>[] {
if (attached) {
return experiencesArray.map(exp => getExperienceQuickPickForAttached(exp.api));
return experiencesArray.map((exp) => getExperienceQuickPickForAttached(exp.api));
} else {
return experiencesArray.map(exp => getExperienceQuickPick(exp.api));
return experiencesArray.map((exp) => getExperienceQuickPick(exp.api));
}
}
export function getCosmosExperienceQuickPicks(attached?: boolean): IAzureQuickPickItem<Experience>[] {
if (attached) {
return cosmosExperiencesArray.map(exp => getExperienceQuickPickForAttached(exp.api));
return cosmosExperiencesArray.map((exp) => getExperienceQuickPickForAttached(exp.api));
} else {
return cosmosExperiencesArray.map(exp => getExperienceQuickPick(exp.api));
return cosmosExperiencesArray.map((exp) => getExperienceQuickPick(exp.api));
}
}
@ -106,13 +106,55 @@ export function getExperienceQuickPickForAttached(api: API): IAzureQuickPickItem
// Mongo is distinguished by having kind="MongoDB". All others have kind="GlobalDocumentDB"
// Table and Gremlin are distinguished from SQL by their capabilities
export const CoreExperience: Experience = { api: API.Core, longName: "Core", description: "(SQL)", shortName: "SQL", kind: DBAccountKind.GlobalDocumentDB, tag: 'Core (SQL)' } as const;
export const MongoExperience: Experience = { api: API.MongoDB, longName: "Azure Cosmos DB for MongoDB API", shortName: "MongoDB", kind: DBAccountKind.MongoDB, tag: "Azure Cosmos DB for MongoDB API" } as const;
export const TableExperience: Experience = { api: API.Table, longName: "Azure Table", shortName: "Table", kind: DBAccountKind.GlobalDocumentDB, capability: 'EnableTable', tag: 'Azure Table' } as const;
export const GremlinExperience: Experience = { api: API.Graph, longName: "Gremlin", description: "(graph)", shortName: "Gremlin", kind: DBAccountKind.GlobalDocumentDB, capability: 'EnableGremlin', tag: 'Gremlin (graph)' } as const;
const PostgresSingleExperience: Experience = { api: API.PostgresSingle, longName: "PostgreSQL Single Server", shortName: "PostgreSQLSingle" };
const PostgresFlexibleExperience: Experience = { api: API.PostgresFlexible, longName: "PostgreSQL Flexible Server", shortName: "PostgreSQLFlexible" };
export const CoreExperience: Experience = {
api: API.Core,
longName: 'Core',
description: '(SQL)',
shortName: 'SQL',
kind: DBAccountKind.GlobalDocumentDB,
tag: 'Core (SQL)',
} as const;
export const MongoExperience: Experience = {
api: API.MongoDB,
longName: 'Azure Cosmos DB for MongoDB API',
shortName: 'MongoDB',
kind: DBAccountKind.MongoDB,
tag: 'Azure Cosmos DB for MongoDB API',
} as const;
export const TableExperience: Experience = {
api: API.Table,
longName: 'Azure Table',
shortName: 'Table',
kind: DBAccountKind.GlobalDocumentDB,
capability: 'EnableTable',
tag: 'Azure Table',
} as const;
export const GremlinExperience: Experience = {
api: API.Graph,
longName: 'Gremlin',
description: '(graph)',
shortName: 'Gremlin',
kind: DBAccountKind.GlobalDocumentDB,
capability: 'EnableGremlin',
tag: 'Gremlin (graph)',
} as const;
const PostgresSingleExperience: Experience = {
api: API.PostgresSingle,
longName: 'PostgreSQL Single Server',
shortName: 'PostgreSQLSingle',
};
const PostgresFlexibleExperience: Experience = {
api: API.PostgresFlexible,
longName: 'PostgreSQL Flexible Server',
shortName: 'PostgreSQLFlexible',
};
const cosmosExperiencesArray: Experience[] = [CoreExperience, MongoExperience, TableExperience, GremlinExperience];
const experiencesArray: Experience[] = [...cosmosExperiencesArray, PostgresSingleExperience, PostgresFlexibleExperience];
const experiencesMap = new Map<API, Experience>(experiencesArray.map((info: Experience): [API, Experience] => [info.api, info]));
const experiencesArray: Experience[] = [
...cosmosExperiencesArray,
PostgresSingleExperience,
PostgresFlexibleExperience,
];
const experiencesMap = new Map<API, Experience>(
experiencesArray.map((info: Experience): [API, Experience] => [info.api, info]),
);

Просмотреть файл

@ -3,13 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeFileSystem, AzExtTreeItem, DialogResponses, IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils';
import { FileStat, FileType, MessageItem, Uri, workspace } from "vscode";
import { FileChangeType } from "vscode-languageclient";
import { ext } from "./extensionVariables";
import { localize } from "./utils/localize";
import { getWorkspaceSetting, updateGlobalSetting } from "./utils/settingUtils";
import { getNodeEditorLabel } from "./utils/vscodeUtils";
import {
AzExtTreeFileSystem,
DialogResponses,
UserCancelledError,
type AzExtTreeItem,
type IActionContext,
} from '@microsoft/vscode-azext-utils';
import { FileType, workspace, type FileStat, type MessageItem, type Uri } from 'vscode';
import { FileChangeType } from 'vscode-languageclient';
import { ext } from './extensionVariables';
import { localize } from './utils/localize';
import { getWorkspaceSetting, updateGlobalSetting } from './utils/settingUtils';
import { getNodeEditorLabel } from './utils/vscodeUtils';
export interface IEditableTreeItem extends AzExtTreeItem {
id: string;
@ -34,14 +40,30 @@ export class DatabasesFileSystem extends AzExtTreeFileSystem<IEditableTreeItem>
return Buffer.from(await node.getFileContent(context));
}
public async writeFileImpl(context: IActionContext, node: IEditableTreeItem, content: Uint8Array, _originalUri: Uri): Promise<void> {
public async writeFileImpl(
context: IActionContext,
node: IEditableTreeItem,
content: Uint8Array,
_originalUri: Uri,
): Promise<void> {
const showSavePromptKey: string = 'showSavePrompt';
// NOTE: Using "cosmosDB" instead of "azureDatabases" here for the sake of backwards compatibility. If/when this file system adds support for non-cosmosdb items, we should consider changing this to "azureDatabases"
const prefix: string = 'cosmosDB';
const nodeEditorLabel: string = getNodeEditorLabel(node);
if (this._showSaveConfirmation && getWorkspaceSetting<boolean>(showSavePromptKey, undefined, prefix)) {
const message: string = localize('saveConfirmation', 'Saving "{0}" will update the entity "{1}" to the cloud.', node.filePath, nodeEditorLabel);
const result: MessageItem | undefined = await context.ui.showWarningMessage(message, { stepName: 'writeFile' }, DialogResponses.upload, DialogResponses.alwaysUpload, DialogResponses.dontUpload);
const message: string = localize(
'saveConfirmation',
'Saving "{0}" will update the entity "{1}" to the cloud.',
node.filePath,
nodeEditorLabel,
);
const result: MessageItem | undefined = await context.ui.showWarningMessage(
message,
{ stepName: 'writeFile' },
DialogResponses.upload,
DialogResponses.alwaysUpload,
DialogResponses.dontUpload,
);
if (result === DialogResponses.alwaysUpload) {
await updateGlobalSetting(showSavePromptKey, false, prefix);
} else if (result === DialogResponses.dontUpload) {

Просмотреть файл

@ -3,14 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtServiceClientCredentials } from "@microsoft/vscode-azext-utils";
import { type AzExtServiceClientCredentials } from '@microsoft/vscode-azext-utils';
/**
* Gets a function that can request an access token for a specified scope for the signed-in azure account.
*/
export function getTokenFunction(credentials: AzExtServiceClientCredentials, scope: string): () => Promise<string> {
return async () => {
const getTokenResult = await credentials.getToken(scope) as { token: string } | undefined;
return getTokenResult?.token ?? "";
const getTokenResult = (await credentials.getToken(scope)) as { token: string } | undefined;
return getTokenResult?.token ?? '';
};
}

Просмотреть файл

@ -3,24 +3,27 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { callWithTelemetryAndErrorHandling, IActionContext } from '@microsoft/vscode-azext-utils';
import { callWithTelemetryAndErrorHandling, type IActionContext } from '@microsoft/vscode-azext-utils';
import { API } from '../../AzureDBExperiences';
import { getCosmosKeyCredential } from '../../docdb/getCosmosClient';
import { DocDBAccountTreeItemBase } from '../../docdb/tree/DocDBAccountTreeItemBase';
import { ext } from '../../extensionVariables';
import { ParsedMongoConnectionString } from '../../mongo/mongoConnectionStrings';
import { MongoAccountTreeItem } from '../../mongo/tree/MongoAccountTreeItem';
import { ParsedConnectionString } from '../../ParsedConnectionString';
import { type ParsedConnectionString } from '../../ParsedConnectionString';
import { ParsedPostgresConnectionString } from '../../postgres/postgresConnectionStrings';
import { PostgresServerTreeItem } from '../../postgres/tree/PostgresServerTreeItem';
import { nonNullProp } from '../../utils/nonNull';
import { DatabaseAccountTreeItem } from '../../vscode-cosmosdb.api';
import { type DatabaseAccountTreeItem } from '../../vscode-cosmosdb.api';
export class DatabaseAccountTreeItemInternal implements DatabaseAccountTreeItem {
protected _parsedCS: ParsedConnectionString;
private _accountNode: MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem | undefined;
constructor(parsedCS: ParsedConnectionString, accountNode?: MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem) {
constructor(
parsedCS: ParsedConnectionString,
accountNode?: MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem,
) {
this._parsedCS = parsedCS;
this._accountNode = accountNode;
}
@ -37,32 +40,35 @@ export class DatabaseAccountTreeItemInternal implements DatabaseAccountTreeItem
return this._parsedCS.port;
}
public get azureData(): { accountName: string, accountId: string } | undefined {
if (this._accountNode instanceof MongoAccountTreeItem || this._accountNode instanceof DocDBAccountTreeItemBase) {
public get azureData(): { accountName: string; accountId: string } | undefined {
if (
this._accountNode instanceof MongoAccountTreeItem ||
this._accountNode instanceof DocDBAccountTreeItemBase
) {
if (this._accountNode?.databaseAccount) {
return {
accountName: nonNullProp(this._accountNode.databaseAccount, 'name'),
accountId: this._accountNode.fullId
accountId: this._accountNode.fullId,
};
}
} else if (this._accountNode instanceof PostgresServerTreeItem) {
if (this._accountNode.azureName) {
return {
accountName: this._accountNode.azureName,
accountId: this._accountNode.fullId
accountId: this._accountNode.fullId,
};
}
}
return undefined;
}
public get docDBData(): { masterKey: string; documentEndpoint: string; } | undefined {
public get docDBData(): { masterKey: string; documentEndpoint: string } | undefined {
if (this._accountNode instanceof DocDBAccountTreeItemBase) {
const keyCred = getCosmosKeyCredential(this._accountNode.root.credentials);
if (keyCred) {
return {
documentEndpoint: this._accountNode.root.endpoint,
masterKey: keyCred.key
masterKey: keyCred.key,
};
} else {
return undefined;
@ -77,7 +83,7 @@ export class DatabaseAccountTreeItemInternal implements DatabaseAccountTreeItem
const connectionString = this._parsedCS;
return {
username: connectionString.username,
password: connectionString.password
password: connectionString.password,
};
} else {
return undefined;
@ -92,10 +98,11 @@ export class DatabaseAccountTreeItemInternal implements DatabaseAccountTreeItem
});
}
protected async getAccountNode(context: IActionContext): Promise<MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem> {
protected async getAccountNode(
context: IActionContext,
): Promise<MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem> {
// If this._accountNode is undefined, attach a new node based on connection string
if (!this._accountNode) {
let apiType: API;
if (this._parsedCS instanceof ParsedMongoConnectionString) {
apiType = API.MongoDB;
@ -104,7 +111,11 @@ export class DatabaseAccountTreeItemInternal implements DatabaseAccountTreeItem
} else {
apiType = API.Core;
}
this._accountNode = await ext.attachedAccountsNode.attachConnectionString(context, this.connectionString, apiType);
this._accountNode = await ext.attachedAccountsNode.attachConnectionString(
context,
this.connectionString,
apiType,
);
}
return this._accountNode;

Просмотреть файл

@ -3,23 +3,32 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, callWithTelemetryAndErrorHandling, IActionContext } from '@microsoft/vscode-azext-utils';
import { DocDBAccountTreeItemBase } from '../../docdb/tree/DocDBAccountTreeItemBase';
import { DocDBDatabaseTreeItemBase } from '../../docdb/tree/DocDBDatabaseTreeItemBase';
import {
callWithTelemetryAndErrorHandling,
type AzExtTreeItem,
type IActionContext,
} from '@microsoft/vscode-azext-utils';
import { type DocDBAccountTreeItemBase } from '../../docdb/tree/DocDBAccountTreeItemBase';
import { type DocDBDatabaseTreeItemBase } from '../../docdb/tree/DocDBDatabaseTreeItemBase';
import { ext } from '../../extensionVariables';
import { MongoAccountTreeItem } from '../../mongo/tree/MongoAccountTreeItem';
import { MongoDatabaseTreeItem } from '../../mongo/tree/MongoDatabaseTreeItem';
import { ParsedConnectionString } from '../../ParsedConnectionString';
import { PostgresDatabaseTreeItem } from '../../postgres/tree/PostgresDatabaseTreeItem';
import { PostgresServerTreeItem } from '../../postgres/tree/PostgresServerTreeItem';
import { DatabaseTreeItem } from '../../vscode-cosmosdb.api';
import { type MongoAccountTreeItem } from '../../mongo/tree/MongoAccountTreeItem';
import { type MongoDatabaseTreeItem } from '../../mongo/tree/MongoDatabaseTreeItem';
import { type ParsedConnectionString } from '../../ParsedConnectionString';
import { type PostgresDatabaseTreeItem } from '../../postgres/tree/PostgresDatabaseTreeItem';
import { type PostgresServerTreeItem } from '../../postgres/tree/PostgresServerTreeItem';
import { type DatabaseTreeItem } from '../../vscode-cosmosdb.api';
import { DatabaseAccountTreeItemInternal } from './DatabaseAccountTreeItemInternal';
export class DatabaseTreeItemInternal extends DatabaseAccountTreeItemInternal implements DatabaseTreeItem {
public databaseName: string;
private _dbNode: AzExtTreeItem | undefined;
constructor(parsedCS: ParsedConnectionString, databaseName: string, accountNode?: MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem, dbNode?: MongoDatabaseTreeItem | DocDBDatabaseTreeItemBase | PostgresDatabaseTreeItem) {
constructor(
parsedCS: ParsedConnectionString,
databaseName: string,
accountNode?: MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem,
dbNode?: MongoDatabaseTreeItem | DocDBDatabaseTreeItemBase | PostgresDatabaseTreeItem,
) {
super(parsedCS, accountNode);
this.databaseName = databaseName;
this._dbNode = dbNode;
@ -30,7 +39,8 @@ export class DatabaseTreeItemInternal extends DatabaseAccountTreeItemInternal im
context.errorHandling.suppressDisplay = true;
context.errorHandling.rethrow = true;
const accountNode: MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem = await this.getAccountNode(context);
const accountNode: MongoAccountTreeItem | DocDBAccountTreeItemBase | PostgresServerTreeItem =
await this.getAccountNode(context);
if (!this._dbNode) {
const databaseId = `${accountNode.fullId}/${this.databaseName}`;
this._dbNode = await ext.rgApi.workspaceResourceTree.findTreeItem(databaseId, context);

Просмотреть файл

@ -5,9 +5,12 @@
import { ParsedDocDBConnectionString } from '../../docdb/docDBConnectionStrings';
import { ParsedMongoConnectionString } from '../../mongo/mongoConnectionStrings';
import { ParsedConnectionString } from '../../ParsedConnectionString';
import { ParsedPostgresConnectionString, parsePostgresConnectionString } from '../../postgres/postgresConnectionStrings';
import { DatabaseAccountTreeItem, DatabaseTreeItem } from '../../vscode-cosmosdb.api';
import { type ParsedConnectionString } from '../../ParsedConnectionString';
import {
ParsedPostgresConnectionString,
parsePostgresConnectionString,
} from '../../postgres/postgresConnectionStrings';
import { type DatabaseAccountTreeItem, type DatabaseTreeItem } from '../../vscode-cosmosdb.api';
/**
* This cache is used to speed up api calls from other extensions to the Cosmos DB extension
@ -16,11 +19,16 @@ import { DatabaseAccountTreeItem, DatabaseTreeItem } from '../../vscode-cosmosdb
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const sessionCache: Map<string, DatabaseAccountTreeItem | DatabaseTreeItem> = new Map();
export function cacheTreeItem(parsedCS: ParsedConnectionString, treeItem: DatabaseAccountTreeItem | DatabaseTreeItem): void {
export function cacheTreeItem(
parsedCS: ParsedConnectionString,
treeItem: DatabaseAccountTreeItem | DatabaseTreeItem,
): void {
sessionCache.set(parsedCS.fullId, treeItem);
}
export function tryGetTreeItemFromCache(parsedCS: ParsedConnectionString): DatabaseAccountTreeItem | DatabaseTreeItem | undefined {
export function tryGetTreeItemFromCache(
parsedCS: ParsedConnectionString,
): DatabaseAccountTreeItem | DatabaseTreeItem | undefined {
return sessionCache.get(parsedCS.fullId);
}
@ -36,7 +44,7 @@ export function removeTreeItemFromCache(expected: ParsedConnectionString): void
} else {
actual = new ParsedDocDBConnectionString(value.connectionString, value.hostName, value.port, undefined);
}
if (actual && (actual.accountId === expected.accountId)) {
if (actual && actual.accountId === expected.accountId) {
sessionCache.delete(key);
}
}

Просмотреть файл

@ -3,7 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, callWithTelemetryAndErrorHandling, IActionContext } from '@microsoft/vscode-azext-utils';
import {
callWithTelemetryAndErrorHandling,
type AzExtTreeItem,
type IActionContext,
} from '@microsoft/vscode-azext-utils';
import { parseDocDBConnectionString } from '../../docdb/docDBConnectionStrings';
import { DocDBAccountTreeItemBase } from '../../docdb/tree/DocDBAccountTreeItemBase';
import { DocDBDatabaseTreeItemBase } from '../../docdb/tree/DocDBDatabaseTreeItemBase';
@ -11,18 +15,23 @@ import { ext } from '../../extensionVariables';
import { parseMongoConnectionString } from '../../mongo/mongoConnectionStrings';
import { MongoAccountTreeItem } from '../../mongo/tree/MongoAccountTreeItem';
import { MongoDatabaseTreeItem } from '../../mongo/tree/MongoDatabaseTreeItem';
import { ParsedConnectionString } from '../../ParsedConnectionString';
import { createPostgresConnectionString, parsePostgresConnectionString } from '../../postgres/postgresConnectionStrings';
import { type ParsedConnectionString } from '../../ParsedConnectionString';
import {
createPostgresConnectionString,
parsePostgresConnectionString,
} from '../../postgres/postgresConnectionStrings';
import { PostgresDatabaseTreeItem } from '../../postgres/tree/PostgresDatabaseTreeItem';
import { PostgresServerTreeItem } from '../../postgres/tree/PostgresServerTreeItem';
import { SubscriptionTreeItem } from '../../tree/SubscriptionTreeItem';
import { nonNullProp } from '../../utils/nonNull';
import { DatabaseAccountTreeItem, DatabaseTreeItem, TreeItemQuery } from '../../vscode-cosmosdb.api';
import { type DatabaseAccountTreeItem, type DatabaseTreeItem, type TreeItemQuery } from '../../vscode-cosmosdb.api';
import { cacheTreeItem, tryGetTreeItemFromCache } from './apiCache';
import { DatabaseAccountTreeItemInternal } from './DatabaseAccountTreeItemInternal';
import { DatabaseTreeItemInternal } from './DatabaseTreeItemInternal';
export async function findTreeItem(query: TreeItemQuery): Promise<DatabaseAccountTreeItem | DatabaseTreeItem | undefined> {
export async function findTreeItem(
query: TreeItemQuery,
): Promise<DatabaseAccountTreeItem | DatabaseTreeItem | undefined> {
return await callWithTelemetryAndErrorHandling('api.findTreeItem', async (context: IActionContext) => {
context.errorHandling.suppressDisplay = true;
context.errorHandling.rethrow = true;
@ -30,7 +39,13 @@ export async function findTreeItem(query: TreeItemQuery): Promise<DatabaseAccoun
let parsedCS: ParsedConnectionString;
if (query.postgresData) {
const postgresData = query.postgresData;
const connectionString: string = createPostgresConnectionString(postgresData.hostName, postgresData.port, postgresData.username, postgresData.password, postgresData.databaseName);
const connectionString: string = createPostgresConnectionString(
postgresData.hostName,
postgresData.port,
postgresData.username,
postgresData.password,
postgresData.databaseName,
);
parsedCS = parsePostgresConnectionString(connectionString);
} else {
const connectionString = nonNullProp(query, 'connectionString');
@ -87,7 +102,12 @@ export async function findTreeItem(query: TreeItemQuery): Promise<DatabaseAccoun
});
}
async function searchDbAccounts(dbAccounts: AzExtTreeItem[], expected: ParsedConnectionString, context: IActionContext, maxTime: number): Promise<DatabaseAccountTreeItem | DatabaseTreeItem | undefined> {
async function searchDbAccounts(
dbAccounts: AzExtTreeItem[],
expected: ParsedConnectionString,
context: IActionContext,
maxTime: number,
): Promise<DatabaseAccountTreeItem | DatabaseTreeItem | undefined> {
try {
for (const dbAccount of dbAccounts) {
if (Date.now() > maxTime) {
@ -109,12 +129,24 @@ async function searchDbAccounts(dbAccounts: AzExtTreeItem[], expected: ParsedCon
if (expected.databaseName) {
const dbs = await dbAccount.getCachedChildren(context);
for (const db of dbs) {
if ((db instanceof MongoDatabaseTreeItem || db instanceof DocDBDatabaseTreeItemBase) && expected.databaseName === db.databaseName) {
if (
(db instanceof MongoDatabaseTreeItem || db instanceof DocDBDatabaseTreeItemBase) &&
expected.databaseName === db.databaseName
) {
return new DatabaseTreeItemInternal(expected, expected.databaseName, dbAccount, db);
}
if ((db instanceof PostgresDatabaseTreeItem && dbAccount instanceof PostgresServerTreeItem) && expected.databaseName === db.databaseName) {
if (
db instanceof PostgresDatabaseTreeItem &&
dbAccount instanceof PostgresServerTreeItem &&
expected.databaseName === db.databaseName
) {
const fullConnectionString = await dbAccount.getFullConnectionString();
return new DatabaseTreeItemInternal(fullConnectionString, expected.databaseName, dbAccount, db);
return new DatabaseTreeItemInternal(
fullConnectionString,
expected.databaseName,
dbAccount,
db,
);
}
}
@ -125,7 +157,6 @@ async function searchDbAccounts(dbAccounts: AzExtTreeItem[], expected: ParsedCon
} else {
return new DatabaseTreeItemInternal(expected, expected.databaseName, dbAccount);
}
}
if (dbAccount instanceof PostgresServerTreeItem) {
@ -134,10 +165,9 @@ async function searchDbAccounts(dbAccounts: AzExtTreeItem[], expected: ParsedCon
} else {
return new DatabaseAccountTreeItemInternal(expected, dbAccount);
}
}
}
} catch (error) {
} catch {
// Swallow all errors to avoid blocking the db account search
// https://github.com/microsoft/vscode-cosmosdb/issues/966
}

Просмотреть файл

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { callWithTelemetryAndErrorHandling, IActionContext } from '@microsoft/vscode-azext-utils';
import { PickAppResourceOptions } from '@microsoft/vscode-azext-utils/hostapi';
import { callWithTelemetryAndErrorHandling, type IActionContext } from '@microsoft/vscode-azext-utils';
import { type PickAppResourceOptions } from '@microsoft/vscode-azext-utils/hostapi';
import { databaseAccountType } from '../../constants';
import { parseDocDBConnectionString } from '../../docdb/docDBConnectionStrings';
import { DocDBAccountTreeItemBase } from '../../docdb/tree/DocDBAccountTreeItemBase';
@ -15,16 +15,26 @@ import { GraphDatabaseTreeItem } from '../../graph/tree/GraphDatabaseTreeItem';
import { parseMongoConnectionString } from '../../mongo/mongoConnectionStrings';
import { MongoAccountTreeItem } from '../../mongo/tree/MongoAccountTreeItem';
import { MongoDatabaseTreeItem } from '../../mongo/tree/MongoDatabaseTreeItem';
import { ParsedConnectionString } from '../../ParsedConnectionString';
import { type ParsedConnectionString } from '../../ParsedConnectionString';
import { PostgresDatabaseTreeItem } from '../../postgres/tree/PostgresDatabaseTreeItem';
import { PostgresServerTreeItem } from '../../postgres/tree/PostgresServerTreeItem';
import { localize } from '../../utils/localize';
import { AzureDatabasesApiType, DatabaseAccountTreeItem, DatabaseTreeItem, PickTreeItemOptions } from '../../vscode-cosmosdb.api';
import {
type AzureDatabasesApiType,
type DatabaseAccountTreeItem,
type DatabaseTreeItem,
type PickTreeItemOptions,
} from '../../vscode-cosmosdb.api';
import { cacheTreeItem } from './apiCache';
import { DatabaseAccountTreeItemInternal } from './DatabaseAccountTreeItemInternal';
import { DatabaseTreeItemInternal } from './DatabaseTreeItemInternal';
const databaseContextValues = [MongoDatabaseTreeItem.contextValue, DocDBDatabaseTreeItem.contextValue, GraphDatabaseTreeItem.contextValue, PostgresDatabaseTreeItem.contextValue];
const databaseContextValues = [
MongoDatabaseTreeItem.contextValue,
DocDBDatabaseTreeItem.contextValue,
GraphDatabaseTreeItem.contextValue,
PostgresDatabaseTreeItem.contextValue,
];
function getDatabaseContextValue(apiType: AzureDatabasesApiType): string {
switch (apiType) {
case 'Mongo':
@ -40,8 +50,9 @@ function getDatabaseContextValue(apiType: AzureDatabasesApiType): string {
}
}
export async function pickTreeItem(pickTreeOptions: PickTreeItemOptions): Promise<DatabaseTreeItem | DatabaseAccountTreeItem | undefined> {
export async function pickTreeItem(
pickTreeOptions: PickTreeItemOptions,
): Promise<DatabaseTreeItem | DatabaseAccountTreeItem | undefined> {
return await callWithTelemetryAndErrorHandling('api.pickTreeItem', async (context: IActionContext) => {
context.errorHandling.suppressDisplay = true;
context.errorHandling.rethrow = true;
@ -50,9 +61,9 @@ export async function pickTreeItem(pickTreeOptions: PickTreeItemOptions): Promis
switch (pickTreeOptions.resourceType) {
case 'Database':
options.filter = { type: databaseAccountType };
options.expectedChildContextValue = pickTreeOptions.apiType ?
pickTreeOptions.apiType.map(getDatabaseContextValue) :
databaseContextValues;
options.expectedChildContextValue = pickTreeOptions.apiType
? pickTreeOptions.apiType.map(getDatabaseContextValue)
: databaseContextValues;
break;
case 'DatabaseAccount':
options.filter = { type: databaseAccountType };
@ -91,9 +102,9 @@ export async function pickTreeItem(pickTreeOptions: PickTreeItemOptions): Promis
throw new RangeError(localize('invalidItem', 'Invalid item "{0}".', pickedItem.constructor.name));
}
const result = databaseNode ?
new DatabaseTreeItemInternal(parsedCS, databaseNode.databaseName, accountNode, databaseNode) :
new DatabaseAccountTreeItemInternal(parsedCS, accountNode);
const result = databaseNode
? new DatabaseTreeItemInternal(parsedCS, databaseNode.databaseName, accountNode, databaseNode)
: new DatabaseAccountTreeItemInternal(parsedCS, accountNode);
cacheTreeItem(parsedCS, result);
return result;
});

Просмотреть файл

@ -3,12 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, callWithTelemetryAndErrorHandling, IActionContext } from "@microsoft/vscode-azext-utils";
import { ext } from "../../extensionVariables";
import {
callWithTelemetryAndErrorHandling,
type AzExtTreeItem,
type IActionContext,
} from '@microsoft/vscode-azext-utils';
import { ext } from '../../extensionVariables';
export async function revealTreeItem(resourceId: string): Promise<void> {
return await callWithTelemetryAndErrorHandling('api.revealTreeItem', async (context: IActionContext) => {
const node: AzExtTreeItem | undefined = await ext.rgApi.appResourceTree.findTreeItem(resourceId, { ...context, loadAll: true });
const node: AzExtTreeItem | undefined = await ext.rgApi.appResourceTree.findTreeItem(resourceId, {
...context,
loadAll: true,
});
if (node) {
await ext.rgApi.appResourceTreeView.reveal(node, { select: true, focus: true, expand: true });
}

Просмотреть файл

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureWizardExecuteStep } from "@microsoft/vscode-azext-utils";
import { IDeleteWizardContext } from "./IDeleteWizardContext";
import { AzureWizardExecuteStep } from '@microsoft/vscode-azext-utils';
import { type IDeleteWizardContext } from './IDeleteWizardContext';
export class DatabaseAccountDeleteStep extends AzureWizardExecuteStep<IDeleteWizardContext> {
public priority: number = 100;

Просмотреть файл

@ -1,9 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, ExecuteActivityContext, IActionContext, ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import {
type AzExtTreeItem,
type ExecuteActivityContext,
type IActionContext,
type ISubscriptionContext,
} from '@microsoft/vscode-azext-utils';
export interface IDeleteWizardContext extends IActionContext, ExecuteActivityContext {
node: AzExtTreeItem;

Просмотреть файл

@ -3,15 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CosmosDBManagementClient } from '@azure/arm-cosmosdb';
import { type CosmosDBManagementClient } from '@azure/arm-cosmosdb';
import { getResourceGroupFromId } from '@microsoft/vscode-azext-azureutils';
import { AzExtTreeItem } from '@microsoft/vscode-azext-utils';
import { type AzExtTreeItem } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ext } from '../../extensionVariables';
import { createCosmosDBClient } from '../../utils/azureClients';
import { getDatabaseAccountNameFromId } from '../../utils/azureUtils';
import { localize } from '../../utils/localize';
import { IDeleteWizardContext } from './IDeleteWizardContext';
import { type IDeleteWizardContext } from './IDeleteWizardContext';
export async function deleteCosmosDBAccount(context: IDeleteWizardContext, node: AzExtTreeItem): Promise<void> {
const client: CosmosDBManagementClient = await createCosmosDBClient([context, node.subscription]);
@ -20,12 +20,19 @@ export async function deleteCosmosDBAccount(context: IDeleteWizardContext, node:
const deletePromise = client.databaseAccounts.beginDeleteAndWait(resourceGroup, accountName);
if (!context.suppressNotification) {
const deletingMessage: string = `Deleting account "${accountName}"...`;
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: deletingMessage }, async () => {
await deletePromise;
const deleteMessage: string = localize("deleteAccountMsg", `Successfully deleted account "{0}".`, accountName);
void vscode.window.showInformationMessage(deleteMessage);
ext.outputChannel.appendLog(deleteMessage);
});
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.Notification, title: deletingMessage },
async () => {
await deletePromise;
const deleteMessage: string = localize(
'deleteAccountMsg',
`Successfully deleted account "{0}".`,
accountName,
);
void vscode.window.showInformationMessage(deleteMessage);
ext.outputChannel.appendLog(deleteMessage);
},
);
} else {
await deletePromise;
}

Просмотреть файл

@ -3,33 +3,45 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, AzureWizard, DeleteConfirmationStep, IActionContext } from "@microsoft/vscode-azext-utils";
import { createActivityContext } from "../../utils/activityUtils";
import { localize } from "../../utils/localize";
import { DatabaseAccountDeleteStep } from "./DatabaseAccountDeleteStep";
import { IDeleteWizardContext } from "./IDeleteWizardContext";
import {
AzureWizard,
DeleteConfirmationStep,
type AzExtTreeItem,
type IActionContext,
} from '@microsoft/vscode-azext-utils';
import { createActivityContext } from '../../utils/activityUtils';
import { localize } from '../../utils/localize';
import { DatabaseAccountDeleteStep } from './DatabaseAccountDeleteStep';
import { type IDeleteWizardContext } from './IDeleteWizardContext';
export async function deleteDatabaseAccount(context: IActionContext, node: AzExtTreeItem, isPostgres: boolean = false): Promise<void> {
export async function deleteDatabaseAccount(
context: IActionContext,
node: AzExtTreeItem,
isPostgres: boolean = false,
): Promise<void> {
const wizardContext: IDeleteWizardContext = Object.assign(context, {
node,
deletePostgres: isPostgres,
subscription: node.subscription,
...(await createActivityContext())
...(await createActivityContext()),
});
const title = wizardContext.deletePostgres ?
localize('deletePoSer', 'Delete Postgres Server "{0}"', node.label) :
localize('deleteDbAcc', 'Delete Database Account "{0}"', node.label)
const title = wizardContext.deletePostgres
? localize('deletePoSer', 'Delete Postgres Server "{0}"', node.label)
: localize('deleteDbAcc', 'Delete Database Account "{0}"', node.label);
const confirmationMessage = wizardContext.deletePostgres ?
localize('deleteAccountConfirm', 'Are you sure you want to delete server "{0}" and its contents?', node.label) :
localize('deleteAccountConfirm', 'Are you sure you want to delete account "{0}" and its contents?', node.label);
const confirmationMessage = wizardContext.deletePostgres
? localize('deleteAccountConfirm', 'Are you sure you want to delete server "{0}" and its contents?', node.label)
: localize(
'deleteAccountConfirm',
'Are you sure you want to delete account "{0}" and its contents?',
node.label,
);
const wizard = new AzureWizard(wizardContext, {
title,
promptSteps: [new DeleteConfirmationStep(confirmationMessage)],
executeSteps: [new DatabaseAccountDeleteStep()]
executeSteps: [new DatabaseAccountDeleteStep()],
});
await wizard.prompt();

Просмотреть файл

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ItemDefinition } from '@azure/cosmos';
import { IActionContext, parseError } from '@microsoft/vscode-azext-utils';
import { type ItemDefinition } from '@azure/cosmos';
import { parseError, type IActionContext } from '@microsoft/vscode-azext-utils';
import * as fse from 'fs-extra';
import * as vscode from 'vscode';
import { cosmosMongoFilter, sqlFilter } from '../constants';
@ -14,7 +14,11 @@ import { MongoCollectionTreeItem } from '../mongo/tree/MongoCollectionTreeItem';
import { nonNullProp, nonNullValue } from '../utils/nonNull';
import { getRootPath } from '../utils/workspacUtils';
export async function importDocuments(context: IActionContext, uris: vscode.Uri[] | undefined, collectionNode: MongoCollectionTreeItem | DocDBCollectionTreeItem | undefined): Promise<void> {
export async function importDocuments(
context: IActionContext,
uris: vscode.Uri[] | undefined,
collectionNode: MongoCollectionTreeItem | DocDBCollectionTreeItem | undefined,
): Promise<void> {
if (!uris) {
uris = await askForDocuments(context);
}
@ -29,39 +33,36 @@ export async function importDocuments(context: IActionContext, uris: vscode.Uri[
});
if (ignoredUris.length) {
ext.outputChannel.appendLog(`Ignoring the following files which are not json:`);
ignoredUris.forEach(uri => ext.outputChannel.appendLog(`${uri.fsPath}`));
ignoredUris.forEach((uri) => ext.outputChannel.appendLog(`${uri.fsPath}`));
ext.outputChannel.show();
}
if (!collectionNode) {
collectionNode = await ext.rgApi.pickAppResource<MongoCollectionTreeItem | DocDBCollectionTreeItem>(context, {
filter: [
cosmosMongoFilter,
sqlFilter
],
expectedChildContextValue: [MongoCollectionTreeItem.contextValue, DocDBCollectionTreeItem.contextValue]
filter: [cosmosMongoFilter, sqlFilter],
expectedChildContextValue: [MongoCollectionTreeItem.contextValue, DocDBCollectionTreeItem.contextValue],
});
}
let result: string;
result = await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: "Importing documents..."
title: 'Importing documents...',
},
async (progress) => {
uris = nonNullValue(uris, 'uris');
collectionNode = nonNullValue(collectionNode, 'collectionNode');
progress.report({ increment: 20, message: "Parsing documents for errors" });
progress.report({ increment: 20, message: 'Parsing documents for errors' });
const documents = await parseDocuments(uris);
progress.report({ increment: 30, message: "Parsed documents. Importing" });
progress.report({ increment: 30, message: 'Parsed documents. Importing' });
if (collectionNode instanceof MongoCollectionTreeItem) {
result = await insertDocumentsIntoMongo(collectionNode, documents);
} else {
result = await insertDocumentsIntoDocdb(collectionNode, documents, uris);
}
progress.report({ increment: 50, message: "Finished importing" });
progress.report({ increment: 50, message: 'Finished importing' });
return result;
}
},
);
await collectionNode.refresh(context);
@ -71,10 +72,10 @@ export async function importDocuments(context: IActionContext, uris: vscode.Uri[
async function askForDocuments(context: IActionContext): Promise<vscode.Uri[]> {
const openDialogOptions: vscode.OpenDialogOptions = {
canSelectMany: true,
openLabel: "Import",
openLabel: 'Import',
filters: {
JSON: ["json"]
}
JSON: ['json'],
},
};
const rootPath: string | undefined = getRootPath();
if (rootPath) {
@ -96,7 +97,7 @@ async function parseDocuments(uris: vscode.Uri[]): Promise<any[]> {
} catch (e) {
if (!errorFoundFlag) {
errorFoundFlag = true;
ext.outputChannel.appendLog("Errors found in documents listed below. Please fix these.");
ext.outputChannel.appendLog('Errors found in documents listed below. Please fix these.');
ext.outputChannel.show();
}
const err = parseError(e);
@ -118,8 +119,12 @@ async function parseDocuments(uris: vscode.Uri[]): Promise<any[]> {
return documents;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function insertDocumentsIntoDocdb(collectionNode: DocDBCollectionTreeItem, documents: any[], uris: vscode.Uri[]): Promise<string> {
async function insertDocumentsIntoDocdb(
collectionNode: DocDBCollectionTreeItem,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
documents: any[],
uris: vscode.Uri[],
): Promise<string> {
const ids: string[] = [];
let i = 0;
const erroneousFiles: vscode.Uri[] = [];
@ -132,11 +137,14 @@ async function insertDocumentsIntoDocdb(collectionNode: DocDBCollectionTreeItem,
}
if (erroneousFiles.length) {
ext.outputChannel.appendLog(`The following documents do not contain the required partition key:`);
erroneousFiles.forEach(file => ext.outputChannel.appendLog(file.path));
erroneousFiles.forEach((file) => ext.outputChannel.appendLog(file.path));
ext.outputChannel.show();
throw new Error(`See output for list of documents that do not contain the partition key '${nonNullProp(collectionNode, 'partitionKey').paths[0]}' required by collection '${collectionNode.label}'`);
throw new Error(
`See output for list of documents that do not contain the partition key '${nonNullProp(collectionNode, 'partitionKey').paths[0]}' required by collection '${collectionNode.label}'`,
);
}
for (const document of documents) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const retrieved: ItemDefinition = await collectionNode.documentsTreeItem.createDocument(document);
if (retrieved.id) {
ids.push(retrieved.id);
@ -151,7 +159,8 @@ async function insertDocumentsIntoDocdb(collectionNode: DocDBCollectionTreeItem,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function insertDocumentsIntoMongo(node: MongoCollectionTreeItem, documents: any[]): Promise<string> {
let output = "";
let output = '';
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const parsed = await node.collection.insertMany(documents);
if (parsed.acknowledged) {
output = `Import into mongo successful. Inserted ${parsed.insertedCount} document(s). See output for more details.`;

Просмотреть файл

@ -23,7 +23,7 @@ export interface IThemedIconPath {
export function getThemedIconPath(iconName: string): IThemedIconPath {
const a = {
light: path.join(getResourcesPath(), 'icons', 'light', iconName),
dark: path.join(getResourcesPath(), 'icons', 'dark', iconName)
dark: path.join(getResourcesPath(), 'icons', 'dark', iconName),
};
assert(fs.existsSync(a.light));
return a;
@ -32,7 +32,7 @@ export function getThemedIconPath(iconName: string): IThemedIconPath {
export function getThemeAgnosticIconPath(iconName: string): IThemedIconPath {
const a = {
light: path.join(getResourcesPath(), 'icons', 'theme-agnostic', iconName),
dark: path.join(getResourcesPath(), 'icons', 'theme-agnostic', iconName)
dark: path.join(getResourcesPath(), 'icons', 'theme-agnostic', iconName),
};
assert(fs.existsSync(a.light));
return a;
@ -44,8 +44,7 @@ export function getResourcesPath(): string {
export const doubleClickDebounceDelay = 500; //milliseconds
export const defaultStoredProcedure =
`function sample(prefix) {
export const defaultStoredProcedure = `function sample(prefix) {
var collection = getContext().getCollection();
// Query documents and take 1st item.
@ -70,13 +69,14 @@ export const defaultStoredProcedure =
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
};` ;
};`;
export const defaultTrigger = `function trigger() {
}`;
export const emulatorPassword = 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==';
export const emulatorPassword =
'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==';
// https://docs.mongodb.com/manual/mongo/#working-with-the-mongo-shell
export const testDb: string = 'test';
@ -91,14 +91,14 @@ export const SERVERLESS_CAPABILITY_NAME = 'EnableServerless';
export const databaseAccountType = 'Microsoft.DocumentDB/databaseAccounts';
export const mongoDefaultExperienceTag = "Azure Cosmos DB for MongoDB API";
export const mongoDefaultExperienceTag = 'Azure Cosmos DB for MongoDB API';
export const cosmosMongoFilter = {
type: databaseAccountType,
kind: MongoExperience.kind,
tags: {
defaultExperience: mongoDefaultExperienceTag
}
defaultExperience: mongoDefaultExperienceTag,
},
};
export const gremlinDefaultExperienceTag = 'Gremlin (graph)';
@ -107,8 +107,8 @@ export const cosmosGremlinFilter = {
type: databaseAccountType,
kind: GremlinExperience.kind,
tags: {
defaultExperience: gremlinDefaultExperienceTag
}
defaultExperience: gremlinDefaultExperienceTag,
},
};
export const tableDefaultExperienceTag = 'Azure Table';
@ -117,8 +117,8 @@ export const cosmosTableFilter = {
type: databaseAccountType,
kind: TableExperience.kind,
tags: {
defaultExperience: tableDefaultExperienceTag
}
defaultExperience: tableDefaultExperienceTag,
},
};
export const sqlDefaultExperienceTag = 'Core (SQL)';
@ -127,14 +127,14 @@ export const sqlFilter = {
type: databaseAccountType,
kind: CoreExperience.kind,
tags: {
defaultExperience: sqlDefaultExperienceTag
}
defaultExperience: sqlDefaultExperienceTag,
},
};
export const postgresFlexibleFilter = {
type: 'Microsoft.DBforPostgreSQL/flexibleServers'
type: 'Microsoft.DBforPostgreSQL/flexibleServers',
};
export const postgresSingleFilter = {
type: 'Microsoft.DBForPostgreSQL/servers'
type: 'Microsoft.DBForPostgreSQL/servers',
};

Просмотреть файл

@ -3,19 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, callWithTelemetryAndErrorHandling } from "@microsoft/vscode-azext-utils";
import { callWithTelemetryAndErrorHandling, type IActionContext } from '@microsoft/vscode-azext-utils';
import {
CancellationToken,
CodeLens,
CodeLensProvider,
Event,
EventEmitter,
Position,
ProviderResult,
Range,
TextDocument
} from "vscode";
import { KeyValueStore } from "../KeyValueStore";
type CancellationToken,
type CodeLensProvider,
type Event,
type ProviderResult,
type TextDocument,
} from 'vscode';
import { KeyValueStore } from '../KeyValueStore';
export type NoSqlQueryConnection = {
databaseId: string;
@ -25,7 +25,7 @@ export type NoSqlQueryConnection = {
isEmulator: boolean;
};
export const noSqlQueryConnectionKey = "NO_SQL_QUERY_CONNECTION_KEY.v1";
export const noSqlQueryConnectionKey = 'NO_SQL_QUERY_CONNECTION_KEY.v1';
export class NoSqlCodeLensProvider implements CodeLensProvider {
private _onDidChangeEmitter: EventEmitter<void> = new EventEmitter<void>();
@ -39,59 +39,46 @@ export class NoSqlCodeLensProvider implements CodeLensProvider {
}
public provideCodeLenses(document: TextDocument, _token: CancellationToken): ProviderResult<CodeLens[]> {
return callWithTelemetryAndErrorHandling("nosql.provideCodeLenses", (context: IActionContext) => {
return callWithTelemetryAndErrorHandling('nosql.provideCodeLenses', (context: IActionContext) => {
context.telemetry.suppressIfSuccessful = true;
const text = document.getText();
const queryText = text;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
const connectedCollection: NoSqlQueryConnection | undefined = KeyValueStore.instance.get(noSqlQueryConnectionKey) as any;
const connectedCollection: NoSqlQueryConnection | undefined = KeyValueStore.instance.get(
noSqlQueryConnectionKey,
) as unknown as NoSqlQueryConnection;
let connectCodeLens: CodeLens;
if (!connectedCollection) {
connectCodeLens = new CodeLens(
new Range(new Position(0, 0), new Position(0, 0)),
{
title: "Not connected",
command: "cosmosDB.connectNoSqlContainer",
arguments: []
}
);
connectCodeLens = new CodeLens(new Range(new Position(0, 0), new Position(0, 0)), {
title: 'Not connected',
command: 'cosmosDB.connectNoSqlContainer',
arguments: [],
});
} else {
connectCodeLens = new CodeLens(
new Range(new Position(0, 0), new Position(0, 0)),
{
title: `Connected to ${connectedCollection.databaseId}.${connectedCollection.containerId}`,
command: "cosmosDB.connectNoSqlContainer",
arguments: []
}
);
connectCodeLens = new CodeLens(new Range(new Position(0, 0), new Position(0, 0)), {
title: `Connected to ${connectedCollection.databaseId}.${connectedCollection.containerId}`,
command: 'cosmosDB.connectNoSqlContainer',
arguments: [],
});
}
const lenses: CodeLens[] = [
connectCodeLens,
new CodeLens(
new Range(new Position(0, 0), new Position(0, 0)),
{
title: "Execute",
command: "cosmosDB.executeNoSqlQuery",
arguments: [{ queryText }]
}
),
new CodeLens(
new Range(new Position(0, 0), new Position(0, 0)),
{
title: "Execute with Query Metrics",
command: "cosmosDB.executeNoSqlQuery",
arguments: [{ queryText, populateQueryMetrics: true }]
}
),
new CodeLens(
new Range(new Position(0, 0), new Position(0, 0)),
{
title: "Get Query Plan",
command: "cosmosDB.getNoSqlQueryPlan",
arguments: [{ queryText }]
}
)
new CodeLens(new Range(new Position(0, 0), new Position(0, 0)), {
title: 'Execute',
command: 'cosmosDB.executeNoSqlQuery',
arguments: [{ queryText }],
}),
new CodeLens(new Range(new Position(0, 0), new Position(0, 0)), {
title: 'Execute with Query Metrics',
command: 'cosmosDB.executeNoSqlQuery',
arguments: [{ queryText, populateQueryMetrics: true }],
}),
new CodeLens(new Range(new Position(0, 0), new Position(0, 0)), {
title: 'Get Query Plan',
command: 'cosmosDB.getNoSqlQueryPlan',
arguments: [{ queryText }],
}),
];
return lenses;

Просмотреть файл

@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { KeyValueStore } from "../../KeyValueStore";
import { ext } from "../../extensionVariables";
import { NoSqlQueryConnection, noSqlQueryConnectionKey } from "../NoSqlCodeLensProvider";
import { getCosmosKeyCredential } from "../getCosmosClient";
import { DocDBCollectionTreeItem } from "../tree/DocDBCollectionTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { KeyValueStore } from '../../KeyValueStore';
import { ext } from '../../extensionVariables';
import { noSqlQueryConnectionKey, type NoSqlQueryConnection } from '../NoSqlCodeLensProvider';
import { getCosmosKeyCredential } from '../getCosmosClient';
import { DocDBCollectionTreeItem } from '../tree/DocDBCollectionTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export function setConnectedNoSqlContainer(node: DocDBCollectionTreeItem): void {
const root = node.root;
@ -19,7 +19,7 @@ export function setConnectedNoSqlContainer(node: DocDBCollectionTreeItem): void
containerId: node.id,
endpoint: root.endpoint,
masterKey: keyCred?.key,
isEmulator: !!root.isEmulator
isEmulator: !!root.isEmulator,
};
KeyValueStore.instance.set(noSqlQueryConnectionKey, noSqlQueryConnection);
ext.noSqlCodeLensProvider.updateCodeLens();

Просмотреть файл

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { DocDBDatabaseTreeItem } from "../tree/DocDBDatabaseTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { DocDBDatabaseTreeItem } from '../tree/DocDBDatabaseTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function createDocDBCollection(context: IActionContext, node?: DocDBDatabaseTreeItem): Promise<void> {
if (!node) {

Просмотреть файл

@ -3,11 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { DocDBAccountTreeItem } from "../tree/DocDBAccountTreeItem";
import { DocDBDatabaseTreeItem } from "../tree/DocDBDatabaseTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { type DocDBAccountTreeItem } from '../tree/DocDBAccountTreeItem';
import { type DocDBDatabaseTreeItem } from '../tree/DocDBDatabaseTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function createDocDBDatabase(context: IActionContext, node?: DocDBAccountTreeItem): Promise<void> {
if (!node) {

Просмотреть файл

@ -3,16 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { commands } from "vscode";
import { DocDBDocumentTreeItem } from "../tree/DocDBDocumentTreeItem";
import { DocDBDocumentsTreeItem } from "../tree/DocDBDocumentsTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { commands } from 'vscode';
import { type DocDBDocumentTreeItem } from '../tree/DocDBDocumentTreeItem';
import { DocDBDocumentsTreeItem } from '../tree/DocDBDocumentsTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function createDocDBDocument(context: IActionContext, node?: DocDBDocumentsTreeItem): Promise<void> {
if (!node) {
node = await pickDocDBAccount<DocDBDocumentsTreeItem>(context, DocDBDocumentsTreeItem.contextValue);
}
const documentNode = <DocDBDocumentTreeItem>await node.createChild(context);
await commands.executeCommand("cosmosDB.openDocument", documentNode);
await commands.executeCommand('cosmosDB.openDocument', documentNode);
}

Просмотреть файл

@ -3,15 +3,21 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { commands } from "vscode";
import { DocDBStoredProceduresTreeItem } from "../tree/DocDBStoredProceduresTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { commands } from 'vscode';
import { DocDBStoredProceduresTreeItem } from '../tree/DocDBStoredProceduresTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function createDocDBStoredProcedure(context: IActionContext, node?: DocDBStoredProceduresTreeItem): Promise<void> {
export async function createDocDBStoredProcedure(
context: IActionContext,
node?: DocDBStoredProceduresTreeItem,
): Promise<void> {
if (!node) {
node = await pickDocDBAccount<DocDBStoredProceduresTreeItem>(context, DocDBStoredProceduresTreeItem.contextValue);
node = await pickDocDBAccount<DocDBStoredProceduresTreeItem>(
context,
DocDBStoredProceduresTreeItem.contextValue,
);
}
const childNode = await node.createChild(context);
await commands.executeCommand("cosmosDB.openStoredProcedure", childNode);
await commands.executeCommand('cosmosDB.openStoredProcedure', childNode);
}

Просмотреть файл

@ -3,15 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { commands } from "vscode";
import { DocDBTriggersTreeItem } from "../tree/DocDBTriggersTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { commands } from 'vscode';
import { DocDBTriggersTreeItem } from '../tree/DocDBTriggersTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function createDocDBTrigger(context: IActionContext, node?: DocDBTriggersTreeItem): Promise<void> {
if (!node) {
node = await pickDocDBAccount<DocDBTriggersTreeItem>(context, DocDBTriggersTreeItem.contextValue);
}
const childNode = await node.createChild(context);
await commands.executeCommand("cosmosDB.openTrigger", childNode);
await commands.executeCommand('cosmosDB.openTrigger', childNode);
}

Просмотреть файл

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { DocDBCollectionTreeItem } from "../tree/DocDBCollectionTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import { DocDBCollectionTreeItem } from '../tree/DocDBCollectionTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function deleteDocDBCollection(context: IActionContext, node?: DocDBCollectionTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;

Просмотреть файл

@ -3,12 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { ext } from "../../extensionVariables";
import { localize } from "../../utils/localize";
import { DocDBDatabaseTreeItem } from "../tree/DocDBDatabaseTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ext } from '../../extensionVariables';
import { localize } from '../../utils/localize';
import { DocDBDatabaseTreeItem } from '../tree/DocDBDatabaseTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function deleteDocDBDatabase(context: IActionContext, node?: DocDBDatabaseTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;
@ -17,7 +17,7 @@ export async function deleteDocDBDatabase(context: IActionContext, node?: DocDBD
node = await pickDocDBAccount<DocDBDatabaseTreeItem>(context, DocDBDatabaseTreeItem.contextValue);
}
await node.deleteTreeItem(context);
const successMessage = localize("deleteMongoDatabaseMsg", 'Successfully deleted database "{0}"', node.databaseName);
const successMessage = localize('deleteMongoDatabaseMsg', 'Successfully deleted database "{0}"', node.databaseName);
void vscode.window.showInformationMessage(successMessage);
ext.outputChannel.info(successMessage);
}

Просмотреть файл

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { DocDBDocumentTreeItem } from "../tree/DocDBDocumentTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import { DocDBDocumentTreeItem } from '../tree/DocDBDocumentTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function deleteDocDBDocument(context: IActionContext, node?: DocDBDocumentTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;

Просмотреть файл

@ -3,11 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { DocDBStoredProcedureTreeItem } from "../tree/DocDBStoredProcedureTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import { DocDBStoredProcedureTreeItem } from '../tree/DocDBStoredProcedureTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function deleteDocDBStoredProcedure(context: IActionContext, node?: DocDBStoredProcedureTreeItem): Promise<void> {
export async function deleteDocDBStoredProcedure(
context: IActionContext,
node?: DocDBStoredProcedureTreeItem,
): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;
suppressCreateContext.suppressCreatePick = true;
if (!node) {

Просмотреть файл

@ -3,16 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { DocDBTriggerTreeItem } from "../tree/DocDBTriggerTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import { DocDBTriggerTreeItem } from '../tree/DocDBTriggerTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function deleteDocDBTrigger(context: IActionContext, node?: DocDBTriggerTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;
suppressCreateContext.suppressCreatePick = true;
if (!node) {
node = await pickDocDBAccount<DocDBTriggerTreeItem>(context, DocDBTriggerTreeItem.contextValue);
}
await node.deleteTreeItem(context);
}

Просмотреть файл

@ -3,12 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { localize } from "../../utils/localize";
import { DocDBStoredProcedureTreeItem } from "../tree/DocDBStoredProcedureTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import { localize } from '../../utils/localize';
import { DocDBStoredProcedureTreeItem } from '../tree/DocDBStoredProcedureTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function executeDocDBStoredProcedure(context: IActionContext, node?: DocDBStoredProcedureTreeItem): Promise<void> {
export async function executeDocDBStoredProcedure(
context: IActionContext,
node?: DocDBStoredProcedureTreeItem,
): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;
suppressCreateContext.suppressCreatePick = true;
if (!node) {
@ -22,15 +25,18 @@ export async function executeDocDBStoredProcedure(context: IActionContext, node?
const paramString = await context.ui.showInputBox({
title: 'Parameters',
placeHolder: localize("executeCosmosStoredProcedureParameters", "empty or array of values e.g. [1, {key: value}]"),
placeHolder: localize(
'executeCosmosStoredProcedureParameters',
'empty or array of values e.g. [1, {key: value}]',
),
// @todo: add a learnMoreLink
});
let parameters: (string | number | object)[] | undefined = undefined;
if (paramString !== "") {
if (paramString !== '') {
try {
parameters = JSON.parse(paramString) as (string | number | object)[];
} catch (error) {
} catch {
// Ignore parameters if they are invalid
}
}

Просмотреть файл

@ -3,16 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { ViewColumn } from "vscode";
import { KeyValueStore } from "../../KeyValueStore";
import { localize } from "../../utils/localize";
import * as vscodeUtil from "../../utils/vscodeUtils";
import { NoSqlQueryConnection, noSqlQueryConnectionKey } from "../NoSqlCodeLensProvider";
import { CosmosDBCredential, getCosmosClient } from "../getCosmosClient";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ViewColumn } from 'vscode';
import { KeyValueStore } from '../../KeyValueStore';
import { localize } from '../../utils/localize';
import * as vscodeUtil from '../../utils/vscodeUtils';
import { noSqlQueryConnectionKey, type NoSqlQueryConnection } from '../NoSqlCodeLensProvider';
import { getCosmosClient, type CosmosDBCredential } from '../getCosmosClient';
export async function executeNoSqlQuery(_context: IActionContext, args: { queryText: string, populateQueryMetrics?: boolean }): Promise<void> {
export async function executeNoSqlQuery(
_context: IActionContext,
args: { queryText: string; populateQueryMetrics?: boolean },
): Promise<void> {
let queryText: string;
let populateQueryMetrics: boolean;
if (!args) {
@ -29,26 +32,47 @@ export async function executeNoSqlQuery(_context: IActionContext, args: { queryT
}
const connectedCollection = KeyValueStore.instance.get(noSqlQueryConnectionKey);
if (!connectedCollection) {
throw new Error("Unable to execute query due to missing node data. Please connect to a Cosmos DB collection node.");
throw new Error(
'Unable to execute query due to missing node data. Please connect to a Cosmos DB collection node.',
);
} else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { databaseId, containerId, endpoint, masterKey, isEmulator } = connectedCollection as NoSqlQueryConnection;
const { databaseId, containerId, endpoint, masterKey, isEmulator } =
connectedCollection as NoSqlQueryConnection;
const credentials: CosmosDBCredential[] = [];
if (masterKey !== undefined) {
credentials.push({ type: "key", key: masterKey });
credentials.push({ type: 'key', key: masterKey });
}
credentials.push({ type: "auth" });
credentials.push({ type: 'auth' });
const client = getCosmosClient(endpoint, credentials, isEmulator);
const options = { populateQueryMetrics };
const response = await client.database(databaseId).container(containerId).items.query(queryText, options).fetchAll();
const response = await client
.database(databaseId)
.container(containerId)
.items.query(queryText, options)
.fetchAll();
const resultDocumentTitle = `query results for ${containerId}`;
if (populateQueryMetrics === true) {
await vscodeUtil.showNewFile(JSON.stringify({
result: response.resources,
queryMetrics: response.queryMetrics
}, undefined, 2), resultDocumentTitle, ".json", ViewColumn.Beside);
await vscodeUtil.showNewFile(
JSON.stringify(
{
result: response.resources,
queryMetrics: response.queryMetrics,
},
undefined,
2,
),
resultDocumentTitle,
'.json',
ViewColumn.Beside,
);
} else {
await vscodeUtil.showNewFile(JSON.stringify(response.resources, undefined, 2), resultDocumentTitle, ".json", ViewColumn.Beside);
await vscodeUtil.showNewFile(
JSON.stringify(response.resources, undefined, 2),
resultDocumentTitle,
'.json',
ViewColumn.Beside,
);
}
}
}

Просмотреть файл

@ -3,16 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { ViewColumn } from "vscode";
import { KeyValueStore } from "../../KeyValueStore";
import { localize } from "../../utils/localize";
import * as vscodeUtil from "../../utils/vscodeUtils";
import { NoSqlQueryConnection, noSqlQueryConnectionKey } from "../NoSqlCodeLensProvider";
import { CosmosDBCredential, getCosmosClient } from "../getCosmosClient";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ViewColumn } from 'vscode';
import { KeyValueStore } from '../../KeyValueStore';
import { localize } from '../../utils/localize';
import * as vscodeUtil from '../../utils/vscodeUtils';
import { noSqlQueryConnectionKey, type NoSqlQueryConnection } from '../NoSqlCodeLensProvider';
import { getCosmosClient, type CosmosDBCredential } from '../getCosmosClient';
export async function getNoSqlQueryPlan(_context: IActionContext, args: { queryText: string } | undefined): Promise<void> {
export async function getNoSqlQueryPlan(
_context: IActionContext,
args: { queryText: string } | undefined,
): Promise<void> {
let queryText: string;
if (!args) {
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
@ -26,17 +29,23 @@ export async function getNoSqlQueryPlan(_context: IActionContext, args: { queryT
}
const connectedCollection = KeyValueStore.instance.get(noSqlQueryConnectionKey);
if (!connectedCollection) {
throw new Error("Unable to get query plan due to missing node data. Please connect to a Cosmos DB collection.");
throw new Error('Unable to get query plan due to missing node data. Please connect to a Cosmos DB collection.');
} else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { databaseId, containerId, endpoint, masterKey, isEmulator } = connectedCollection as NoSqlQueryConnection;
const { databaseId, containerId, endpoint, masterKey, isEmulator } =
connectedCollection as NoSqlQueryConnection;
const credentials: CosmosDBCredential[] = [];
if (masterKey !== undefined) {
credentials.push({ type: "key", key: masterKey });
credentials.push({ type: 'key', key: masterKey });
}
credentials.push({ type: "auth" });
credentials.push({ type: 'auth' });
const client = getCosmosClient(endpoint, credentials, isEmulator);
const response = await client.database(databaseId).container(containerId).getQueryPlan(queryText);
await vscodeUtil.showNewFile(JSON.stringify(response.result, undefined, 2), `query results for ${containerId}`, ".json", ViewColumn.Beside);
await vscodeUtil.showNewFile(
JSON.stringify(response.result, undefined, 2),
`query results for ${containerId}`,
'.json',
ViewColumn.Beside,
);
}
}

Просмотреть файл

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { ext } from "../../extensionVariables";
import { DocDBStoredProcedureTreeItem } from "../tree/DocDBStoredProcedureTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { ext } from '../../extensionVariables';
import { DocDBStoredProcedureTreeItem } from '../tree/DocDBStoredProcedureTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function openStoredProcedure(context: IActionContext, node?: DocDBStoredProcedureTreeItem): Promise<void> {
if (!node) {

Просмотреть файл

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { ext } from "../../extensionVariables";
import { DocDBTriggerTreeItem } from "../tree/DocDBTriggerTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { ext } from '../../extensionVariables';
import { DocDBTriggerTreeItem } from '../tree/DocDBTriggerTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function openTrigger(context: IActionContext, node?: DocDBTriggerTreeItem): Promise<void> {
if (!node) {

Просмотреть файл

@ -3,18 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils";
import { sqlFilter } from "../../constants";
import { ext } from "../../extensionVariables";
import { type AzExtTreeItem, type IActionContext } from '@microsoft/vscode-azext-utils';
import { sqlFilter } from '../../constants';
import { ext } from '../../extensionVariables';
export async function pickDocDBAccount<T extends AzExtTreeItem>(
context: IActionContext,
expectedContextValue?: string | RegExp | (string | RegExp)[]
expectedContextValue?: string | RegExp | (string | RegExp)[],
): Promise<T> {
return await ext.rgApi.pickAppResource<T>(context, {
filter: [
sqlFilter
],
expectedChildContextValue: expectedContextValue
filter: [sqlFilter],
expectedChildContextValue: expectedContextValue,
});
}

Просмотреть файл

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import * as vscodeUtil from "../../utils/vscodeUtils";
import { DocDBCollectionTreeItem } from "../tree/DocDBCollectionTreeItem";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import * as vscodeUtil from '../../utils/vscodeUtils';
import { DocDBCollectionTreeItem } from '../tree/DocDBCollectionTreeItem';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function viewDocDBCollectionOffer(context: IActionContext, node?: DocDBCollectionTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;
@ -16,5 +16,5 @@ export async function viewDocDBCollectionOffer(context: IActionContext, node?: D
}
const client = node.root.getCosmosClient();
const offer = await node.getContainerClient(client).readOffer();
await vscodeUtil.showNewFile(JSON.stringify(offer.resource, undefined, 2), `offer of ${node.label}`, ".json");
await vscodeUtil.showNewFile(JSON.stringify(offer.resource, undefined, 2), `offer of ${node.label}`, '.json');
}

Просмотреть файл

@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscodeUtil from "../../utils/vscodeUtils";
import { DocDBCollectionTreeItem } from "../tree/DocDBCollectionTreeItem";
import { setConnectedNoSqlContainer } from "./connectNoSqlContainer";
import { pickDocDBAccount } from "./pickDocDBAccount";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscodeUtil from '../../utils/vscodeUtils';
import { DocDBCollectionTreeItem } from '../tree/DocDBCollectionTreeItem';
import { setConnectedNoSqlContainer } from './connectNoSqlContainer';
import { pickDocDBAccount } from './pickDocDBAccount';
export async function writeNoSqlQuery(context: IActionContext, node?: DocDBCollectionTreeItem): Promise<void> {
if (!node) {
@ -15,5 +15,5 @@ export async function writeNoSqlQuery(context: IActionContext, node?: DocDBColle
}
setConnectedNoSqlContainer(node);
const sampleQuery = `SELECT * FROM ${node.id}`;
await vscodeUtil.showNewFile(sampleQuery, `query for ${node.label}`, ".nosql");
await vscodeUtil.showNewFile(sampleQuery, `query for ${node.label}`, '.nosql');
}

Просмотреть файл

@ -3,42 +3,48 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CosmosClient } from "@azure/cosmos";
import { appendExtensionUserAgent } from "@microsoft/vscode-azext-utils";
import * as https from "https";
import { CosmosClient } from '@azure/cosmos';
import { appendExtensionUserAgent } from '@microsoft/vscode-azext-utils';
import * as https from 'https';
import * as vscode from 'vscode';
import { ext } from "../extensionVariables";
import { ext } from '../extensionVariables';
// eslint-disable-next-line import/no-internal-modules
import { getSessionFromVSCode } from "@microsoft/vscode-azext-azureauth/out/src/getSessionFromVSCode";
import { getSessionFromVSCode } from '@microsoft/vscode-azext-azureauth/out/src/getSessionFromVSCode';
export type CosmosDBKeyCredential = {
type: "key";
type: 'key';
key: string;
};
export type CosmosDBAuthCredential = {
type: "auth";
type: 'auth';
};
export type CosmosDBCredential = CosmosDBKeyCredential | CosmosDBAuthCredential;
export function getCosmosKeyCredential(credentials: CosmosDBCredential[]): CosmosDBKeyCredential | undefined {
return credentials.filter((cred): cred is CosmosDBKeyCredential => cred.type === "key")[0];
return credentials.filter((cred): cred is CosmosDBKeyCredential => cred.type === 'key')[0];
}
export function getCosmosAuthCredential(credentials: CosmosDBCredential[]): CosmosDBAuthCredential | undefined {
return credentials.filter((cred): cred is CosmosDBAuthCredential => cred.type === "auth")[0];
return credentials.filter((cred): cred is CosmosDBAuthCredential => cred.type === 'auth')[0];
}
export function getCosmosClient(
endpoint: string,
credentials: CosmosDBCredential[],
isEmulator: boolean | undefined
isEmulator: boolean | undefined,
): CosmosClient {
const vscodeStrictSSL: boolean | undefined = vscode.workspace.getConfiguration().get<boolean>(ext.settingsKeys.vsCode.proxyStrictSSL);
const enableEndpointDiscovery: boolean | undefined = vscode.workspace.getConfiguration().get<boolean>(ext.settingsKeys.enableEndpointDiscovery);
const connectionPolicy = { enableEndpointDiscovery: (enableEndpointDiscovery === undefined) ? true : enableEndpointDiscovery };
const vscodeStrictSSL: boolean | undefined = vscode.workspace
.getConfiguration()
.get<boolean>(ext.settingsKeys.vsCode.proxyStrictSSL);
const enableEndpointDiscovery: boolean | undefined = vscode.workspace
.getConfiguration()
.get<boolean>(ext.settingsKeys.enableEndpointDiscovery);
const connectionPolicy = {
enableEndpointDiscovery: enableEndpointDiscovery === undefined ? true : enableEndpointDiscovery,
};
const keyCred = getCosmosKeyCredential(credentials);
const authCred = getCosmosAuthCredential(credentials);
@ -47,13 +53,13 @@ export function getCosmosClient(
endpoint,
userAgentSuffix: appendExtensionUserAgent(),
agent: new https.Agent({ rejectUnauthorized: isEmulator ? !isEmulator : vscodeStrictSSL }),
connectionPolicy
connectionPolicy,
};
// @todo: Add telemetry to monitor usage of each credential type
if (keyCred) {
return new CosmosClient({
...commonProperties,
key: keyCred.key
key: keyCred.key,
});
} else if (authCred) {
return new CosmosClient({
@ -62,13 +68,13 @@ export function getCosmosClient(
getToken: async (scopes, _options) => {
const session = await getSessionFromVSCode(scopes, undefined, { createIfNone: true });
return {
token: session?.accessToken ?? "",
expiresOnTimestamp: 0
token: session?.accessToken ?? '',
expiresOnTimestamp: 0,
};
}
}
},
},
});
} else {
throw Error("No credential available to create CosmosClient.");
throw Error('No credential available to create CosmosClient.');
}
}

Просмотреть файл

@ -3,39 +3,39 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerCommand, registerCommandWithTreeNodeUnwrapping } from "@microsoft/vscode-azext-utils";
import { languages } from "vscode";
import { doubleClickDebounceDelay } from "../constants";
import { ext } from "../extensionVariables";
import { NoSqlCodeLensProvider } from "./NoSqlCodeLensProvider";
import { connectNoSqlContainer } from "./commands/connectNoSqlContainer";
import { createDocDBCollection } from "./commands/createDocDBCollection";
import { createDocDBDatabase } from "./commands/createDocDBDatabase";
import { createDocDBDocument } from "./commands/createDocDBDocument";
import { createDocDBStoredProcedure } from "./commands/createDocDBStoredProcedure";
import { createDocDBTrigger } from "./commands/createDocDBTrigger";
import { deleteDocDBCollection } from "./commands/deleteDocDBCollection";
import { deleteDocDBDatabase } from "./commands/deleteDocDBDatabase";
import { deleteDocDBDocument } from "./commands/deleteDocDBDocument";
import { deleteDocDBStoredProcedure } from "./commands/deleteDocDBStoredProcedure";
import { deleteDocDBTrigger } from "./commands/deleteDocDBTrigger";
import { executeDocDBStoredProcedure } from "./commands/executeDocDBStoredProcedure";
import { executeNoSqlQuery } from "./commands/executeNoSqlQuery";
import { getNoSqlQueryPlan } from "./commands/getNoSqlQueryPlan";
import { openStoredProcedure } from "./commands/openStoredProcedure";
import { openTrigger } from "./commands/openTrigger";
import { viewDocDBCollectionOffer } from "./commands/viewDocDBCollectionOffer";
import { writeNoSqlQuery } from "./commands/writeNoSqlQuery";
import { registerCommand, registerCommandWithTreeNodeUnwrapping } from '@microsoft/vscode-azext-utils';
import { languages } from 'vscode';
import { doubleClickDebounceDelay } from '../constants';
import { ext } from '../extensionVariables';
import { NoSqlCodeLensProvider } from './NoSqlCodeLensProvider';
import { connectNoSqlContainer } from './commands/connectNoSqlContainer';
import { createDocDBCollection } from './commands/createDocDBCollection';
import { createDocDBDatabase } from './commands/createDocDBDatabase';
import { createDocDBDocument } from './commands/createDocDBDocument';
import { createDocDBStoredProcedure } from './commands/createDocDBStoredProcedure';
import { createDocDBTrigger } from './commands/createDocDBTrigger';
import { deleteDocDBCollection } from './commands/deleteDocDBCollection';
import { deleteDocDBDatabase } from './commands/deleteDocDBDatabase';
import { deleteDocDBDocument } from './commands/deleteDocDBDocument';
import { deleteDocDBStoredProcedure } from './commands/deleteDocDBStoredProcedure';
import { deleteDocDBTrigger } from './commands/deleteDocDBTrigger';
import { executeDocDBStoredProcedure } from './commands/executeDocDBStoredProcedure';
import { executeNoSqlQuery } from './commands/executeNoSqlQuery';
import { getNoSqlQueryPlan } from './commands/getNoSqlQueryPlan';
import { openStoredProcedure } from './commands/openStoredProcedure';
import { openTrigger } from './commands/openTrigger';
import { viewDocDBCollectionOffer } from './commands/viewDocDBCollectionOffer';
import { writeNoSqlQuery } from './commands/writeNoSqlQuery';
const nosqlLanguageId = "nosql";
const nosqlLanguageId = 'nosql';
export function registerDocDBCommands(): void {
ext.noSqlCodeLensProvider = new NoSqlCodeLensProvider();
ext.context.subscriptions.push(languages.registerCodeLensProvider(nosqlLanguageId, ext.noSqlCodeLensProvider));
registerCommand("cosmosDB.connectNoSqlContainer", connectNoSqlContainer);
registerCommand("cosmosDB.executeNoSqlQuery", executeNoSqlQuery);
registerCommand("cosmosDB.getNoSqlQueryPlan", getNoSqlQueryPlan);
registerCommand('cosmosDB.connectNoSqlContainer', connectNoSqlContainer);
registerCommand('cosmosDB.executeNoSqlQuery', executeNoSqlQuery);
registerCommand('cosmosDB.getNoSqlQueryPlan', getNoSqlQueryPlan);
// #region Account command
@ -54,7 +54,7 @@ export function registerDocDBCommands(): void {
registerCommandWithTreeNodeUnwrapping('cosmosDB.writeNoSqlQuery', writeNoSqlQuery);
registerCommandWithTreeNodeUnwrapping('cosmosDB.deleteDocDBCollection', deleteDocDBCollection);
registerCommandWithTreeNodeUnwrapping("cosmosDB.viewDocDBCollectionOffer", viewDocDBCollectionOffer);
registerCommandWithTreeNodeUnwrapping('cosmosDB.viewDocDBCollectionOffer', viewDocDBCollectionOffer);
// #endregion
@ -78,7 +78,11 @@ export function registerDocDBCommands(): void {
// #region StoredProcedure command
registerCommandWithTreeNodeUnwrapping('cosmosDB.openStoredProcedure', openStoredProcedure, doubleClickDebounceDelay);
registerCommandWithTreeNodeUnwrapping(
'cosmosDB.openStoredProcedure',
openStoredProcedure,
doubleClickDebounceDelay,
);
registerCommandWithTreeNodeUnwrapping('cosmosDB.deleteDocDBStoredProcedure', deleteDocDBStoredProcedure);
registerCommandWithTreeNodeUnwrapping('cosmosDB.executeDocDBStoredProcedure', executeDocDBStoredProcedure);

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DatabaseDefinition, Resource } from '@azure/cosmos';
import { type DatabaseDefinition, type Resource } from '@azure/cosmos';
import { DocDBAccountTreeItemBase } from './DocDBAccountTreeItemBase';
import { DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { DocDBDatabaseTreeItem } from './DocDBDatabaseTreeItem';
@ -13,7 +13,7 @@ import { DocDBStoredProceduresTreeItem } from './DocDBStoredProceduresTreeItem';
import { DocDBStoredProcedureTreeItem } from './DocDBStoredProcedureTreeItem';
export class DocDBAccountTreeItem extends DocDBAccountTreeItemBase {
public static contextValue: string = "cosmosDBDocumentServer";
public static contextValue: string = 'cosmosDBDocumentServer';
public contextValue: string = DocDBAccountTreeItem.contextValue;
public initChild(resource: DatabaseDefinition & Resource): DocDBDatabaseTreeItem {

Просмотреть файл

@ -3,16 +3,27 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DatabaseAccountGetResults } from '@azure/arm-cosmosdb/src/models';
import { CosmosClient, DatabaseDefinition, DatabaseResponse, FeedOptions, QueryIterator, Resource } from '@azure/cosmos';
import { AzExtParentTreeItem, AzExtTreeItem, ICreateChildImplContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { IDeleteWizardContext } from '../../commands/deleteDatabaseAccount/IDeleteWizardContext';
import { type DatabaseAccountGetResults } from '@azure/arm-cosmosdb/src/models';
import {
type CosmosClient,
type DatabaseDefinition,
type DatabaseResponse,
type FeedOptions,
type QueryIterator,
type Resource,
} from '@azure/cosmos';
import {
type AzExtParentTreeItem,
type AzExtTreeItem,
type ICreateChildImplContext,
} from '@microsoft/vscode-azext-utils';
import type * as vscode from 'vscode';
import { type IDeleteWizardContext } from '../../commands/deleteDatabaseAccount/IDeleteWizardContext';
import { deleteCosmosDBAccount } from '../../commands/deleteDatabaseAccount/deleteCosmosDBAccount';
import { getThemeAgnosticIconPath, SERVERLESS_CAPABILITY_NAME } from '../../constants';
import { nonNullProp } from '../../utils/nonNull';
import { rejectOnTimeout } from '../../utils/timeout';
import { CosmosDBCredential, getCosmosClient, getCosmosKeyCredential } from '../getCosmosClient';
import { getCosmosClient, getCosmosKeyCredential, type CosmosDBCredential } from '../getCosmosClient';
import { getSignedInPrincipalIdForAccountEndpoint } from '../utils/azureSessionHelper';
import { ensureRbacPermission, isRbacException, showRbacPermissionError } from '../utils/rbacUtils';
import { DocDBTreeItemBase } from './DocDBTreeItemBase';
@ -23,7 +34,7 @@ import { DocDBTreeItemBase } from './DocDBTreeItemBase';
*/
export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<DatabaseDefinition & Resource> {
public readonly label: string;
public readonly childTypeLabel: string = "Database";
public readonly childTypeLabel: string = 'Database';
private hasShownRbacNotification: boolean = false;
constructor(
@ -33,7 +44,7 @@ export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<Databas
endpoint: string,
credentials: CosmosDBCredential[],
isEmulator: boolean | undefined,
readonly databaseAccount?: DatabaseAccountGetResults
readonly databaseAccount?: DatabaseAccountGetResults,
) {
super(parent);
this.id = id;
@ -42,11 +53,11 @@ export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<Databas
endpoint,
credentials,
isEmulator,
getCosmosClient: () => getCosmosClient(endpoint, credentials, isEmulator)
getCosmosClient: () => getCosmosClient(endpoint, credentials, isEmulator),
};
const keys = credentials
.map((cred) => cred.type === "key" ? cred.key : undefined)
.map((cred) => (cred.type === 'key' ? cred.key : undefined))
.filter((value): value is string => value !== undefined);
this.valuesToMask.push(id, endpoint, ...keys);
}
@ -65,8 +76,9 @@ export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<Databas
}
public get isServerless(): boolean {
return this.databaseAccount?.capabilities ? this.databaseAccount.capabilities.some(cap => cap.name === SERVERLESS_CAPABILITY_NAME) : false;
return this.databaseAccount?.capabilities
? this.databaseAccount.capabilities.some((cap) => cap.name === SERVERLESS_CAPABILITY_NAME)
: false;
}
public getIterator(client: CosmosClient, feedOptions: FeedOptions): QueryIterator<DatabaseDefinition & Resource> {
@ -77,7 +89,7 @@ export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<Databas
const databaseName = await context.ui.showInputBox({
placeHolder: 'Database Name',
validateInput: validateDatabaseName,
stepName: 'createDatabase'
stepName: 'createDatabase',
});
const client = this.root.getCosmosClient();
@ -87,17 +99,22 @@ export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<Databas
public async loadMoreChildrenImpl(clearCache: boolean): Promise<AzExtTreeItem[]> {
if (this.root.isEmulator) {
const unableToReachEmulatorMessage: string = "Unable to reach emulator. Please ensure it is started and connected to the port specified by the 'cosmosDB.emulator.port' setting, then try again.";
return await rejectOnTimeout(2000, () => super.loadMoreChildrenImpl(clearCache), unableToReachEmulatorMessage);
const unableToReachEmulatorMessage: string =
"Unable to reach emulator. Please ensure it is started and connected to the port specified by the 'cosmosDB.emulator.port' setting, then try again.";
return await rejectOnTimeout(
2000,
() => super.loadMoreChildrenImpl(clearCache),
unableToReachEmulatorMessage,
);
} else {
try {
return await super.loadMoreChildrenImpl(clearCache);
} catch (e) {
if (e instanceof Error && isRbacException(e) && !this.hasShownRbacNotification) {
this.hasShownRbacNotification = true;
const principalId = await getSignedInPrincipalIdForAccountEndpoint(this.root.endpoint) ?? '';
const principalId = (await getSignedInPrincipalIdForAccountEndpoint(this.root.endpoint)) ?? '';
// chedck if the principal ID matches the one that is signed in, otherwise this might be a security problem, hence show the error message
if (e.message.includes(`[${principalId}]`) && await ensureRbacPermission(this, principalId)) {
if (e.message.includes(`[${principalId}]`) && (await ensureRbacPermission(this, principalId))) {
return await super.loadMoreChildrenImpl(clearCache);
} else {
void showRbacPermissionError(this.fullId, principalId);
@ -115,10 +132,10 @@ export abstract class DocDBAccountTreeItemBase extends DocDBTreeItemBase<Databas
function validateDatabaseName(name: string): string | undefined | null {
if (!name || name.length < 1 || name.length > 255) {
return "Name has to be between 1 and 255 chars long";
return 'Name has to be between 1 and 255 chars long';
}
if (name.endsWith(" ")) {
return "Database name cannot end with space";
if (name.endsWith(' ')) {
return 'Database name cannot end with space';
}
if (/[/\\?#=]/.test(name)) {
return `Database name cannot contain the characters '\\', '/', '#', '?', '='`;

Просмотреть файл

@ -3,23 +3,35 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Container, ContainerDefinition, CosmosClient, PartitionKeyDefinition, Resource } from '@azure/cosmos';
import { AzExtParentTreeItem, AzExtTreeItem, DialogResponses, IActionContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import {
type Container,
type ContainerDefinition,
type CosmosClient,
type PartitionKeyDefinition,
type Resource,
} from '@azure/cosmos';
import {
AzExtParentTreeItem,
DialogResponses,
type AzExtTreeItem,
type IActionContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { DocDBDatabaseTreeItem } from './DocDBDatabaseTreeItem';
import { type DocDBDatabaseTreeItem } from './DocDBDatabaseTreeItem';
import { DocDBDocumentTreeItem } from './DocDBDocumentTreeItem';
import { DocDBDocumentsTreeItem } from './DocDBDocumentsTreeItem';
import { DocDBStoredProcedureTreeItem } from './DocDBStoredProcedureTreeItem';
import { DocDBStoredProceduresTreeItem } from './DocDBStoredProceduresTreeItem';
import { DocDBTriggerTreeItem } from './DocDBTriggerTreeItem';
import { DocDBTriggersTreeItem } from './DocDBTriggersTreeItem';
import { IDocDBTreeRoot } from './IDocDBTreeRoot';
import { type IDocDBTreeRoot } from './IDocDBTreeRoot';
/**
* Represents a DocumentDB collection
*/
export class DocDBCollectionTreeItem extends AzExtParentTreeItem {
public static contextValue: string = "cosmosDBDocumentCollection";
public static contextValue: string = 'cosmosDBDocumentCollection';
public readonly contextValue: string = DocDBCollectionTreeItem.contextValue;
public readonly parent: DocDBDatabaseTreeItem;
@ -27,7 +39,10 @@ export class DocDBCollectionTreeItem extends AzExtParentTreeItem {
private readonly _storedProceduresTreeItem: DocDBStoredProceduresTreeItem;
private readonly _triggersTreeItem: DocDBTriggersTreeItem;
constructor(parent: DocDBDatabaseTreeItem, private _container: ContainerDefinition & Resource) {
constructor(
parent: DocDBDatabaseTreeItem,
private _container: ContainerDefinition & Resource,
) {
super(parent);
this.parent = parent;
this.documentsTreeItem = new DocDBDocumentsTreeItem(this);
@ -61,7 +76,11 @@ export class DocDBCollectionTreeItem extends AzExtParentTreeItem {
public async deleteTreeItemImpl(context: IActionContext): Promise<void> {
const message: string = `Are you sure you want to delete collection '${this.label}' and its contents?`;
await context.ui.showWarningMessage(message, { modal: true, stepName: 'deleteCollection' }, DialogResponses.deleteResponse);
await context.ui.showWarningMessage(
message,
{ modal: true, stepName: 'deleteCollection' },
DialogResponses.deleteResponse,
);
const client = this.root.getCosmosClient();
await this.getContainerClient(client).delete();
}
@ -94,6 +113,6 @@ export class DocDBCollectionTreeItem extends AzExtParentTreeItem {
}
public getContainerClient(client: CosmosClient): Container {
return (this.parent.getDatabaseClient(client)).container(this.id);
return this.parent.getDatabaseClient(client).container(this.id);
}
}

Просмотреть файл

@ -3,12 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ContainerDefinition, CosmosClient, Database, Resource } from '@azure/cosmos';
import { type ContainerDefinition, type CosmosClient, type Database, type Resource } from '@azure/cosmos';
import { DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { DocDBDatabaseTreeItemBase } from './DocDBDatabaseTreeItemBase';
export class DocDBDatabaseTreeItem extends DocDBDatabaseTreeItemBase {
public static contextValue: string = "cosmosDBDocumentDatabase";
public static contextValue: string = 'cosmosDBDocumentDatabase';
public readonly contextValue: string = DocDBDatabaseTreeItem.contextValue;
public readonly childTypeLabel: string = 'Collection';

Просмотреть файл

@ -3,11 +3,26 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ContainerDefinition, ContainerResponse, CosmosClient, DatabaseDefinition, FeedOptions, QueryIterator, RequestOptions, Resource } from '@azure/cosmos';
import { AzExtTreeItem, DialogResponses, IActionContext, ICreateChildImplContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import {
type ContainerDefinition,
type ContainerResponse,
type CosmosClient,
type DatabaseDefinition,
type FeedOptions,
type QueryIterator,
type RequestOptions,
type Resource,
} from '@azure/cosmos';
import {
DialogResponses,
type AzExtTreeItem,
type IActionContext,
type ICreateChildImplContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { nonNullProp } from '../../utils/nonNull';
import { DocDBAccountTreeItemBase } from './DocDBAccountTreeItemBase';
import { type DocDBAccountTreeItemBase } from './DocDBAccountTreeItemBase';
import { DocDBTreeItemBase } from './DocDBTreeItemBase';
const minThroughputFixed: number = 400;
@ -60,7 +75,11 @@ export abstract class DocDBDatabaseTreeItemBase extends DocDBTreeItemBase<Contai
// Delete the database
public async deleteTreeItemImpl(context: IActionContext): Promise<void> {
const message: string = `Are you sure you want to delete database '${this.label}' and its contents?`;
await context.ui.showWarningMessage(message, { modal: true, stepName: 'deleteDatabase' }, DialogResponses.deleteResponse);
await context.ui.showWarningMessage(
message,
{ modal: true, stepName: 'deleteDatabase' },
DialogResponses.deleteResponse,
);
const client = this.root.getCosmosClient();
await client.database(this.id).delete();
}
@ -70,30 +89,32 @@ export abstract class DocDBDatabaseTreeItemBase extends DocDBTreeItemBase<Contai
const containerName = await context.ui.showInputBox({
placeHolder: `Enter an id for your ${this.childTypeLabel}`,
validateInput: validateCollectionName,
stepName: `create${this.childTypeLabel}`
stepName: `create${this.childTypeLabel}`,
});
const containerDefinition: ContainerDefinition = {
id: containerName
id: containerName,
};
const partitionKey = await this.getNewPartitionKey(context);
if (partitionKey) {
containerDefinition.partitionKey = {
paths: [partitionKey]
paths: [partitionKey],
};
}
const options: RequestOptions = {};
if (!this.parent.isServerless) {
const isFixed: boolean = !(containerDefinition.partitionKey);
const isFixed: boolean = !containerDefinition.partitionKey;
const minThroughput = isFixed ? minThroughputFixed : minThroughputPartitioned;
const throughput: number = Number(await context.ui.showInputBox({
value: minThroughput.toString(),
prompt: `Initial throughput capacity, between ${minThroughput} and ${maxThroughput} inclusive in increments of ${throughputStepSize}. Enter 0 if the account doesn't support throughput.`,
stepName: 'throughputCapacity',
validateInput: (input: string) => validateThroughput(isFixed, input)
}));
const throughput: number = Number(
await context.ui.showInputBox({
value: minThroughput.toString(),
prompt: `Initial throughput capacity, between ${minThroughput} and ${maxThroughput} inclusive in increments of ${throughputStepSize}. Enter 0 if the account doesn't support throughput.`,
stepName: 'throughputCapacity',
validateInput: (input: string) => validateThroughput(isFixed, input),
}),
);
if (throughput !== 0) {
options.offerThroughput = throughput;
@ -102,7 +123,9 @@ export abstract class DocDBDatabaseTreeItemBase extends DocDBTreeItemBase<Contai
context.showCreatingTreeItem(containerName);
const client = this.root.getCosmosClient();
const container: ContainerResponse = await client.database(this.id).containers.create(containerDefinition, options);
const container: ContainerResponse = await client
.database(this.id)
.containers.create(containerDefinition, options);
return this.initChild(nonNullProp(container, 'resource'));
}
@ -112,7 +135,7 @@ export abstract class DocDBDatabaseTreeItemBase extends DocDBTreeItemBase<Contai
prompt: 'Enter the partition key for the collection, or leave blank for fixed size.',
stepName: 'partitionKeyForCollection',
validateInput: this.validatePartitionKey,
placeHolder: 'e.g. /address/zipCode'
placeHolder: 'e.g. /address/zipCode',
});
if (partitionKey && partitionKey.length && partitionKey[0] !== '/') {
@ -124,14 +147,14 @@ export abstract class DocDBDatabaseTreeItemBase extends DocDBTreeItemBase<Contai
protected validatePartitionKey(key: string): string | undefined {
if (/[#?\\]/.test(key)) {
return "Cannot contain these characters: ?,#,\\, etc.";
return 'Cannot contain these characters: ?,#,\\, etc.';
}
return undefined;
}
}
function validateThroughput(isFixed: boolean, input: string): string | undefined | null {
if (input === "0") {
if (input === '0') {
return undefined;
}
@ -141,18 +164,18 @@ function validateThroughput(isFixed: boolean, input: string): string | undefined
if (value < minThroughput || value > maxThroughput || (value - minThroughput) % throughputStepSize !== 0) {
return `Value must be between ${minThroughput} and ${maxThroughput} in increments of ${throughputStepSize}`;
}
} catch (err) {
return "Input must be a number";
} catch {
return 'Input must be a number';
}
return undefined;
}
function validateCollectionName(name: string): string | undefined | null {
if (!name) {
return "Collection name cannot be empty";
return 'Collection name cannot be empty';
}
if (name.endsWith(" ")) {
return "Collection name cannot end with space";
if (name.endsWith(' ')) {
return 'Collection name cannot end with space';
}
if (/[/\\?#]/.test(name)) {
return `Collection name cannot contain the characters '\\', '/', '#', '?'`;

Просмотреть файл

@ -3,16 +3,21 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CosmosClient, Item, ItemDefinition, RequestOptions } from '@azure/cosmos';
import { AzExtTreeItem, DialogResponses, IActionContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import { type CosmosClient, type Item, type ItemDefinition, type RequestOptions } from '@azure/cosmos';
import {
AzExtTreeItem,
DialogResponses,
type IActionContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { IEditableTreeItem } from '../../DatabasesFileSystem';
import { type IEditableTreeItem } from '../../DatabasesFileSystem';
import { ext } from '../../extensionVariables';
import { nonNullProp } from '../../utils/nonNull';
import { getDocumentTreeItemLabel } from '../../utils/vscodeUtils';
import { DocDBDocumentsTreeItem } from './DocDBDocumentsTreeItem';
import { type DocDBDocumentsTreeItem } from './DocDBDocumentsTreeItem';
import { sanitizeId } from './DocDBUtils';
import { IDocDBTreeRoot } from './IDocDBTreeRoot';
import { type IDocDBTreeRoot } from './IDocDBTreeRoot';
const hiddenFields: string[] = ['_rid', '_self', '_etag', '_attachments', '_ts'];
@ -20,7 +25,7 @@ const hiddenFields: string[] = ['_rid', '_self', '_etag', '_attachments', '_ts']
* Represents a Cosmos DB DocumentDB (SQL) document
*/
export class DocDBDocumentTreeItem extends AzExtTreeItem implements IEditableTreeItem {
public static contextValue: string = "cosmosDBDocument";
public static contextValue: string = 'cosmosDBDocument';
public readonly contextValue: string = DocDBDocumentTreeItem.contextValue;
public readonly parent: DocDBDocumentsTreeItem;
public readonly cTime: number = Date.now();
@ -30,6 +35,7 @@ export class DocDBDocumentTreeItem extends AzExtTreeItem implements IEditableTre
constructor(parent: DocDBDocumentsTreeItem, document: ItemDefinition) {
super(parent);
this.parent = parent;
this._document = document;
this._label = getDocumentTreeItemLabel(this._document);
ext.fileSystem.fireChangedEvent(this);
@ -72,13 +78,18 @@ export class DocDBDocumentTreeItem extends AzExtTreeItem implements IEditableTre
public async deleteTreeItemImpl(context: IActionContext): Promise<void> {
const message: string = `Are you sure you want to delete document '${this.label}'?`;
await context.ui.showWarningMessage(message, { modal: true, stepName: 'deleteDocument' }, DialogResponses.deleteResponse);
await context.ui.showWarningMessage(
message,
{ modal: true, stepName: 'deleteDocument' },
DialogResponses.deleteResponse,
);
const client = this.root.getCosmosClient();
await this.getDocumentClient(client).delete();
}
public async getFileContent(): Promise<string> {
const clonedDoc: {} = { ...this.document };
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
const clonedDoc: Object = { ...this.document };
for (const field of hiddenFields) {
delete clonedDoc[field];
}
@ -95,7 +106,7 @@ export class DocDBDocumentTreeItem extends AzExtTreeItem implements IEditableTre
const client: CosmosClient = this.root.getCosmosClient();
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (["_etag"].some((element) => !newData[element])) {
if (['_etag'].some((element) => !newData[element])) {
throw new Error(`The "_self" and "_etag" fields are required to update a document`);
} else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
@ -108,7 +119,8 @@ export class DocDBDocumentTreeItem extends AzExtTreeItem implements IEditableTre
private getPartitionKeyValue(): string | number | undefined {
const partitionKey = this.parent.parent.partitionKey;
if (!partitionKey) { //Fixed collections -> no partitionKeyValue
if (!partitionKey) {
//Fixed collections -> no partitionKeyValue
return undefined;
}
const fields = partitionKey.paths[0].split('/');
@ -119,7 +131,8 @@ export class DocDBDocumentTreeItem extends AzExtTreeItem implements IEditableTre
for (const field of fields) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
value = value ? value[field] : this.document[field];
if (!value) { //Partition Key exists, but this document doesn't have a value
if (!value) {
//Partition Key exists, but this document doesn't have a value
return '';
}
}
@ -128,6 +141,8 @@ export class DocDBDocumentTreeItem extends AzExtTreeItem implements IEditableTre
}
private getDocumentClient(client: CosmosClient): Item {
return this.parent.getContainerClient(client).item(nonNullProp(this.document, 'id'), this.getPartitionKeyValue());
return this.parent
.getContainerClient(client)
.item(nonNullProp(this.document, 'id'), this.getPartitionKeyValue());
}
}

Просмотреть файл

@ -3,11 +3,22 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Container, CosmosClient, FeedOptions, ItemDefinition, ItemResponse, QueryIterator } from '@azure/cosmos';
import { IActionContext, ICreateChildImplContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import {
type Container,
type CosmosClient,
type FeedOptions,
type ItemDefinition,
type ItemResponse,
type QueryIterator,
} from '@azure/cosmos';
import {
type IActionContext,
type ICreateChildImplContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { nonNullProp } from '../../utils/nonNull';
import { DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { type DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { DocDBDocumentTreeItem } from './DocDBDocumentTreeItem';
import { DocDBTreeItemBase } from './DocDBTreeItemBase';
@ -15,9 +26,9 @@ import { DocDBTreeItemBase } from './DocDBTreeItemBase';
* This class provides logic for DocumentDB collections
*/
export class DocDBDocumentsTreeItem extends DocDBTreeItemBase<ItemDefinition> {
public static contextValue: string = "cosmosDBDocumentsGroup";
public static contextValue: string = 'cosmosDBDocumentsGroup';
public readonly contextValue: string = DocDBDocumentsTreeItem.contextValue;
public readonly childTypeLabel: string = "Documents";
public readonly childTypeLabel: string = 'Documents';
public readonly parent: DocDBCollectionTreeItem;
public suppressMaskLabel = true;
@ -31,11 +42,11 @@ export class DocDBDocumentsTreeItem extends DocDBTreeItemBase<ItemDefinition> {
}
public get id(): string {
return "$Documents";
return '$Documents';
}
public get label(): string {
return "Documents";
return 'Documents';
}
public get link(): string {
@ -51,11 +62,14 @@ export class DocDBDocumentsTreeItem extends DocDBTreeItemBase<ItemDefinition> {
}
public async createChildImpl(context: ICreateChildImplContext): Promise<DocDBDocumentTreeItem> {
let docID = await context.ui.showInputBox({ prompt: "Enter a document ID or leave blank for a generated ID", stepName: 'createDocument' });
let docID = await context.ui.showInputBox({
prompt: 'Enter a document ID or leave blank for a generated ID',
stepName: 'createDocument',
});
docID = docID.trim();
let body: ItemDefinition = { id: docID };
body = (await this.promptForPartitionKey(context, body));
body = await this.promptForPartitionKey(context, body);
context.showCreatingTreeItem(docID);
const item: ItemDefinition = await this.createDocument(body);
@ -63,11 +77,13 @@ export class DocDBDocumentsTreeItem extends DocDBTreeItemBase<ItemDefinition> {
}
public async createDocument(body: ItemDefinition): Promise<ItemDefinition> {
const item: ItemResponse<ItemDefinition> = await this.getContainerClient(this.root.getCosmosClient()).items.create(body);
const item: ItemResponse<ItemDefinition> = await this.getContainerClient(
this.root.getCosmosClient(),
).items.create(body);
return nonNullProp(item, 'resource');
}
public documentHasPartitionKey(doc: Object): boolean {
public documentHasPartitionKey(doc: object): boolean {
let interim = doc;
let partitionKey: string | undefined = this.parent.partitionKey && this.parent.partitionKey.paths[0];
if (!partitionKey) {
@ -95,7 +111,7 @@ export class DocDBDocumentsTreeItem extends DocDBTreeItemBase<ItemDefinition> {
if (partitionKey) {
const partitionKeyValue: string = await context.ui.showInputBox({
prompt: `Enter a value for the partition key ("${partitionKey}")`,
stepName: 'valueforParititionKey'
stepName: 'valueforParititionKey',
});
// Unlike delete/replace, createDocument does not accept a partition key value via an options parameter.
// We need to present the partitionKey value as part of the document contents
@ -109,14 +125,14 @@ export class DocDBDocumentsTreeItem extends DocDBTreeItemBase<ItemDefinition> {
}
// Create a nested Object given the partition key path and value
private createPartitionPathObject(partitionKey: string, partitionKeyValue: string): Object {
private createPartitionPathObject(partitionKey: string, partitionKeyValue: string): object {
//remove leading slash
if (partitionKey[0] === '/') {
partitionKey = partitionKey.slice(1);
}
const keyPath = partitionKey.split('/');
const PartitionPath: Object = {};
let interim: Object = PartitionPath;
const PartitionPath: object = {};
let interim: object = PartitionPath;
let i: number;
for (i = 0; i < keyPath.length - 1; i++) {
interim[keyPath[i]] = {};

Просмотреть файл

@ -3,27 +3,37 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Resource, StoredProcedureDefinition } from '@azure/cosmos';
import { AzExtTreeItem, DialogResponses, IActionContext, TreeItemIconPath, openReadOnlyJson, randomUtils } from '@microsoft/vscode-azext-utils';
import * as vscode from "vscode";
import { IEditableTreeItem } from '../../DatabasesFileSystem';
import { type Resource, type StoredProcedureDefinition } from '@azure/cosmos';
import {
AzExtTreeItem,
DialogResponses,
openReadOnlyJson,
randomUtils,
type IActionContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { type IEditableTreeItem } from '../../DatabasesFileSystem';
import { ext } from '../../extensionVariables';
import { localize } from '../../utils/localize';
import { nonNullProp } from '../../utils/nonNull';
import { DocDBStoredProceduresTreeItem } from './DocDBStoredProceduresTreeItem';
import { IDocDBTreeRoot } from './IDocDBTreeRoot';
import { type DocDBStoredProceduresTreeItem } from './DocDBStoredProceduresTreeItem';
import { type IDocDBTreeRoot } from './IDocDBTreeRoot';
/**
* Represents a Cosmos DB DocumentDB (SQL) stored procedure
*/
export class DocDBStoredProcedureTreeItem extends AzExtTreeItem implements IEditableTreeItem {
public static contextValue: string = "cosmosDBStoredProcedure";
public static contextValue: string = 'cosmosDBStoredProcedure';
public readonly contextValue: string = DocDBStoredProcedureTreeItem.contextValue;
public readonly cTime: number = Date.now();
public readonly parent: DocDBStoredProceduresTreeItem;
public mTime: number = Date.now();
constructor(parent: DocDBStoredProceduresTreeItem, public procedure: (StoredProcedureDefinition & Resource)) {
constructor(
parent: DocDBStoredProceduresTreeItem,
public procedure: StoredProcedureDefinition & Resource,
) {
super(parent);
ext.fileSystem.fireChangedEvent(this);
this.commandId = 'cosmosDB.openStoredProcedure';
@ -51,7 +61,6 @@ export class DocDBStoredProcedureTreeItem extends AzExtTreeItem implements IEdit
public async getFileContent(): Promise<string> {
return typeof this.procedure.body === 'string' ? this.procedure.body : '';
}
public async refreshImpl(): Promise<void> {
@ -60,7 +69,10 @@ export class DocDBStoredProcedureTreeItem extends AzExtTreeItem implements IEdit
public async writeFileContent(_context: IActionContext, content: string): Promise<void> {
const client = this.root.getCosmosClient();
const replace = await this.parent.getContainerClient(client).scripts.storedProcedure(this.id).replace({ id: this.id, body: content });
const replace = await this.parent
.getContainerClient(client)
.scripts.storedProcedure(this.id)
.replace({ id: this.id, body: content });
this.procedure = nonNullProp(replace, 'resource');
}
@ -69,20 +81,31 @@ export class DocDBStoredProcedureTreeItem extends AzExtTreeItem implements IEdit
}
public async deleteTreeItemImpl(context: IActionContext): Promise<void> {
const message: string = localize("deleteCosmosStoredProcedure", `Are you sure you want to delete stored procedure '{0}'?`, this.label);
await context.ui.showWarningMessage(message, { modal: true, stepName: 'deleteStoredProcedure' }, DialogResponses.deleteResponse);
const message: string = localize(
'deleteCosmosStoredProcedure',
`Are you sure you want to delete stored procedure '{0}'?`,
this.label,
);
await context.ui.showWarningMessage(
message,
{ modal: true, stepName: 'deleteStoredProcedure' },
DialogResponses.deleteResponse,
);
const client = this.root.getCosmosClient();
await this.parent.getContainerClient(client).scripts.storedProcedure(this.id).delete();
}
public async execute(context: IActionContext, partitionKey: string, parameters?: unknown[]): Promise<void> {
const client = this.root.getCosmosClient();
const result = await this.parent.getContainerClient(client).scripts.storedProcedure(this.id).execute(partitionKey, parameters);
const result = await this.parent
.getContainerClient(client)
.scripts.storedProcedure(this.id)
.execute(partitionKey, parameters);
try {
const resultFileName = `${this.label}-result`;
await openReadOnlyJson({ label: resultFileName, fullId: randomUtils.getRandomHexString() }, result);
} catch (error) {
} catch {
await context.ui.showWarningMessage(`Unable to parse execution result`);
}
}

Просмотреть файл

@ -3,14 +3,21 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Container, CosmosClient, FeedOptions, QueryIterator, Resource, StoredProcedureDefinition } from '@azure/cosmos';
import { AzExtTreeItem, ICreateChildImplContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import * as vscode from "vscode";
import {
type Container,
type CosmosClient,
type FeedOptions,
type QueryIterator,
type Resource,
type StoredProcedureDefinition,
} from '@azure/cosmos';
import { type AzExtTreeItem, type ICreateChildImplContext, type TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { defaultStoredProcedure } from '../../constants';
import { GraphCollectionTreeItem } from '../../graph/tree/GraphCollectionTreeItem';
import { type GraphCollectionTreeItem } from '../../graph/tree/GraphCollectionTreeItem';
import { localize } from '../../utils/localize';
import { nonNullProp } from '../../utils/nonNull';
import { DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { type DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { DocDBStoredProcedureTreeItem } from './DocDBStoredProcedureTreeItem';
import { DocDBTreeItemBase } from './DocDBTreeItemBase';
@ -18,9 +25,9 @@ import { DocDBTreeItemBase } from './DocDBTreeItemBase';
* This class represents the DocumentDB "Stored Procedures" node in the tree
*/
export class DocDBStoredProceduresTreeItem extends DocDBTreeItemBase<StoredProcedureDefinition> {
public static contextValue: string = "cosmosDBStoredProceduresGroup";
public static contextValue: string = 'cosmosDBStoredProceduresGroup';
public readonly contextValue: string = DocDBStoredProceduresTreeItem.contextValue;
public readonly childTypeLabel: string = "Stored Procedure";
public readonly childTypeLabel: string = 'Stored Procedure';
public readonly parent: DocDBCollectionTreeItem | GraphCollectionTreeItem;
public suppressMaskLabel = true;
@ -42,13 +49,15 @@ export class DocDBStoredProceduresTreeItem extends DocDBTreeItemBase<StoredProce
const currStoredProcedureList: AzExtTreeItem[] = await this.getCachedChildren(context);
const currStoredProcedureNames: string[] = [];
for (const sp of currStoredProcedureList) {
currStoredProcedureNames.push(nonNullProp(sp, "id"));
currStoredProcedureNames.push(nonNullProp(sp, 'id'));
}
const spID = (await context.ui.showInputBox({
prompt: "Enter a unique stored procedure ID",
stepName: 'createStoredProcedure',
validateInput: (name: string) => this.validateStoredProcedureName(name, currStoredProcedureNames)
})).trim();
const spID = (
await context.ui.showInputBox({
prompt: 'Enter a unique stored procedure ID',
stepName: 'createStoredProcedure',
validateInput: (name: string) => this.validateStoredProcedureName(name, currStoredProcedureNames),
})
).trim();
const body: StoredProcedureDefinition = { id: spID, body: defaultStoredProcedure };
context.showCreatingTreeItem(spID);
const sproc = await this.getContainerClient(client).scripts.storedProcedures.create(body);
@ -57,18 +66,21 @@ export class DocDBStoredProceduresTreeItem extends DocDBTreeItemBase<StoredProce
}
public get id(): string {
return "$StoredProcedures";
return '$StoredProcedures';
}
public get label(): string {
return "Stored Procedures";
return 'Stored Procedures';
}
public get link(): string {
return this.parent.link;
}
public getIterator(client: CosmosClient, feedOptions: FeedOptions): QueryIterator<StoredProcedureDefinition & Resource> {
public getIterator(
client: CosmosClient,
feedOptions: FeedOptions,
): QueryIterator<StoredProcedureDefinition & Resource> {
return this.getContainerClient(client).scripts.storedProcedures.readAll(feedOptions);
}
@ -78,14 +90,14 @@ export class DocDBStoredProceduresTreeItem extends DocDBTreeItemBase<StoredProce
private validateStoredProcedureName(name: string, currStoredProcedureNames: string[]): string | undefined {
if (name.length < 1 || name.length > 255) {
return localize("nameLength", "Name has to be between 1 and 255 chars long");
return localize('nameLength', 'Name has to be between 1 and 255 chars long');
}
if (/[/\\?#&]/.test(name)) {
return localize("illegalChars", "Name contains illegal chars: /, \\, ?, #, &");
return localize('illegalChars', 'Name contains illegal chars: /, \\, ?, #, &');
}
if (name[name.length - 1] === " ") {
return localize("endsWithSpace", "Name cannot end with a space.");
if (name[name.length - 1] === ' ') {
return localize('endsWithSpace', 'Name cannot end with a space.');
}
if (currStoredProcedureNames.includes(name)) {
return localize('nameExists', 'Stored Procedure "{0}" already exists.', name);

Просмотреть файл

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CosmosClient, FeedOptions, QueryIterator } from '@azure/cosmos';
import { AzExtParentTreeItem, AzExtTreeItem } from '@microsoft/vscode-azext-utils';
import { type CosmosClient, type FeedOptions, type QueryIterator } from '@azure/cosmos';
import { AzExtParentTreeItem, type AzExtTreeItem } from '@microsoft/vscode-azext-utils';
import { getBatchSizeSetting } from '../../utils/workspacUtils';
import { IDocDBTreeRoot } from './IDocDBTreeRoot';
import { type IDocDBTreeRoot } from './IDocDBTreeRoot';
/**
* This class provides common iteration logic for DocumentDB accounts, databases, and collections

Просмотреть файл

@ -3,28 +3,33 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Resource, TriggerDefinition } from '@azure/cosmos';
import { AzExtTreeItem, DialogResponses, IActionContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import * as vscode from "vscode";
import { IEditableTreeItem } from '../../DatabasesFileSystem';
import { type Resource, type TriggerDefinition } from '@azure/cosmos';
import {
AzExtTreeItem,
DialogResponses,
type IActionContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { type IEditableTreeItem } from '../../DatabasesFileSystem';
import { ext } from '../../extensionVariables';
import { localize } from '../../utils/localize';
import { nonNullProp } from '../../utils/nonNull';
import { DocDBTriggersTreeItem, getTriggerOperation, getTriggerType } from './DocDBTriggersTreeItem';
import { IDocDBTreeRoot } from './IDocDBTreeRoot';
import { getTriggerOperation, getTriggerType, type DocDBTriggersTreeItem } from './DocDBTriggersTreeItem';
import { type IDocDBTreeRoot } from './IDocDBTreeRoot';
/**
* Represents a Cosmos DB DocumentDB (SQL) trigger
*/
export class DocDBTriggerTreeItem extends AzExtTreeItem implements IEditableTreeItem {
public static contextValue: string = "cosmosDBTrigger";
public static contextValue: string = 'cosmosDBTrigger';
public readonly contextValue: string = DocDBTriggerTreeItem.contextValue;
public readonly cTime: number = Date.now();
public readonly parent: DocDBTriggersTreeItem;
public trigger: (TriggerDefinition & Resource);
public trigger: TriggerDefinition & Resource;
public mTime: number = Date.now();
constructor(parent: DocDBTriggersTreeItem, trigger: (TriggerDefinition & Resource)) {
constructor(parent: DocDBTriggersTreeItem, trigger: TriggerDefinition & Resource) {
super(parent);
this.trigger = trigger;
ext.fileSystem.fireChangedEvent(this);
@ -77,7 +82,7 @@ export class DocDBTriggerTreeItem extends AzExtTreeItem implements IEditableTree
id: this.id,
triggerType: triggerType,
triggerOperation: triggerOperation,
body: content
body: content,
});
this.trigger = nonNullProp(replace, 'resource');
}
@ -87,8 +92,16 @@ export class DocDBTriggerTreeItem extends AzExtTreeItem implements IEditableTree
}
public async deleteTreeItemImpl(context: IActionContext): Promise<void> {
const message: string = localize("deleteCosmosTrigger", `Are you sure you want to delete trigger '{0}'?`, this.label);
await context.ui.showWarningMessage(message, { modal: true, stepName: 'deleteTrigger' }, DialogResponses.deleteResponse);
const message: string = localize(
'deleteCosmosTrigger',
`Are you sure you want to delete trigger '{0}'?`,
this.label,
);
await context.ui.showWarningMessage(
message,
{ modal: true, stepName: 'deleteTrigger' },
DialogResponses.deleteResponse,
);
const client = this.root.getCosmosClient();
await this.parent.getContainerClient(client).scripts.trigger(this.id).delete();
}

Просмотреть файл

@ -3,14 +3,28 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Container, CosmosClient, FeedOptions, QueryIterator, Resource, TriggerDefinition, TriggerOperation, TriggerType } from '@azure/cosmos';
import { AzExtTreeItem, IActionContext, ICreateChildImplContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import * as vscode from "vscode";
import {
TriggerOperation,
TriggerType,
type Container,
type CosmosClient,
type FeedOptions,
type QueryIterator,
type Resource,
type TriggerDefinition,
} from '@azure/cosmos';
import {
type AzExtTreeItem,
type IActionContext,
type ICreateChildImplContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { defaultTrigger } from '../../constants';
import { GraphCollectionTreeItem } from '../../graph/tree/GraphCollectionTreeItem';
import { type GraphCollectionTreeItem } from '../../graph/tree/GraphCollectionTreeItem';
import { localize } from '../../utils/localize';
import { nonNullProp } from '../../utils/nonNull';
import { DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { type DocDBCollectionTreeItem } from './DocDBCollectionTreeItem';
import { DocDBTreeItemBase } from './DocDBTreeItemBase';
import { DocDBTriggerTreeItem } from './DocDBTriggerTreeItem';
@ -18,10 +32,9 @@ import { DocDBTriggerTreeItem } from './DocDBTriggerTreeItem';
* This class represents the DocumentDB "Triggers" node in the tree
*/
export class DocDBTriggersTreeItem extends DocDBTreeItemBase<TriggerDefinition> {
public static contextValue: string = "cosmosDBTriggersGroup";
public static contextValue: string = 'cosmosDBTriggersGroup';
public readonly contextValue: string = DocDBTriggersTreeItem.contextValue;
public readonly childTypeLabel: string = "Trigger";
public readonly childTypeLabel: string = 'Trigger';
public readonly parent: DocDBCollectionTreeItem;
public suppressMaskLabel = true;
@ -43,19 +56,26 @@ export class DocDBTriggersTreeItem extends DocDBTreeItemBase<TriggerDefinition>
const currTriggerList: AzExtTreeItem[] = await this.getCachedChildren(context);
const currTriggerNames: string[] = [];
for (const sp of currTriggerList) {
currTriggerNames.push(nonNullProp(sp, "id"));
currTriggerNames.push(nonNullProp(sp, 'id'));
}
const triggerID = (await context.ui.showInputBox({
prompt: "Enter a unique trigger ID",
stepName: 'createTrigger',
validateInput: (name: string) => this.validateTriggerName(name, currTriggerNames)
})).trim();
const triggerID = (
await context.ui.showInputBox({
prompt: 'Enter a unique trigger ID',
stepName: 'createTrigger',
validateInput: (name: string) => this.validateTriggerName(name, currTriggerNames),
})
).trim();
const triggerType = await getTriggerType(context);
const triggerOperation = await getTriggerOperation(context);
const body: TriggerDefinition = { id: triggerID, body: defaultTrigger, triggerType: triggerType, triggerOperation: triggerOperation };
const body: TriggerDefinition = {
id: triggerID,
body: defaultTrigger,
triggerType: triggerType,
triggerOperation: triggerOperation,
};
context.showCreatingTreeItem(triggerID);
const response = await this.getContainerClient(client).scripts.triggers.create(body);
@ -63,11 +83,11 @@ export class DocDBTriggersTreeItem extends DocDBTreeItemBase<TriggerDefinition>
}
public get id(): string {
return "$Triggers";
return '$Triggers';
}
public get label(): string {
return "Triggers";
return 'Triggers';
}
public get link(): string {
@ -84,14 +104,14 @@ export class DocDBTriggersTreeItem extends DocDBTreeItemBase<TriggerDefinition>
private validateTriggerName(name: string, currTriggerNames: string[]): string | undefined {
if (name.length < 1 || name.length > 255) {
return localize("nameLength", "Name has to be between 1 and 255 chars long");
return localize('nameLength', 'Name has to be between 1 and 255 chars long');
}
if (/[/\\?#&]/.test(name)) {
return localize("illegalChars", "Name contains illegal chars: /, \\, ?, #, &");
return localize('illegalChars', 'Name contains illegal chars: /, \\, ?, #, &');
}
if (name[name.length - 1] === " ") {
return localize("endsWithSpace", "Name cannot end with a space.");
if (name[name.length - 1] === ' ') {
return localize('endsWithSpace', 'Name cannot end with a space.');
}
if (currTriggerNames.includes(name)) {
return localize('nameExists', 'Trigger "{0}" already exists.', name);
@ -104,15 +124,15 @@ export class DocDBTriggersTreeItem extends DocDBTreeItemBase<TriggerDefinition>
export async function getTriggerType(context: IActionContext): Promise<TriggerType> {
const options = Object.keys(TriggerType).map((type) => ({ label: type }));
const triggerTypeOption = await context.ui.showQuickPick<vscode.QuickPickItem>(options, {
placeHolder: localize("createDocDBTriggerSelectType", "Select the trigger type")
placeHolder: localize('createDocDBTriggerSelectType', 'Select the trigger type'),
});
return triggerTypeOption.label === "Pre" ? TriggerType.Pre : TriggerType.Post;
return triggerTypeOption.label === 'Pre' ? TriggerType.Pre : TriggerType.Post;
}
export async function getTriggerOperation(context: IActionContext): Promise<TriggerOperation> {
const options = Object.keys(TriggerOperation).map((key) => ({ label: key }));
const triggerOperationOption = await context.ui.showQuickPick<vscode.QuickPickItem>(options, {
placeHolder: localize("createDocDBTriggerSelectOperation", "Select the trigger operation")
placeHolder: localize('createDocDBTriggerSelectOperation', 'Select the trigger operation'),
});
return TriggerOperation[triggerOperationOption.label as keyof typeof TriggerOperation];
}

Просмотреть файл

@ -1,11 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CosmosClient } from "@azure/cosmos";
import { CosmosDBCredential } from "../getCosmosClient";
import { type CosmosClient } from '@azure/cosmos';
import { type CosmosDBCredential } from '../getCosmosClient';
export interface IDocDBTreeRoot {
endpoint: string;

Просмотреть файл

@ -5,7 +5,7 @@
// eslint-disable-next-line import/no-internal-modules
import { getSessionFromVSCode } from '@microsoft/vscode-azext-azureauth/out/src/getSessionFromVSCode';
import * as vscode from "vscode";
import type * as vscode from 'vscode';
export async function getSignedInPrincipalIdForAccountEndpoint(accountEndpoint: string): Promise<string | undefined> {
const session = await getSessionForDatabaseAccount(accountEndpoint);

Просмотреть файл

@ -3,72 +3,114 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SqlRoleAssignmentCreateUpdateParameters } from '@azure/arm-cosmosdb';
import { type SqlRoleAssignmentCreateUpdateParameters } from '@azure/arm-cosmosdb';
import { getResourceGroupFromId } from '@microsoft/vscode-azext-azureutils';
import { callWithTelemetryAndErrorHandling, IActionContext, IAzureMessageOptions, ISubscriptionContext } from '@microsoft/vscode-azext-utils';
import {
callWithTelemetryAndErrorHandling,
type IActionContext,
type IAzureMessageOptions,
type ISubscriptionContext,
} from '@microsoft/vscode-azext-utils';
import { randomUUID } from 'crypto';
import * as vscode from 'vscode';
import { createCosmosDBClient } from '../../utils/azureClients';
import { getDatabaseAccountNameFromId } from '../../utils/azureUtils';
import { localize } from '../../utils/localize';
import { DocDBAccountTreeItemBase } from '../tree/DocDBAccountTreeItemBase';
import { type DocDBAccountTreeItemBase } from '../tree/DocDBAccountTreeItemBase';
export async function ensureRbacPermission(docDbItem: DocDBAccountTreeItemBase, principalId: string): Promise<boolean> {
return await callWithTelemetryAndErrorHandling('cosmosDB.addMissingRbacRole', async (context: IActionContext) => {
return (
(await callWithTelemetryAndErrorHandling('cosmosDB.addMissingRbacRole', async (context: IActionContext) => {
context.errorHandling.suppressDisplay = false;
context.errorHandling.rethrow = false;
context.errorHandling.suppressDisplay = false;
context.errorHandling.rethrow = false;
const accountName: string = getDatabaseAccountNameFromId(docDbItem.fullId);
if (await askForRbacPermissions(accountName, docDbItem.subscription.subscriptionDisplayName, context)) {
context.telemetry.properties.lastStep = 'addRbacContributorPermission';
const resourceGroup: string = getResourceGroupFromId(docDbItem.fullId);
const start: number = Date.now();
await addRbacContributorPermission(
accountName,
principalId,
resourceGroup,
context,
docDbItem.subscription,
);
//send duration of the previous call (in seconds) in addition to the duration of the whole event including user prompt
context.telemetry.measurements['createRoleAssignment'] = (Date.now() - start) / 1000;
const accountName: string = getDatabaseAccountNameFromId(docDbItem.fullId);
if (await askForRbacPermissions(accountName, docDbItem.subscription.subscriptionDisplayName, context)) {
context.telemetry.properties.lastStep = "addRbacContributorPermission";
const resourceGroup: string = getResourceGroupFromId(docDbItem.fullId);
const start: number = Date.now();
await addRbacContributorPermission(accountName, principalId, resourceGroup, context, docDbItem.subscription);
//send duration of the previous call (in seconds) in addition to the duration of the whole event including user prompt
context.telemetry.measurements["createRoleAssignment"] = (Date.now() - start) / 1000;
return true;
}
return false;
}) ?? false;
return true;
}
return false;
})) ?? false
);
}
export function isRbacException(error: Error): boolean {
return (error instanceof Error && error.message.includes("does not have required RBAC permissions to perform action"));
return (
error instanceof Error && error.message.includes('does not have required RBAC permissions to perform action')
);
}
export async function showRbacPermissionError(accountName: string, principalId: string): Promise<void> {
const message = localize("rbacPermissionErrorMsg", "You do not have the required permissions to access [{0}] with your principal Id [{1}].\nPlease contact the account owner to get the required permissions.", accountName, principalId);
const readMoreItem = localize("learnMore", "Learn More");
const message = localize(
'rbacPermissionErrorMsg',
'You do not have the required permissions to access [{0}] with your principal Id [{1}].\nPlease contact the account owner to get the required permissions.',
accountName,
principalId,
);
const readMoreItem = localize('learnMore', 'Learn More');
await vscode.window.showErrorMessage(message, { modal: false }, ...[readMoreItem]).then((item) => {
if (item === readMoreItem) {
void vscode.env.openExternal(vscode.Uri.parse("https://aka.ms/cosmos-native-rbac"));
void vscode.env.openExternal(vscode.Uri.parse('https://aka.ms/cosmos-native-rbac'));
}
});
}
async function askForRbacPermissions(databaseAccount: string, subscription: string, context: IActionContext): Promise<boolean> {
const message =
[localize("rbacMissingErrorMsg", "You need the 'Data Contributor' RBAC role permission to enable all Azure Databases Extension features for the selected account.\n\n"),
localize("rbacMissingErrorAccountName", "Account Name: {0}\n", databaseAccount),
localize("rbacMissingErrorSubscriptionName", "Subscription: {0}\n", subscription)
].join("");
const options: IAzureMessageOptions = { modal: true, detail: message, learnMoreLink: "https://aka.ms/cosmos-native-rbac", stepName: "askSetRbac" };
const setPermissionItem: vscode.MessageItem = { title: localize("rbacExtendPermissionBtn", "Extend RBAC permissions") };
async function askForRbacPermissions(
databaseAccount: string,
subscription: string,
context: IActionContext,
): Promise<boolean> {
const message = [
localize(
'rbacMissingErrorMsg',
"You need the 'Data Contributor' RBAC role permission to enable all Azure Databases Extension features for the selected account.\n\n",
),
localize('rbacMissingErrorAccountName', 'Account Name: {0}\n', databaseAccount),
localize('rbacMissingErrorSubscriptionName', 'Subscription: {0}\n', subscription),
].join('');
const options: IAzureMessageOptions = {
modal: true,
detail: message,
learnMoreLink: 'https://aka.ms/cosmos-native-rbac',
stepName: 'askSetRbac',
};
const setPermissionItem: vscode.MessageItem = {
title: localize('rbacExtendPermissionBtn', 'Extend RBAC permissions'),
};
const result = await context.ui.showWarningMessage(localize("rbacMissingErrorTitle", "No required RBAC permissions"), options, ...[setPermissionItem]);
const result = await context.ui.showWarningMessage(
localize('rbacMissingErrorTitle', 'No required RBAC permissions'),
options,
...[setPermissionItem],
);
return result === setPermissionItem;
}
async function addRbacContributorPermission(databaseAccount: string, principalId: string, resourceGroup: string, context: IActionContext, subscription: ISubscriptionContext): Promise<string | undefined> {
const defaultRoleId = "00000000-0000-0000-0000-000000000002"; // this is a predefined role with read and write access to data plane resources
async function addRbacContributorPermission(
databaseAccount: string,
principalId: string,
resourceGroup: string,
context: IActionContext,
subscription: ISubscriptionContext,
): Promise<string | undefined> {
const defaultRoleId = '00000000-0000-0000-0000-000000000002'; // this is a predefined role with read and write access to data plane resources
const fullAccountId = `/subscriptions/${subscription.subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${databaseAccount}`;
const createUpdateSqlRoleAssignmentParameters: SqlRoleAssignmentCreateUpdateParameters =
{
const createUpdateSqlRoleAssignmentParameters: SqlRoleAssignmentCreateUpdateParameters = {
principalId: principalId,
roleDefinitionId: fullAccountId + "/sqlRoleDefinitions/" + defaultRoleId,
roleDefinitionId: fullAccountId + '/sqlRoleDefinitions/' + defaultRoleId,
scope: fullAccountId,
};
@ -82,8 +124,12 @@ async function addRbacContributorPermission(databaseAccount: string, principalId
const roleAssignmentId = randomUUID();
const client = await createCosmosDBClient([context, subscription]);
const create = await client.sqlResources.beginCreateUpdateSqlRoleAssignmentAndWait(roleAssignmentId, resourceGroup, databaseAccount, createUpdateSqlRoleAssignmentParameters);
const create = await client.sqlResources.beginCreateUpdateSqlRoleAssignmentAndWait(
roleAssignmentId,
resourceGroup,
databaseAccount,
createUpdateSqlRoleAssignmentParameters,
);
return create.id;
}

Просмотреть файл

@ -6,7 +6,22 @@
'use strict';
import { registerAzureUtilsExtensionVariables } from '@microsoft/vscode-azext-azureutils';
import { AzExtParentTreeItem, AzExtTreeItem, AzureExtensionApi, IActionContext, ITreeItemPickerContext, apiUtils, callWithTelemetryAndErrorHandling, createApiProvider, createAzExtLogOutputChannel, registerCommandWithTreeNodeUnwrapping, registerErrorHandler, registerEvent, registerReportIssueCommand, registerUIExtensionVariables } from '@microsoft/vscode-azext-utils';
import {
callWithTelemetryAndErrorHandling,
createApiProvider,
createAzExtLogOutputChannel,
registerCommandWithTreeNodeUnwrapping,
registerErrorHandler,
registerEvent,
registerReportIssueCommand,
registerUIExtensionVariables,
type AzExtParentTreeItem,
type AzExtTreeItem,
type AzureExtensionApi,
type IActionContext,
type ITreeItemPickerContext,
type apiUtils,
} from '@microsoft/vscode-azext-utils';
import { AzExtResourceType } from '@microsoft/vscode-azureresources-api';
import { platform } from 'os';
import * as vscode from 'vscode';
@ -16,11 +31,17 @@ import { pickTreeItem } from './commands/api/pickTreeItem';
import { revealTreeItem } from './commands/api/revealTreeItem';
import { deleteDatabaseAccount } from './commands/deleteDatabaseAccount/deleteDatabaseAccount';
import { importDocuments } from './commands/importDocuments';
import { cosmosGremlinFilter, cosmosMongoFilter, cosmosTableFilter, doubleClickDebounceDelay, sqlFilter } from './constants';
import {
cosmosGremlinFilter,
cosmosMongoFilter,
cosmosTableFilter,
doubleClickDebounceDelay,
sqlFilter,
} from './constants';
import { registerDocDBCommands } from './docdb/registerDocDBCommands';
import { DocDBAccountTreeItem } from './docdb/tree/DocDBAccountTreeItem';
import { DocDBAccountTreeItemBase } from './docdb/tree/DocDBAccountTreeItemBase';
import { DocDBCollectionTreeItem } from './docdb/tree/DocDBCollectionTreeItem';
import { type DocDBAccountTreeItemBase } from './docdb/tree/DocDBAccountTreeItemBase';
import { type DocDBCollectionTreeItem } from './docdb/tree/DocDBCollectionTreeItem';
import { DocDBDocumentTreeItem } from './docdb/tree/DocDBDocumentTreeItem';
import { ext } from './extensionVariables';
import { getResourceGroupsApi } from './getExtensionApi';
@ -29,7 +50,7 @@ import { GraphAccountTreeItem } from './graph/tree/GraphAccountTreeItem';
import { registerMongoCommands } from './mongo/registerMongoCommands';
import { setConnectedNode } from './mongo/setConnectedNode';
import { MongoAccountTreeItem } from './mongo/tree/MongoAccountTreeItem';
import { MongoCollectionTreeItem } from './mongo/tree/MongoCollectionTreeItem';
import { type MongoCollectionTreeItem } from './mongo/tree/MongoCollectionTreeItem';
import { MongoDocumentTreeItem } from './mongo/tree/MongoDocumentTreeItem';
import { registerPostgresCommands } from './postgres/commands/registerPostgresCommands';
import { DatabaseResolver } from './resolver/AppResolver';
@ -39,13 +60,22 @@ import { AttachedAccountSuffix } from './tree/AttachedAccountsTreeItem';
import { SubscriptionTreeItem } from './tree/SubscriptionTreeItem';
import { localize } from './utils/localize';
const cosmosDBTopLevelContextValues: string[] = [GraphAccountTreeItem.contextValue, DocDBAccountTreeItem.contextValue, TableAccountTreeItem.contextValue, MongoAccountTreeItem.contextValue];
const cosmosDBTopLevelContextValues: string[] = [
GraphAccountTreeItem.contextValue,
DocDBAccountTreeItem.contextValue,
TableAccountTreeItem.contextValue,
MongoAccountTreeItem.contextValue,
];
export async function activateInternal(context: vscode.ExtensionContext, perfStats: { loadStartTime: number, loadEndTime: number }, ignoreBundle?: boolean): Promise<apiUtils.AzureExtensionApiProvider> {
export async function activateInternal(
context: vscode.ExtensionContext,
perfStats: { loadStartTime: number; loadEndTime: number },
ignoreBundle?: boolean,
): Promise<apiUtils.AzureExtensionApiProvider> {
ext.context = context;
ext.ignoreBundle = ignoreBundle;
ext.outputChannel = createAzExtLogOutputChannel("Azure Databases");
ext.outputChannel = createAzExtLogOutputChannel('Azure Databases');
context.subscriptions.push(ext.outputChannel);
registerUIExtensionVariables(ext);
registerAzureUtilsExtensionVariables(ext);
@ -58,10 +88,18 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta
ext.rgApi = await getResourceGroupsApi();
ext.rgApi.registerApplicationResourceResolver(AzExtResourceType.AzureCosmosDb, new DatabaseResolver());
ext.rgApi.registerApplicationResourceResolver(AzExtResourceType.PostgresqlServersStandard, new DatabaseResolver());
ext.rgApi.registerApplicationResourceResolver(AzExtResourceType.PostgresqlServersFlexible, new DatabaseResolver());
ext.rgApi.registerApplicationResourceResolver(
AzExtResourceType.PostgresqlServersStandard,
new DatabaseResolver(),
);
ext.rgApi.registerApplicationResourceResolver(
AzExtResourceType.PostgresqlServersFlexible,
new DatabaseResolver(),
);
const workspaceRootTreeItem = (ext.rgApi.workspaceResourceTree as unknown as { _rootTreeItem: AzExtParentTreeItem })._rootTreeItem;
const workspaceRootTreeItem = (
ext.rgApi.workspaceResourceTree as unknown as { _rootTreeItem: AzExtParentTreeItem }
)._rootTreeItem;
const databaseWorkspaceProvider = new DatabaseWorkspaceProvider(workspaceRootTreeItem);
ext.rgApi.registerWorkspaceResourceProvider('AttachedDatabaseAccount', databaseWorkspaceProvider);
@ -72,99 +110,141 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta
registerPostgresCommands();
registerMongoCommands();
context.subscriptions.push(vscode.workspace.registerFileSystemProvider(DatabasesFileSystem.scheme, ext.fileSystem));
context.subscriptions.push(
vscode.workspace.registerFileSystemProvider(DatabasesFileSystem.scheme, ext.fileSystem),
);
registerCommandWithTreeNodeUnwrapping('cosmosDB.selectSubscriptions', () => vscode.commands.executeCommand("azure-account.selectSubscriptions"));
registerCommandWithTreeNodeUnwrapping('cosmosDB.selectSubscriptions', () =>
vscode.commands.executeCommand('azure-account.selectSubscriptions'),
);
registerCommandWithTreeNodeUnwrapping('azureDatabases.createServer', createServer);
registerCommandWithTreeNodeUnwrapping('cosmosDB.deleteAccount', deleteAccount);
registerCommandWithTreeNodeUnwrapping('cosmosDB.attachDatabaseAccount', async (actionContext: IActionContext) => {
await ext.attachedAccountsNode.attachNewAccount(actionContext);
await ext.rgApi.workspaceResourceTree.refresh(actionContext, ext.attachedAccountsNode);
});
registerCommandWithTreeNodeUnwrapping(
'cosmosDB.attachDatabaseAccount',
async (actionContext: IActionContext) => {
await ext.attachedAccountsNode.attachNewAccount(actionContext);
await ext.rgApi.workspaceResourceTree.refresh(actionContext, ext.attachedAccountsNode);
},
);
registerCommandWithTreeNodeUnwrapping('cosmosDB.attachEmulator', async (actionContext: IActionContext) => {
if (platform() !== 'win32') {
actionContext.errorHandling.suppressReportIssue = true;
throw new Error(localize('emulatorNotSupported', 'The Cosmos DB emulator is only supported on Windows.'));
throw new Error(
localize('emulatorNotSupported', 'The Cosmos DB emulator is only supported on Windows.'),
);
}
await ext.attachedAccountsNode.attachEmulator(actionContext);
await ext.rgApi.workspaceResourceTree.refresh(actionContext, ext.attachedAccountsNode);
});
registerCommandWithTreeNodeUnwrapping('azureDatabases.refresh', async (actionContext: IActionContext, node?: AzExtTreeItem) => {
if (node) {
await node.refresh(actionContext);
} else {
await ext.rgApi.appResourceTree.refresh(actionContext, node);
}
});
registerCommandWithTreeNodeUnwrapping('azureDatabases.detachDatabaseAccount', async (actionContext: IActionContext & ITreeItemPickerContext, node?: AzExtTreeItem) => {
const children = await ext.attachedAccountsNode.loadAllChildren(actionContext);
if (children[0].contextValue === "cosmosDBAttachDatabaseAccount") {
const message = localize('noAttachedAccounts', 'There are no Attached Accounts.');
void vscode.window.showInformationMessage(message);
} else {
if (!node) {
node = await ext.rgApi.workspaceResourceTree.showTreeItemPicker<AzExtTreeItem>(cosmosDBTopLevelContextValues.map((val: string) => val += AttachedAccountSuffix), actionContext);
registerCommandWithTreeNodeUnwrapping(
'azureDatabases.refresh',
async (actionContext: IActionContext, node?: AzExtTreeItem) => {
if (node) {
await node.refresh(actionContext);
} else {
await ext.rgApi.appResourceTree.refresh(actionContext, node);
}
if (node instanceof MongoAccountTreeItem) {
if (ext.connectedMongoDB && node.fullId === ext.connectedMongoDB.parent.fullId) {
setConnectedNode(undefined);
await node.refresh(actionContext);
},
);
registerCommandWithTreeNodeUnwrapping(
'azureDatabases.detachDatabaseAccount',
async (actionContext: IActionContext & ITreeItemPickerContext, node?: AzExtTreeItem) => {
const children = await ext.attachedAccountsNode.loadAllChildren(actionContext);
if (children[0].contextValue === 'cosmosDBAttachDatabaseAccount') {
const message = localize('noAttachedAccounts', 'There are no Attached Accounts.');
void vscode.window.showInformationMessage(message);
} else {
if (!node) {
node = await ext.rgApi.workspaceResourceTree.showTreeItemPicker<AzExtTreeItem>(
cosmosDBTopLevelContextValues.map((val: string) => (val += AttachedAccountSuffix)),
actionContext,
);
}
if (node instanceof MongoAccountTreeItem) {
if (ext.connectedMongoDB && node.fullId === ext.connectedMongoDB.parent.fullId) {
setConnectedNode(undefined);
await node.refresh(actionContext);
}
}
await ext.attachedAccountsNode.detach(node);
await ext.rgApi.workspaceResourceTree.refresh(actionContext, ext.attachedAccountsNode);
}
await ext.attachedAccountsNode.detach(node);
await ext.rgApi.workspaceResourceTree.refresh(actionContext, ext.attachedAccountsNode);
}
});
registerCommandWithTreeNodeUnwrapping('cosmosDB.importDocument', async (actionContext: IActionContext, selectedNode: vscode.Uri | MongoCollectionTreeItem | DocDBCollectionTreeItem, uris: vscode.Uri[]) => {
if (selectedNode instanceof vscode.Uri) {
await importDocuments(actionContext, uris || [selectedNode], undefined);
} else {
await importDocuments(actionContext, undefined, selectedNode);
}
});
},
);
registerCommandWithTreeNodeUnwrapping(
'cosmosDB.importDocument',
async (
actionContext: IActionContext,
selectedNode: vscode.Uri | MongoCollectionTreeItem | DocDBCollectionTreeItem,
uris: vscode.Uri[],
) => {
if (selectedNode instanceof vscode.Uri) {
await importDocuments(actionContext, uris || [selectedNode], undefined);
} else {
await importDocuments(actionContext, undefined, selectedNode);
}
},
);
registerCommandWithTreeNodeUnwrapping('cosmosDB.copyConnectionString', cosmosDBCopyConnectionString);
registerCommandWithTreeNodeUnwrapping('cosmosDB.openDocument', async (actionContext: IActionContext, node?: MongoDocumentTreeItem | DocDBDocumentTreeItem) => {
if (!node) {
node = await ext.rgApi.pickAppResource<MongoDocumentTreeItem | DocDBDocumentTreeItem>(actionContext, {
filter: [
cosmosMongoFilter,
sqlFilter
],
expectedChildContextValue: [MongoDocumentTreeItem.contextValue, DocDBDocumentTreeItem.contextValue]
});
}
registerCommandWithTreeNodeUnwrapping(
'cosmosDB.openDocument',
async (actionContext: IActionContext, node?: MongoDocumentTreeItem | DocDBDocumentTreeItem) => {
if (!node) {
node = await ext.rgApi.pickAppResource<MongoDocumentTreeItem | DocDBDocumentTreeItem>(
actionContext,
{
filter: [cosmosMongoFilter, sqlFilter],
expectedChildContextValue: [
MongoDocumentTreeItem.contextValue,
DocDBDocumentTreeItem.contextValue,
],
},
);
}
// Clear un-uploaded local changes to the document before opening https://github.com/microsoft/vscode-cosmosdb/issues/1619
ext.fileSystem.fireChangedEvent(node);
await ext.fileSystem.showTextDocument(node);
}, doubleClickDebounceDelay);
registerCommandWithTreeNodeUnwrapping('azureDatabases.update', async (_actionContext: IActionContext, uri: vscode.Uri) => await ext.fileSystem.updateWithoutPrompt(uri));
registerCommandWithTreeNodeUnwrapping('azureDatabases.loadMore', async (actionContext: IActionContext, node: AzExtTreeItem) => await ext.rgApi.appResourceTree.loadMore(node, actionContext));
// Clear un-uploaded local changes to the document before opening https://github.com/microsoft/vscode-cosmosdb/issues/1619
ext.fileSystem.fireChangedEvent(node);
await ext.fileSystem.showTextDocument(node);
},
doubleClickDebounceDelay,
);
registerCommandWithTreeNodeUnwrapping(
'azureDatabases.update',
async (_actionContext: IActionContext, uri: vscode.Uri) => await ext.fileSystem.updateWithoutPrompt(uri),
);
registerCommandWithTreeNodeUnwrapping(
'azureDatabases.loadMore',
async (actionContext: IActionContext, node: AzExtTreeItem) =>
await ext.rgApi.appResourceTree.loadMore(node, actionContext),
);
registerEvent(
'cosmosDB.onDidChangeConfiguration',
vscode.workspace.onDidChangeConfiguration,
async (actionContext: IActionContext, event: vscode.ConfigurationChangeEvent) => {
actionContext.telemetry.properties.isActivationEvent = "true";
actionContext.telemetry.properties.isActivationEvent = 'true';
actionContext.errorHandling.suppressDisplay = true;
if (event.affectsConfiguration(ext.settingsKeys.documentLabelFields)) {
await vscode.commands.executeCommand("azureDatabases.refresh");
await vscode.commands.executeCommand('azureDatabases.refresh');
}
});
},
);
// Suppress "Report an Issue" button for all errors in favor of the command
registerErrorHandler(c => c.errorHandling.suppressReportIssue = true);
registerErrorHandler((c) => (c.errorHandling.suppressReportIssue = true));
registerReportIssueCommand('azureDatabases.reportIssue');
});
return createApiProvider([<AzureExtensionApi>{
findTreeItem,
pickTreeItem,
revealTreeItem,
apiVersion: '1.2.0'
}]);
return createApiProvider([
<AzureExtensionApi>{
findTreeItem,
pickTreeItem,
revealTreeItem,
apiVersion: '1.2.0',
},
]);
}
// this method is called when your extension is deactivated
@ -174,7 +254,10 @@ export function deactivateInternal(): void {
export async function createServer(context: IActionContext, node?: SubscriptionTreeItem): Promise<void> {
if (!node) {
node = await ext.rgApi.appResourceTree.showTreeItemPicker<SubscriptionTreeItem>(SubscriptionTreeItem.contextValue, context);
node = await ext.rgApi.appResourceTree.showTreeItemPicker<SubscriptionTreeItem>(
SubscriptionTreeItem.contextValue,
context,
);
}
await SubscriptionTreeItem.createChild(context, node);
@ -185,28 +268,21 @@ export async function deleteAccount(context: IActionContext, node?: AzExtTreeIte
suppressCreateContext.suppressCreatePick = true;
if (!node) {
node = await ext.rgApi.pickAppResource<AzExtTreeItem>(context, {
filter: [
cosmosMongoFilter,
cosmosTableFilter,
cosmosGremlinFilter,
sqlFilter
]
filter: [cosmosMongoFilter, cosmosTableFilter, cosmosGremlinFilter, sqlFilter],
});
}
await deleteDatabaseAccount(context, node, false)
await deleteDatabaseAccount(context, node, false);
}
export async function cosmosDBCopyConnectionString(context: IActionContext, node?: MongoAccountTreeItem | DocDBAccountTreeItemBase): Promise<void> {
export async function cosmosDBCopyConnectionString(
context: IActionContext,
node?: MongoAccountTreeItem | DocDBAccountTreeItemBase,
): Promise<void> {
const message = 'The connection string has been copied to the clipboard';
if (!node) {
node = await ext.rgApi.pickAppResource<MongoAccountTreeItem | DocDBAccountTreeItemBase>(context, {
filter: [
cosmosMongoFilter,
cosmosTableFilter,
cosmosGremlinFilter,
sqlFilter
]
filter: [cosmosMongoFilter, cosmosTableFilter, cosmosGremlinFilter, sqlFilter],
});
}

Просмотреть файл

@ -3,18 +3,22 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeDataProvider, AzExtTreeItem, IAzExtLogOutputChannel } from "@microsoft/vscode-azext-utils";
import { AzureHostExtensionApi } from "@microsoft/vscode-azext-utils/hostapi";
import { ExtensionContext, SecretStorage, TreeView } from "vscode";
import { DatabasesFileSystem } from "./DatabasesFileSystem";
import { NoSqlCodeLensProvider } from "./docdb/NoSqlCodeLensProvider";
import { MongoDBLanguageClient } from "./mongo/languageClient";
import { MongoCodeLensProvider } from "./mongo/services/MongoCodeLensProvider";
import { MongoDatabaseTreeItem } from "./mongo/tree/MongoDatabaseTreeItem";
import { PostgresCodeLensProvider } from "./postgres/services/PostgresCodeLensProvider";
import { PostgresDatabaseTreeItem } from "./postgres/tree/PostgresDatabaseTreeItem";
import { AttachedAccountsTreeItem } from "./tree/AttachedAccountsTreeItem";
import { AzureAccountTreeItemWithAttached } from "./tree/AzureAccountTreeItemWithAttached";
import {
type AzExtTreeDataProvider,
type AzExtTreeItem,
type IAzExtLogOutputChannel,
} from '@microsoft/vscode-azext-utils';
import { type AzureHostExtensionApi } from '@microsoft/vscode-azext-utils/hostapi';
import { type ExtensionContext, type SecretStorage, type TreeView } from 'vscode';
import { type DatabasesFileSystem } from './DatabasesFileSystem';
import { type NoSqlCodeLensProvider } from './docdb/NoSqlCodeLensProvider';
import { type MongoDBLanguageClient } from './mongo/languageClient';
import { type MongoCodeLensProvider } from './mongo/services/MongoCodeLensProvider';
import { type MongoDatabaseTreeItem } from './mongo/tree/MongoDatabaseTreeItem';
import { type PostgresCodeLensProvider } from './postgres/services/PostgresCodeLensProvider';
import { type PostgresDatabaseTreeItem } from './postgres/tree/PostgresDatabaseTreeItem';
import { type AttachedAccountsTreeItem } from './tree/AttachedAccountsTreeItem';
import { type AzureAccountTreeItemWithAttached } from './tree/AzureAccountTreeItemWithAttached';
/**
* Namespace for common variables used throughout the extension. They must be initialized in the activate() method of extension.ts
@ -47,7 +51,7 @@ export namespace ext {
export const batchSize = 'azureDatabases.batchSize';
export namespace vsCode {
export const proxyStrictSSL = "http.proxyStrictSSL";
export const proxyStrictSSL = 'http.proxyStrictSSL';
}
}
}

Просмотреть файл

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { apiUtils } from "@microsoft/vscode-azext-utils";
import { AzureHostExtensionApi } from "@microsoft/vscode-azext-utils/hostapi";
import { Extension, extensions } from "vscode";
import { localize } from "./utils/localize";
import { type apiUtils } from '@microsoft/vscode-azext-utils';
import { type AzureHostExtensionApi } from '@microsoft/vscode-azext-utils/hostapi';
import { extensions, type Extension } from 'vscode';
import { localize } from './utils/localize';
export async function getApiExport<T>(extensionId: string): Promise<T | undefined> {
const extension: Extension<T> | undefined = extensions.getExtension(extensionId);
@ -22,7 +22,9 @@ export async function getApiExport<T>(extensionId: string): Promise<T | undefine
}
export async function getResourceGroupsApi(): Promise<AzureHostExtensionApi> {
const rgApiProvider = await getApiExport<apiUtils.AzureExtensionApiProvider>('ms-azuretools.vscode-azureresourcegroups');
const rgApiProvider = await getApiExport<apiUtils.AzureExtensionApiProvider>(
'ms-azuretools.vscode-azureresourcegroups',
);
if (rgApiProvider) {
return rgApiProvider.getApi<AzureHostExtensionApi>('^0.0.1');
} else {

Просмотреть файл

@ -3,11 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CosmosDBManagementClient } from '@azure/arm-cosmosdb';
import { type CosmosDBManagementClient } from '@azure/arm-cosmosdb';
import { nonNullValue } from '../utils/nonNull';
import { IGremlinEndpoint } from '../vscode-cosmosdbgraph.api';
import { type IGremlinEndpoint } from '../vscode-cosmosdbgraph.api';
export async function tryGetGremlinEndpointFromAzure(client: CosmosDBManagementClient, resourceGroup: string, account: string): Promise<IGremlinEndpoint | undefined> {
export async function tryGetGremlinEndpointFromAzure(
client: CosmosDBManagementClient,
resourceGroup: string,
account: string,
): Promise<IGremlinEndpoint | undefined> {
// Only 'bodyOfText' property of 'response' contains the 'gremlinEndpoint' property in the @azure/arm-cosmosdb@9 sdk
const response = await client.databaseAccounts.get(resourceGroup, account);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
@ -39,9 +43,12 @@ export function getPossibleGremlinEndpoints(documentEndpoint: string): IGremlinE
* @param url An account URL such as 'https://<graphname>.documents.azure.com:443/'
*/
function parseEndpointUrl(url: string): IGremlinEndpoint {
const [, protocol, host, , portString] = nonNullValue(url.match(/^([^:]+):\/\/([^:]+)(:([0-9]+))?\/?$/), 'urlMatch');
console.assert(!!protocol && !!host, "Unexpected endpoint format");
const port = parseInt(portString || "443", 10);
console.assert(port > 0, "Unexpected port");
return { host, port, ssl: protocol.toLowerCase() === "https" };
const [, protocol, host, , portString] = nonNullValue(
url.match(/^([^:]+):\/\/([^:]+)(:([0-9]+))?\/?$/),
'urlMatch',
);
console.assert(!!protocol && !!host, 'Unexpected endpoint format');
const port = parseInt(portString || '443', 10);
console.assert(port > 0, 'Unexpected port');
return { host, port, ssl: protocol.toLowerCase() === 'https' };
}

Просмотреть файл

@ -3,39 +3,54 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, IActionContext, ITreeItemPickerContext, registerCommandWithTreeNodeUnwrapping } from "@microsoft/vscode-azext-utils";
import {
registerCommandWithTreeNodeUnwrapping,
type AzExtTreeItem,
type IActionContext,
type ITreeItemPickerContext,
} from '@microsoft/vscode-azext-utils';
import { cosmosGremlinFilter, doubleClickDebounceDelay } from '../constants';
import { ext } from '../extensionVariables';
import { GraphAccountTreeItem } from "./tree/GraphAccountTreeItem";
import { GraphCollectionTreeItem } from "./tree/GraphCollectionTreeItem";
import { GraphDatabaseTreeItem } from "./tree/GraphDatabaseTreeItem";
import { GraphTreeItem } from "./tree/GraphTreeItem";
import { type GraphAccountTreeItem } from './tree/GraphAccountTreeItem';
import { GraphCollectionTreeItem } from './tree/GraphCollectionTreeItem';
import { GraphDatabaseTreeItem } from './tree/GraphDatabaseTreeItem';
import { GraphTreeItem } from './tree/GraphTreeItem';
export function registerGraphCommands(): void {
registerCommandWithTreeNodeUnwrapping('cosmosDB.createGraphDatabase', createGraphDatabase);
registerCommandWithTreeNodeUnwrapping('cosmosDB.createGraph', createGraph);
registerCommandWithTreeNodeUnwrapping('cosmosDB.deleteGraphDatabase', async (context: IActionContext, node?: GraphDatabaseTreeItem) => {
const suppressCreateContext: ITreeItemPickerContext = context;
suppressCreateContext.suppressCreatePick = true;
if (!node) {
node = await pickGraph<GraphDatabaseTreeItem>(context, GraphDatabaseTreeItem.contextValue);
}
await node.deleteTreeItem(context);
});
registerCommandWithTreeNodeUnwrapping('cosmosDB.deleteGraph', async (context: IActionContext, node?: GraphCollectionTreeItem) => {
const suppressCreateContext: ITreeItemPickerContext = context;
suppressCreateContext.suppressCreatePick = true;
if (!node) {
node = await pickGraph<GraphCollectionTreeItem>(context, GraphCollectionTreeItem.contextValue);
}
await node.deleteTreeItem(context);
});
registerCommandWithTreeNodeUnwrapping('cosmosDB.openGraphExplorer', async (context: IActionContext, node: GraphTreeItem) => {
if (!node) {
node = await pickGraph<GraphTreeItem>(context, GraphTreeItem.contextValue);
}
await node.showExplorer(context);
}, doubleClickDebounceDelay);
registerCommandWithTreeNodeUnwrapping(
'cosmosDB.deleteGraphDatabase',
async (context: IActionContext, node?: GraphDatabaseTreeItem) => {
const suppressCreateContext: ITreeItemPickerContext = context;
suppressCreateContext.suppressCreatePick = true;
if (!node) {
node = await pickGraph<GraphDatabaseTreeItem>(context, GraphDatabaseTreeItem.contextValue);
}
await node.deleteTreeItem(context);
},
);
registerCommandWithTreeNodeUnwrapping(
'cosmosDB.deleteGraph',
async (context: IActionContext, node?: GraphCollectionTreeItem) => {
const suppressCreateContext: ITreeItemPickerContext = context;
suppressCreateContext.suppressCreatePick = true;
if (!node) {
node = await pickGraph<GraphCollectionTreeItem>(context, GraphCollectionTreeItem.contextValue);
}
await node.deleteTreeItem(context);
},
);
registerCommandWithTreeNodeUnwrapping(
'cosmosDB.openGraphExplorer',
async (context: IActionContext, node: GraphTreeItem) => {
if (!node) {
node = await pickGraph<GraphTreeItem>(context, GraphTreeItem.contextValue);
}
await node.showExplorer(context);
},
doubleClickDebounceDelay,
);
}
export async function createGraphDatabase(context: IActionContext, node?: GraphAccountTreeItem): Promise<void> {
@ -52,11 +67,12 @@ export async function createGraph(context: IActionContext, node?: GraphDatabaseT
await node.createChild(context);
}
async function pickGraph<T extends AzExtTreeItem>(context: IActionContext, expectedContextValue?: string | RegExp | (string | RegExp)[]): Promise<T> {
async function pickGraph<T extends AzExtTreeItem>(
context: IActionContext,
expectedContextValue?: string | RegExp | (string | RegExp)[],
): Promise<T> {
return await ext.rgApi.pickAppResource<T>(context, {
filter: [
cosmosGremlinFilter
],
expectedChildContextValue: expectedContextValue
filter: [cosmosGremlinFilter],
expectedChildContextValue: expectedContextValue,
});
}

Просмотреть файл

@ -3,20 +3,20 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DatabaseAccountGetResults } from '@azure/arm-cosmosdb/src/models';
import { DatabaseDefinition, Resource } from '@azure/cosmos';
import { AzExtParentTreeItem } from '@microsoft/vscode-azext-utils';
import { CosmosDBCredential } from '../../docdb/getCosmosClient';
import { type DatabaseAccountGetResults } from '@azure/arm-cosmosdb/src/models';
import { type DatabaseDefinition, type Resource } from '@azure/cosmos';
import { type AzExtParentTreeItem } from '@microsoft/vscode-azext-utils';
import { type CosmosDBCredential } from '../../docdb/getCosmosClient';
import { DocDBAccountTreeItemBase } from '../../docdb/tree/DocDBAccountTreeItemBase';
import { DocDBStoredProcedureTreeItem } from '../../docdb/tree/DocDBStoredProcedureTreeItem';
import { DocDBStoredProceduresTreeItem } from '../../docdb/tree/DocDBStoredProceduresTreeItem';
import { IGremlinEndpoint } from '../../vscode-cosmosdbgraph.api';
import { type IGremlinEndpoint } from '../../vscode-cosmosdbgraph.api';
import { GraphCollectionTreeItem } from './GraphCollectionTreeItem';
import { GraphDatabaseTreeItem } from './GraphDatabaseTreeItem';
import { GraphTreeItem } from './GraphTreeItem';
export class GraphAccountTreeItem extends DocDBAccountTreeItemBase {
public static contextValue: string = "cosmosDBGraphAccount";
public static contextValue: string = 'cosmosDBGraphAccount';
public contextValue: string = GraphAccountTreeItem.contextValue;
constructor(
@ -27,7 +27,7 @@ export class GraphAccountTreeItem extends DocDBAccountTreeItemBase {
private _gremlinEndpoint: IGremlinEndpoint | undefined,
credentials: CosmosDBCredential[],
isEmulator: boolean | undefined,
readonly databaseAccount?: DatabaseAccountGetResults
readonly databaseAccount?: DatabaseAccountGetResults,
) {
super(parent, id, label, documentEndpoint, credentials, isEmulator, databaseAccount);
this.valuesToMask.push(documentEndpoint);

Просмотреть файл

@ -3,17 +3,23 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Container, ContainerDefinition, CosmosClient, Resource } from '@azure/cosmos';
import { AzExtParentTreeItem, AzExtTreeItem, DialogResponses, IActionContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import { type Container, type ContainerDefinition, type CosmosClient, type Resource } from '@azure/cosmos';
import {
AzExtParentTreeItem,
DialogResponses,
type AzExtTreeItem,
type IActionContext,
type TreeItemIconPath,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { DocDBStoredProceduresTreeItem } from '../../docdb/tree/DocDBStoredProceduresTreeItem';
import { DocDBStoredProcedureTreeItem } from '../../docdb/tree/DocDBStoredProcedureTreeItem';
import { IDocDBTreeRoot } from '../../docdb/tree/IDocDBTreeRoot';
import { GraphDatabaseTreeItem } from './GraphDatabaseTreeItem';
import { type IDocDBTreeRoot } from '../../docdb/tree/IDocDBTreeRoot';
import { type GraphDatabaseTreeItem } from './GraphDatabaseTreeItem';
import { GraphTreeItem } from './GraphTreeItem';
export class GraphCollectionTreeItem extends AzExtParentTreeItem {
public static contextValue: string = "cosmosDBGraph";
public static contextValue: string = 'cosmosDBGraph';
public readonly contextValue: string = GraphCollectionTreeItem.contextValue;
public readonly parent: GraphDatabaseTreeItem;
@ -59,7 +65,11 @@ export class GraphCollectionTreeItem extends AzExtParentTreeItem {
public async deleteTreeItemImpl(context: IActionContext): Promise<void> {
const message: string = `Are you sure you want to delete graph '${this.label}' and its contents?`;
await context.ui.showWarningMessage(message, { modal: true, stepName: 'deleteGraphCollection' }, DialogResponses.deleteResponse);
await context.ui.showWarningMessage(
message,
{ modal: true, stepName: 'deleteGraphCollection' },
DialogResponses.deleteResponse,
);
const client = this.root.getCosmosClient();
await this.getContainerClient(client).delete();
}
@ -81,7 +91,6 @@ export class GraphCollectionTreeItem extends AzExtParentTreeItem {
}
public getContainerClient(client: CosmosClient): Container {
return (this.parent.getDatabaseClient(client)).container(this.id);
return this.parent.getDatabaseClient(client).container(this.id);
}
}

Просмотреть файл

@ -3,20 +3,30 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ContainerDefinition, CosmosClient, Database, DatabaseDefinition, Resource } from '@azure/cosmos';
import { IActionContext } from '@microsoft/vscode-azext-utils';
import {
type ContainerDefinition,
type CosmosClient,
type Database,
type DatabaseDefinition,
type Resource,
} from '@azure/cosmos';
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { DocDBDatabaseTreeItemBase } from '../../docdb/tree/DocDBDatabaseTreeItemBase';
import { IGremlinEndpoint } from '../../vscode-cosmosdbgraph.api';
import { type IGremlinEndpoint } from '../../vscode-cosmosdbgraph.api';
import { getPossibleGremlinEndpoints } from '../gremlinEndpoints';
import { GraphAccountTreeItem } from './GraphAccountTreeItem';
import { type GraphAccountTreeItem } from './GraphAccountTreeItem';
import { GraphCollectionTreeItem } from './GraphCollectionTreeItem';
export class GraphDatabaseTreeItem extends DocDBDatabaseTreeItemBase {
public static contextValue: string = "cosmosDBGraphDatabase";
public static contextValue: string = 'cosmosDBGraphDatabase';
public readonly contextValue: string = GraphDatabaseTreeItem.contextValue;
public readonly childTypeLabel: string = 'Graph';
constructor(parent: GraphAccountTreeItem, private _gremlinEndpoint: IGremlinEndpoint | undefined, database: DatabaseDefinition & Resource) {
constructor(
parent: GraphAccountTreeItem,
private _gremlinEndpoint: IGremlinEndpoint | undefined,
database: DatabaseDefinition & Resource,
) {
super(parent, database);
}
@ -35,7 +45,6 @@ export class GraphDatabaseTreeItem extends DocDBDatabaseTreeItemBase {
public getDatabaseClient(client: CosmosClient): Database {
return client.database(this.id);
}
protected override async getNewPartitionKey(context: IActionContext): Promise<string | undefined> {
@ -43,7 +52,7 @@ export class GraphDatabaseTreeItem extends DocDBDatabaseTreeItemBase {
prompt: 'Enter the partition key for the collection, or leave blank for fixed size.',
stepName: 'partitionKeyForCollection',
validateInput: this.validatePartitionKey,
placeHolder: 'e.g. /address'
placeHolder: 'e.g. /address',
});
if (partitionKey && partitionKey.length && partitionKey[0] !== '/') {
@ -55,10 +64,10 @@ export class GraphDatabaseTreeItem extends DocDBDatabaseTreeItemBase {
protected validatePartitionKey(key: string): string | undefined {
if (/[#?\\]/.test(key)) {
return "Cannot contain these characters: ?,#,\\, etc.";
return 'Cannot contain these characters: ?,#,\\, etc.';
}
if (/.+\//.test(key)) {
return "Cannot be a nested path";
return 'Cannot be a nested path';
}
return undefined;
}

Просмотреть файл

@ -3,17 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ContainerDefinition, Resource } from '@azure/cosmos';
import { AzExtTreeItem, IActionContext, TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import { type ContainerDefinition, type Resource } from '@azure/cosmos';
import { AzExtTreeItem, type IActionContext, type TreeItemIconPath } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { localize } from '../../utils/localize';
import { openUrl } from '../../utils/openUrl';
import { GraphCollectionTreeItem } from './GraphCollectionTreeItem';
import { type GraphCollectionTreeItem } from './GraphCollectionTreeItem';
const alternativeGraphVisualizationToolsDocLink = "https://aka.ms/cosmosdb-graph-alternative-tools";
const alternativeGraphVisualizationToolsDocLink = 'https://aka.ms/cosmosdb-graph-alternative-tools';
export class GraphTreeItem extends AzExtTreeItem {
public static contextValue: string = "cosmosDBGraphGraph";
public static contextValue: string = 'cosmosDBGraphGraph';
public readonly contextValue: string = GraphTreeItem.contextValue;
public readonly parent: GraphCollectionTreeItem;
public suppressMaskLabel = true;
@ -31,7 +31,7 @@ export class GraphTreeItem extends AzExtTreeItem {
}
public get label(): string {
return "Graph";
return 'Graph';
}
public get link(): string {
@ -44,11 +44,8 @@ export class GraphTreeItem extends AzExtTreeItem {
public async showExplorer(_context: IActionContext): Promise<void> {
const message: string = localize('mustInstallGraph', 'Cosmos DB Graph extension has been retired.');
const alternativeToolsOption = "Alternative Tools";
const result = await vscode.window.showErrorMessage(
message,
alternativeToolsOption
);
const alternativeToolsOption = 'Alternative Tools';
const result = await vscode.window.showErrorMessage(message, alternativeToolsOption);
if (result === alternativeToolsOption) {
await openUrl(alternativeGraphVisualizationToolsDocLink);
}

Просмотреть файл

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RecognitionException } from 'antlr4ts';
import * as vscode from 'vscode';
import { type RecognitionException } from 'antlr4ts';
import type * as vscode from 'vscode';
export interface MongoCommand {
range: vscode.Range;
@ -12,7 +12,7 @@ export interface MongoCommand {
collection?: string;
name?: string;
arguments?: string[];
argumentObjects?: Object[];
argumentObjects?: object[];
errors?: ErrorDescription[];
chained?: boolean;
}

Просмотреть файл

@ -3,14 +3,20 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, IParsedError, openReadOnlyContent, parseError, ReadOnlyContent } from '@microsoft/vscode-azext-utils';
import {
openReadOnlyContent,
parseError,
type IActionContext,
type IParsedError,
type ReadOnlyContent,
} from '@microsoft/vscode-azext-utils';
import { ANTLRInputStream as InputStream } from 'antlr4ts/ANTLRInputStream';
import { CommonTokenStream } from 'antlr4ts/CommonTokenStream';
import { ErrorNode } from 'antlr4ts/tree/ErrorNode';
import { ParseTree } from 'antlr4ts/tree/ParseTree';
import { type ParseTree } from 'antlr4ts/tree/ParseTree';
import { TerminalNode } from 'antlr4ts/tree/TerminalNode';
import { EJSON, ObjectId } from 'bson';
import { Collection } from 'mongodb';
import { type Collection } from 'mongodb';
import { EOL } from 'os';
import * as vscode from 'vscode';
import { ext } from '../extensionVariables';
@ -21,18 +27,18 @@ import { LexerErrorListener, ParserErrorListener } from './errorListeners';
import { mongoLexer } from './grammar/mongoLexer';
import * as mongoParser from './grammar/mongoParser';
import { MongoVisitor } from './grammar/visitors';
import { ErrorDescription, MongoCommand } from './MongoCommand';
import { type ErrorDescription, type MongoCommand } from './MongoCommand';
import { MongoCollectionTreeItem } from './tree/MongoCollectionTreeItem';
import { MongoDatabaseTreeItem, stripQuotes } from './tree/MongoDatabaseTreeItem';
import { IMongoDocument, MongoDocumentTreeItem } from './tree/MongoDocumentTreeItem';
import { stripQuotes, type MongoDatabaseTreeItem } from './tree/MongoDatabaseTreeItem';
import { MongoDocumentTreeItem, type IMongoDocument } from './tree/MongoDocumentTreeItem';
const notInScrapbookMessage = "You must have a MongoDB scrapbook (*.mongo) open to run a MongoDB command.";
const notInScrapbookMessage = 'You must have a MongoDB scrapbook (*.mongo) open to run a MongoDB command.';
export function getAllErrorsFromTextDocument(document: vscode.TextDocument): vscode.Diagnostic[] {
const commands = getAllCommandsFromTextDocument(document);
const errors: vscode.Diagnostic[] = [];
for (const command of commands) {
for (const error of (command.errors || [])) {
for (const error of command.errors || []) {
const diagnostic = new vscode.Diagnostic(error.range, error.message);
errors.push(diagnostic);
}
@ -42,12 +48,15 @@ export function getAllErrorsFromTextDocument(document: vscode.TextDocument): vsc
}
export async function executeAllCommandsFromActiveEditor(context: IActionContext): Promise<void> {
ext.outputChannel.appendLog("Executing all commands in scrapbook...");
ext.outputChannel.appendLog('Executing all commands in scrapbook...');
const commands = getAllCommandsFromActiveEditor();
await executeCommands(context, commands);
}
export async function executeCommandFromActiveEditor(context: IActionContext, position?: vscode.Position): Promise<void> {
export async function executeCommandFromActiveEditor(
context: IActionContext,
position?: vscode.Position,
): Promise<void> {
const commands = getAllCommandsFromActiveEditor();
const command = findCommandAtPosition(commands, position || vscode.window.activeTextEditor?.selection.start);
return await executeCommand(context, command);
@ -70,7 +79,9 @@ export function getAllCommandsFromTextDocument(document: vscode.TextDocument): M
async function executeCommands(context: IActionContext, commands: MongoCommand[]): Promise<void> {
const label: string = 'Scrapbook-execute-all-results';
const fullId: string = `${ext.connectedMongoDB?.fullId}/${label}`;
const readOnlyContent: ReadOnlyContent = await openReadOnlyContent({ label, fullId }, '', '.txt', { viewColumn: vscode.ViewColumn.Beside });
const readOnlyContent: ReadOnlyContent = await openReadOnlyContent({ label, fullId }, '', '.txt', {
viewColumn: vscode.ViewColumn.Beside,
});
for (const command of commands) {
try {
@ -87,23 +98,34 @@ async function executeCommands(context: IActionContext, commands: MongoCommand[]
}
}
async function executeCommand(context: IActionContext, command: MongoCommand, readOnlyContent?: ReadOnlyContent): Promise<void> {
async function executeCommand(
context: IActionContext,
command: MongoCommand,
readOnlyContent?: ReadOnlyContent,
): Promise<void> {
if (command) {
try {
context.telemetry.properties.command = command.name;
context.telemetry.properties.argsCount = String(command.arguments ? command.arguments.length : 0);
} catch (error) {
} catch {
// Ignore
}
const database = ext.connectedMongoDB;
if (!database) {
throw new Error('Please select a MongoDB database to run against by selecting it in the explorer and selecting the "Connect" context menu item');
throw new Error(
'Please select a MongoDB database to run against by selecting it in the explorer and selecting the "Connect" context menu item',
);
}
if (command.errors && command.errors.length > 0) {
//Currently, we take the first error pushed. Tests correlate that the parser visits errors in left-to-right, top-to-bottom.
const err = command.errors[0];
throw new Error(localize('unableToParseSyntax', `Unable to parse syntax. Error near line ${err.range.start.line + 1}, column ${err.range.start.character + 1}: "${err.message}"`));
throw new Error(
localize(
'unableToParseSyntax',
`Unable to parse syntax. Error near line ${err.range.start.line + 1}, column ${err.range.start.character + 1}: "${err.message}"`,
),
);
}
// we don't handle chained commands so we can only handle "find" if isn't chained
@ -118,7 +140,7 @@ async function executeCommand(context: IActionContext, command: MongoCommand, re
} else {
const result = await database.executeCommand(command, context);
if (command.name === 'findOne') {
if (result === "null") {
if (result === 'null') {
throw new Error(`Could not find any documents`);
}
@ -127,7 +149,10 @@ async function executeCommand(context: IActionContext, command: MongoCommand, re
const collectionName: string = nonNullProp(command, 'collection');
const collectionId: string = `${database.fullId}/${collectionName}`;
const colNode: MongoCollectionTreeItem | undefined = await ext.rgApi.appResourceTree.findTreeItem(collectionId, context);
const colNode: MongoCollectionTreeItem | undefined = await ext.rgApi.appResourceTree.findTreeItem(
collectionId,
context,
);
if (!colNode) {
throw new Error(localize('failedToFind', 'Failed to find collection "{0}".', collectionName));
}
@ -139,7 +164,9 @@ async function executeCommand(context: IActionContext, command: MongoCommand, re
} else {
const label: string = 'Scrapbook-results';
const fullId: string = `${database.fullId}/${label}`;
await openReadOnlyContent({ label, fullId }, result, '.json', { viewColumn: vscode.ViewColumn.Beside });
await openReadOnlyContent({ label, fullId }, result, '.json', {
viewColumn: vscode.ViewColumn.Beside,
});
}
await refreshTreeAfterCommand(database, command, context);
@ -150,11 +177,22 @@ async function executeCommand(context: IActionContext, command: MongoCommand, re
}
}
async function refreshTreeAfterCommand(database: MongoDatabaseTreeItem, command: MongoCommand, context: IActionContext): Promise<void> {
async function refreshTreeAfterCommand(
database: MongoDatabaseTreeItem,
command: MongoCommand,
context: IActionContext,
): Promise<void> {
if (command.name === 'drop') {
await database.refresh(context);
} else if (command.collection && command.name && /^(insert|update|delete|replace|remove|write|bulkWrite)/i.test(command.name)) {
const collectionNode = await ext.rgApi.appResourceTree.findTreeItem(database.fullId + "/" + command.collection, context);
} else if (
command.collection &&
command.name &&
/^(insert|update|delete|replace|remove|write|bulkWrite)/i.test(command.name)
) {
const collectionNode = await ext.rgApi.appResourceTree.findTreeItem(
database.fullId + '/' + command.collection,
context,
);
if (collectionNode) {
await collectionNode.refresh(context);
}
@ -194,7 +232,7 @@ export function getAllCommandsFromText(content: string): MongoCommand[] {
collection: undefined,
name: undefined,
range: err.range,
text: ""
text: '',
};
emptyCommand.errors = [err];
commands.push(emptyCommand);
@ -230,12 +268,17 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
const funcCallCount: number = filterType(ctx.children, mongoParser.FunctionCallContext).length;
const stop = nonNullProp(ctx, 'stop');
this.commands.push({
range: new vscode.Range(ctx.start.line - 1, ctx.start.charPositionInLine, stop.line - 1, stop.charPositionInLine),
range: new vscode.Range(
ctx.start.line - 1,
ctx.start.charPositionInLine,
stop.line - 1,
stop.charPositionInLine,
),
text: ctx.text,
name: '',
arguments: [],
argumentObjects: [],
chained: funcCallCount > 1 ? true : false
chained: funcCallCount > 1 ? true : false,
});
return super.visitCommand(ctx);
}
@ -247,7 +290,7 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
public visitFunctionCall(ctx: mongoParser.FunctionCallContext): MongoCommand[] {
if (ctx.parent instanceof mongoParser.CommandContext) {
this.commands[this.commands.length - 1].name = (ctx._FUNCTION_NAME && ctx._FUNCTION_NAME.text) || "";
this.commands[this.commands.length - 1].name = (ctx._FUNCTION_NAME && ctx._FUNCTION_NAME.text) || '';
}
return super.visitFunctionCall(ctx);
}
@ -268,7 +311,8 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
ejsonParsed = EJSON.parse(escapeHandled);
} catch (error) { //EJSON parse failed due to a wrong flag, etc.
} catch (error) {
//EJSON parse failed due to a wrong flag, etc.
const parsedError: IParsedError = parseError(error);
this.addErrorToCommand(parsedError.message, ctx);
}
@ -286,8 +330,10 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
return this.commands;
}
//eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
private contextToObject(ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext): Object {
if (!ctx || ctx.childCount === 0) { //Base case and malformed statements
if (!ctx || ctx.childCount === 0) {
//Base case and malformed statements
return {};
}
// In a well formed expression, Argument and propertyValue tokens should have exactly one child, from their definitions in mongo.g4
@ -308,10 +354,18 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
}
}
private literalContextToObject(child: mongoParser.LiteralContext, ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext): Object {
private literalContextToObject(
child: mongoParser.LiteralContext,
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
//eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
): Object {
const text = child.text;
const tokenType = child.start.type;
const nonStringLiterals = [mongoParser.mongoParser.NullLiteral, mongoParser.mongoParser.BooleanLiteral, mongoParser.mongoParser.NumericLiteral];
const nonStringLiterals = [
mongoParser.mongoParser.NullLiteral,
mongoParser.mongoParser.BooleanLiteral,
mongoParser.mongoParser.NumericLiteral,
];
if (tokenType === mongoParser.mongoParser.StringLiteral) {
return stripQuotes(text);
} else if (tokenType === mongoParser.mongoParser.RegexLiteral) {
@ -325,13 +379,17 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
}
}
private objectLiteralContextToObject(child: mongoParser.ObjectLiteralContext): Object {
private objectLiteralContextToObject(child: mongoParser.ObjectLiteralContext): object {
const propertyNameAndValue = findType(child.children, mongoParser.PropertyNameAndValueListContext);
if (!propertyNameAndValue) { // Argument is {}
if (!propertyNameAndValue) {
// Argument is {}
return {};
} else {
const parsedObject: Object = {};
const propertyAssignments = filterType(propertyNameAndValue.children, mongoParser.PropertyAssignmentContext);
const parsedObject: object = {};
const propertyAssignments = filterType(
propertyNameAndValue.children,
mongoParser.PropertyAssignmentContext,
);
for (const propertyAssignment of propertyAssignments) {
const propertyAssignmentChildren = nonNullProp(propertyAssignment, 'children');
const propertyName = <mongoParser.PropertyNameContext>propertyAssignmentChildren[0];
@ -346,22 +404,34 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
const elementList = findType(child.children, mongoParser.ElementListContext);
if (elementList) {
const elementItems = filterType(elementList.children, mongoParser.PropertyValueContext);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
return elementItems.map(this.contextToObject.bind(this));
} else {
return [];
}
}
private functionCallContextToObject(child: mongoParser.FunctionCallContext, ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext): Object {
private functionCallContextToObject(
child: mongoParser.FunctionCallContext,
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
): Object {
const functionTokens = child.children;
const constructorCall: TerminalNode = nonNullValue(findType(functionTokens, TerminalNode), 'constructorCall');
const argumentsToken: mongoParser.ArgumentsContext = nonNullValue(findType(functionTokens, mongoParser.ArgumentsContext), 'argumentsToken');
if (!(argumentsToken._CLOSED_PARENTHESIS && argumentsToken._OPEN_PARENTHESIS)) { //argumentsToken does not have '(' or ')'
const argumentsToken: mongoParser.ArgumentsContext = nonNullValue(
findType(functionTokens, mongoParser.ArgumentsContext),
'argumentsToken',
);
if (!(argumentsToken._CLOSED_PARENTHESIS && argumentsToken._OPEN_PARENTHESIS)) {
//argumentsToken does not have '(' or ')'
this.addErrorToCommand(`Expecting parentheses or quotes at '${constructorCall.text}'`, ctx);
return {};
}
const argumentContextArray: mongoParser.ArgumentContext[] = filterType(argumentsToken.children, mongoParser.ArgumentContext);
const argumentContextArray: mongoParser.ArgumentContext[] = filterType(
argumentsToken.children,
mongoParser.ArgumentContext,
);
if (argumentContextArray.length > 1) {
this.addErrorToCommand(`Too many arguments. Expecting 0 or 1 argument(s) to ${constructorCall}`, ctx);
return {};
@ -376,13 +446,21 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
case 'Date':
return this.dateToObject(ctx, tokenText);
default:
this.addErrorToCommand(`Unrecognized node type encountered. Could not parse ${constructorCall.text} as part of ${child.text}`, ctx);
this.addErrorToCommand(
`Unrecognized node type encountered. Could not parse ${constructorCall.text} as part of ${child.text}`,
ctx,
);
return {};
}
}
private dateToObject(ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext, tokenText?: string): { $date: string } | {} {
const date: Date | {} = this.tryToConstructDate(ctx, tokenText);
private dateToObject(
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
tokenText?: string,
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
): { $date: string } | Object {
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
const date: Date | Object = this.tryToConstructDate(ctx, tokenText);
if (date instanceof Date) {
return { $date: date.toString() };
} else {
@ -390,8 +468,13 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
}
}
private isodateToObject(ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext, tokenText?: string): { $date: string } | {} {
const date: Date | {} = this.tryToConstructDate(ctx, tokenText, true);
private isodateToObject(
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
tokenText?: string,
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
): { $date: string } | Object {
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
const date: Date | Object = this.tryToConstructDate(ctx, tokenText, true);
if (date instanceof Date) {
return { $date: date.toISOString() };
@ -400,8 +483,14 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
}
}
private tryToConstructDate(ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext, tokenText?: string, isIsodate: boolean = false): Date | {} {
if (!tokenText) { // usage : ObjectID()
private tryToConstructDate(
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
tokenText?: string,
isIsodate: boolean = false,
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
): Date | Object {
if (!tokenText) {
// usage : ObjectID()
return new Date();
} else {
try {
@ -423,10 +512,15 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
}
}
private objectIdToObject(ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext, tokenText?: string): Object {
private objectIdToObject(
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
tokenText?: string,
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
): Object {
let hexID: string;
let constructedObject: ObjectId;
if (!tokenText) { // usage : ObjectID()
if (!tokenText) {
// usage : ObjectID()
constructedObject = new ObjectId();
} else {
hexID = stripQuotes(<string>tokenText);
@ -441,30 +535,44 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
return { $oid: constructedObject.toString() };
}
private regexLiteralContextToObject(ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext, text: string): Object {
private regexLiteralContextToObject(
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
text: string,
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
): Object {
const separator = text.lastIndexOf('/');
const flags = separator !== text.length - 1 ? text.substring(separator + 1) : "";
const flags = separator !== text.length - 1 ? text.substring(separator + 1) : '';
const pattern = text.substring(1, separator);
try {
// validate the pattern and flags.
// It is intended for the errors thrown here to be handled by the catch block.
let tokenObject = new RegExp(pattern, flags);
// eslint-disable-next-line no-self-assign, @typescript-eslint/no-unused-vars
tokenObject = tokenObject;
new RegExp(pattern, flags);
// we are passing back a $regex annotation, hence we ensure parity wit the $regex syntax
return { $regex: this.regexToStringNotation(pattern), $options: flags };
} catch (error) { //User may not have finished typing
} catch (error) {
//User may not have finished typing
const parsedError: IParsedError = parseError(error);
this.addErrorToCommand(parsedError.message, ctx);
return {};
}
}
private addErrorToCommand(errorMessage: string, ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext): void {
private addErrorToCommand(
errorMessage: string,
ctx: mongoParser.ArgumentContext | mongoParser.PropertyValueContext,
): void {
const command = this.commands[this.commands.length - 1];
command.errors = command.errors || [];
const stop = nonNullProp(ctx, 'stop');
const currentErrorDesc: ErrorDescription = { message: errorMessage, range: new vscode.Range(ctx.start.line - 1, ctx.start.charPositionInLine, stop.line - 1, stop.charPositionInLine) };
const currentErrorDesc: ErrorDescription = {
message: errorMessage,
range: new vscode.Range(
ctx.start.line - 1,
ctx.start.charPositionInLine,
stop.line - 1,
stop.charPositionInLine,
),
};
command.errors.push(currentErrorDesc);
}
@ -485,5 +593,4 @@ class FindMongoCommandsVisitor extends MongoVisitor<MongoCommand[]> {
*/
return argAsString.replace(removeDuplicatedBackslash, `\\\\$1`);
}
}

Просмотреть файл

@ -11,33 +11,45 @@ import { randomUtils } from '../utils/randomUtils';
import { getBatchSizeSetting } from '../utils/workspacUtils';
import { wrapError } from '../utils/wrapError';
const timeoutMessage = "Timed out trying to execute the Mongo script. To use a longer timeout, modify the VS Code 'mongo.shell.timeout' setting.";
const timeoutMessage =
"Timed out trying to execute the Mongo script. To use a longer timeout, modify the VS Code 'mongo.shell.timeout' setting.";
const mongoShellMoreMessage = 'Type "it" for more';
const extensionMoreMessage = '(More)';
const sentinelBase = 'EXECUTION COMPLETED';
const sentinelRegex = /\"?EXECUTION COMPLETED [0-9a-fA-F]{10}\"?/;
function createSentinel(): string { return `${sentinelBase} ${randomUtils.getRandomHexString(10)}`; }
const sentinelRegex = /"?EXECUTION COMPLETED [0-9a-fA-F]{10}"?/;
function createSentinel(): string {
return `${sentinelBase} ${randomUtils.getRandomHexString(10)}`;
}
export class MongoShell extends vscode.Disposable {
constructor(private _process: InteractiveChildProcess, private _timeoutSeconds: number) {
constructor(
private _process: InteractiveChildProcess,
private _timeoutSeconds: number,
) {
super(() => this.dispose());
}
public static async create(execPath: string, execArgs: string[], connectionString: string, isEmulator: boolean | undefined, outputChannel: vscode.OutputChannel, timeoutSeconds: number): Promise<MongoShell> {
public static async create(
execPath: string,
execArgs: string[],
connectionString: string,
isEmulator: boolean | undefined,
outputChannel: vscode.OutputChannel,
timeoutSeconds: number,
): Promise<MongoShell> {
try {
const args: string[] = execArgs.slice() || []; // Snapshot since we modify it
args.push(connectionString);
if (isEmulator) {
// Without these the connection will fail due to the self-signed DocDB certificate
if (args.indexOf("--ssl") < 0) {
args.push("--ssl");
if (args.indexOf('--ssl') < 0) {
args.push('--ssl');
}
if (args.indexOf("--sslAllowInvalidCertificates") < 0) {
args.push("--sslAllowInvalidCertificates");
if (args.indexOf('--sslAllowInvalidCertificates') < 0) {
args.push('--sslAllowInvalidCertificates');
}
}
@ -46,13 +58,13 @@ export class MongoShell extends vscode.Disposable {
command: execPath,
args,
outputFilterSearch: sentinelRegex,
outputFilterReplace: ''
outputFilterReplace: '',
});
const shell: MongoShell = new MongoShell(process, timeoutSeconds);
// Try writing an empty script to verify the process is running correctly and allow us
// to catch any errors related to the start-up of the process before trying to write to it.
await shell.executeScript("");
await shell.executeScript('');
// Configure the batch size
await shell.executeScript(`DBQuery.shellBatchSize = ${getBatchSizeSetting()}`);
@ -74,7 +86,7 @@ export class MongoShell extends vscode.Disposable {
public async executeScript(script: string): Promise<string> {
script = convertToSingleLine(script);
let stdOut = "";
let stdOut = '';
const sentinel = createSentinel();
const disposables: vscode.Disposable[] = [];
@ -86,7 +98,7 @@ export class MongoShell extends vscode.Disposable {
// Hook up events
disposables.push(
this._process.onStdOut(text => {
this._process.onStdOut((text) => {
stdOut += text;
// eslint-disable-next-line prefer-const
let { text: stdOutNoSentinel, removed } = removeSentinel(stdOut, sentinel);
@ -97,22 +109,30 @@ export class MongoShell extends vscode.Disposable {
// since we're not currently interactive like that.
// CONSIDER: Ideally we would allow users to click a button to iterate through more data,
// or even just do it for them
stdOutNoSentinel = stdOutNoSentinel.replace(mongoShellMoreMessage, extensionMoreMessage);
stdOutNoSentinel = stdOutNoSentinel.replace(
mongoShellMoreMessage,
extensionMoreMessage,
);
resolve(stdOutNoSentinel);
}
}));
}),
);
disposables.push(
this._process.onStdErr(text => {
this._process.onStdErr((text) => {
// Mongo shell only writes to STDERR for errors relating to starting up. Script errors go to STDOUT.
// So consider this an error.
// (It's okay if we fire this multiple times, the first one wins.)
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(wrapCheckOutputWindow(text.trim()));
}));
}),
);
disposables.push(
this._process.onError(error => {
this._process.onError((error) => {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(error);
}));
}),
);
// Write the script to STDIN
if (script) {
@ -123,7 +143,6 @@ export class MongoShell extends vscode.Disposable {
// it back out as a string value after it's done processing the script
const quotedSentinel = `"${sentinel}"`;
this._process.writeLine(quotedSentinel); // (Don't display the sentinel)
} catch (error) {
// new Promise() doesn't seem to catch exceptions in an async function, we need to explicitly reject it
@ -131,16 +150,16 @@ export class MongoShell extends vscode.Disposable {
// Give a chance for start-up errors to show up before rejecting with this more general error message
await delay(500);
// eslint-disable-next-line no-ex-assign
error = new Error("The process exited prematurely.");
error = new Error('The process exited prematurely.');
}
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(wrapCheckOutputWindow(error));
}
});
return result.trim();
}
finally {
} finally {
// Dispose event handlers
for (const d of disposables) {
d.dispose();
@ -149,21 +168,19 @@ export class MongoShell extends vscode.Disposable {
}
}
function startScriptTimeout(timeoutSeconds: number | 0, reject: (err: unknown) => void): void {
function startScriptTimeout(timeoutSeconds: number, reject: (err: unknown) => void): void {
if (timeoutSeconds > 0) {
setTimeout(
() => {
reject(timeoutMessage);
},
timeoutSeconds * 1000);
setTimeout(() => {
reject(timeoutMessage);
}, timeoutSeconds * 1000);
}
}
function convertToSingleLine(script: string): string {
return script.split(os.EOL)
.map(line => line.trim())
return script
.split(os.EOL)
.map((line) => line.trim())
.join('');
}
function removeSentinel(text: string, sentinel: string): { text: string; removed: boolean } {
@ -176,12 +193,12 @@ function removeSentinel(text: string, sentinel: string): { text: string; removed
}
async function delay(milliseconds: number): Promise<void> {
return new Promise(resolve => {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
}
function wrapCheckOutputWindow(error: unknown): unknown {
const checkOutputMsg = "The output window may contain additional information.";
const checkOutputMsg = 'The output window may contain additional information.';
return parseError(error).message.includes(checkOutputMsg) ? error : wrapError(error, checkOutputMsg);
}

Просмотреть файл

@ -3,14 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, IActionContext, ITreeItemPickerContext, callWithTelemetryAndErrorHandling } from "@microsoft/vscode-azext-utils";
import { Experience, MongoExperience } from "../../AzureDBExperiences";
import { ext } from "../../extensionVariables";
import { setConnectedNode } from "../setConnectedNode";
import { MongoDatabaseTreeItem } from "../tree/MongoDatabaseTreeItem";
import { pickMongo } from "./pickMongo";
import {
callWithTelemetryAndErrorHandling,
type AzExtTreeItem,
type IActionContext,
type ITreeItemPickerContext,
} from '@microsoft/vscode-azext-utils';
import { MongoExperience, type Experience } from '../../AzureDBExperiences';
import { ext } from '../../extensionVariables';
import { setConnectedNode } from '../setConnectedNode';
import { MongoDatabaseTreeItem } from '../tree/MongoDatabaseTreeItem';
import { pickMongo } from './pickMongo';
export const connectedMongoKey: string = "ms-azuretools.vscode-cosmosdb.connectedDB";
export const connectedMongoKey: string = 'ms-azuretools.vscode-cosmosdb.connectedDB';
export async function loadPersistedMongoDB(): Promise<void> {
return callWithTelemetryAndErrorHandling('cosmosDB.loadPersistedMongoDB', async (context: IActionContext) => {
@ -38,7 +43,10 @@ export async function loadPersistedMongoDB(): Promise<void> {
export async function connectMongoDatabase(context: IActionContext, node?: MongoDatabaseTreeItem): Promise<void> {
if (!node) {
// Include defaultExperience in the context to prevent https://github.com/microsoft/vscode-cosmosdb/issues/1517
const experienceContext: ITreeItemPickerContext & { defaultExperience?: Experience } = { ...context, defaultExperience: MongoExperience };
const experienceContext: ITreeItemPickerContext & { defaultExperience?: Experience } = {
...context,
defaultExperience: MongoExperience,
};
node = await pickMongo<MongoDatabaseTreeItem>(experienceContext, MongoDatabaseTreeItem.contextValue);
}

Просмотреть файл

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { MongoDatabaseTreeItem } from "../tree/MongoDatabaseTreeItem";
import { pickMongo } from "./pickMongo";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { MongoDatabaseTreeItem } from '../tree/MongoDatabaseTreeItem';
import { pickMongo } from './pickMongo';
export async function createMongoCollection(context: IActionContext, node?: MongoDatabaseTreeItem): Promise<void> {
if (!node) {

Просмотреть файл

@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { MongoAccountTreeItem } from "../tree/MongoAccountTreeItem";
import { MongoDatabaseTreeItem } from "../tree/MongoDatabaseTreeItem";
import { pickMongo } from "./pickMongo";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { type MongoAccountTreeItem } from '../tree/MongoAccountTreeItem';
import { type MongoDatabaseTreeItem } from '../tree/MongoDatabaseTreeItem';
import { pickMongo } from './pickMongo';
export async function createMongoDatabase(context: IActionContext, node?: MongoAccountTreeItem): Promise<void> {
if (!node) {

Просмотреть файл

@ -3,15 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { MongoCollectionTreeItem } from "../tree/MongoCollectionTreeItem";
import { pickMongo } from "./pickMongo";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { MongoCollectionTreeItem } from '../tree/MongoCollectionTreeItem';
import { pickMongo } from './pickMongo';
export async function createMongoDocument(context: IActionContext, node?: MongoCollectionTreeItem): Promise<void> {
if (!node) {
node = await pickMongo<MongoCollectionTreeItem>(context, MongoCollectionTreeItem.contextValue);
}
const documentNode = await node.createChild(context);
await vscode.commands.executeCommand("cosmosDB.openDocument", documentNode);
await vscode.commands.executeCommand('cosmosDB.openDocument', documentNode);
}

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscodeUtil from "../../utils/vscodeUtils";
import * as vscodeUtil from '../../utils/vscodeUtils';
export async function createMongoSrapbook(): Promise<void> {
await vscodeUtil.showNewFile('', 'Scrapbook', '.mongo');

Просмотреть файл

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { MongoCollectionTreeItem } from "../tree/MongoCollectionTreeItem";
import { pickMongo } from "./pickMongo";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import { MongoCollectionTreeItem } from '../tree/MongoCollectionTreeItem';
import { pickMongo } from './pickMongo';
export async function deleteMongoCollection(context: IActionContext, node?: MongoCollectionTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;

Просмотреть файл

@ -3,14 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { ext } from "../../extensionVariables";
import { localize } from "../../utils/localize";
import { setConnectedNode } from "../setConnectedNode";
import { MongoDatabaseTreeItem } from "../tree/MongoDatabaseTreeItem";
import { connectedMongoKey } from "./connectMongoDatabase";
import { pickMongo } from "./pickMongo";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ext } from '../../extensionVariables';
import { localize } from '../../utils/localize';
import { setConnectedNode } from '../setConnectedNode';
import { MongoDatabaseTreeItem } from '../tree/MongoDatabaseTreeItem';
import { connectedMongoKey } from './connectMongoDatabase';
import { pickMongo } from './pickMongo';
export async function deleteMongoDB(context: IActionContext, node?: MongoDatabaseTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;
@ -25,7 +25,7 @@ export async function deleteMongoDB(context: IActionContext, node?: MongoDatabas
// Temporary workaround for https://github.com/microsoft/vscode-cosmosdb/issues/1754
void ext.mongoLanguageClient.disconnect();
}
const successMessage = localize("deleteMongoDatabaseMsg", 'Successfully deleted database "{0}"', node.databaseName);
const successMessage = localize('deleteMongoDatabaseMsg', 'Successfully deleted database "{0}"', node.databaseName);
void vscode.window.showInformationMessage(successMessage);
ext.outputChannel.info(successMessage);
}

Просмотреть файл

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { MongoDocumentTreeItem } from "../tree/MongoDocumentTreeItem";
import { pickMongo } from "./pickMongo";
import { type IActionContext, type ITreeItemPickerContext } from '@microsoft/vscode-azext-utils';
import { MongoDocumentTreeItem } from '../tree/MongoDocumentTreeItem';
import { pickMongo } from './pickMongo';
export async function deleteMongoDocument(context: IActionContext, node?: MongoDocumentTreeItem): Promise<void> {
const suppressCreateContext: ITreeItemPickerContext = context;

Просмотреть файл

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { executeAllCommandsFromActiveEditor } from "../MongoScrapbook";
import { loadPersistedMongoDB } from "./connectMongoDatabase";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { executeAllCommandsFromActiveEditor } from '../MongoScrapbook';
import { loadPersistedMongoDB } from './connectMongoDatabase';
export async function executeAllMongoCommand(context: IActionContext): Promise<void> {
await loadPersistedMongoDB();

Просмотреть файл

@ -1,7 +1,7 @@
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { executeCommandFromActiveEditor } from "../MongoScrapbook";
import { loadPersistedMongoDB } from "./connectMongoDatabase";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import type * as vscode from 'vscode';
import { executeCommandFromActiveEditor } from '../MongoScrapbook';
import { loadPersistedMongoDB } from './connectMongoDatabase';
export async function executeMongoCommand(context: IActionContext, position?: vscode.Position): Promise<void> {
await loadPersistedMongoDB();

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from "vscode";
import * as vscode from 'vscode';
export function launchMongoShell(): void {
const terminal: vscode.Terminal = vscode.window.createTerminal('Mongo Shell');

Просмотреть файл

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "@microsoft/vscode-azext-utils";
import { ext } from "../../extensionVariables";
import { MongoCollectionTreeItem } from "../tree/MongoCollectionTreeItem";
import { pickMongo } from "./pickMongo";
import { type IActionContext } from '@microsoft/vscode-azext-utils';
import { ext } from '../../extensionVariables';
import { MongoCollectionTreeItem } from '../tree/MongoCollectionTreeItem';
import { pickMongo } from './pickMongo';
export async function openMongoCollection(context: IActionContext, node?: MongoCollectionTreeItem): Promise<void> {
if (!node) {

Просмотреть файл

@ -3,18 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils";
import { cosmosMongoFilter } from "../../constants";
import { ext } from "../../extensionVariables";
import { type AzExtTreeItem, type IActionContext } from '@microsoft/vscode-azext-utils';
import { cosmosMongoFilter } from '../../constants';
import { ext } from '../../extensionVariables';
export async function pickMongo<T extends AzExtTreeItem>(
context: IActionContext,
expectedContextValue?: string | RegExp | (string | RegExp)[]
expectedContextValue?: string | RegExp | (string | RegExp)[],
): Promise<T> {
return await ext.rgApi.pickAppResource<T>(context, {
filter: [
cosmosMongoFilter
],
expectedChildContextValue: expectedContextValue
filter: [cosmosMongoFilter],
expectedChildContextValue: expectedContextValue,
});
}

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MongoClient, MongoClientOptions } from 'mongodb';
import { MongoClient, type MongoClientOptions } from 'mongodb';
import { emulatorPassword, Links } from '../constants';
export async function connectToMongoClient(connectionString: string, appName: string): Promise<MongoClient> {
@ -13,7 +13,7 @@ export async function connectToMongoClient(connectionString: string, appName: st
appName: `@${appName}@`,
// https://github.com/lmammino/mongo-uri-builder/issues/2
useNewUrlParser: true,
useUnifiedTopology: true
useUnifiedTopology: true,
};
if (isCosmosEmulatorConnectionString(connectionString)) {
@ -25,7 +25,7 @@ export async function connectToMongoClient(connectionString: string, appName: st
return await MongoClient.connect(connectionString, options);
} catch (err) {
// Note: This file can't use `parseError` from `@microsoft/vscode-azext-utils` because it's used by languageService.ts - see that file for more info
const error = <{ message?: string, name?: string }>err;
const error = <{ message?: string; name?: string }>err;
const message = error && error.message;
// Example error: "failed to connect to server [localhost:10255] on first connect [MongoError: connect ECONNREFUSED 127.0.0.1:10255]"
@ -34,13 +34,16 @@ export async function connectToMongoClient(connectionString: string, appName: st
throw new MongoConnectError();
}
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw error;
}
}
export class MongoConnectError extends Error {
constructor() {
super(`Unable to connect to local Mongo DB instance. Make sure it is started correctly. See ${Links.LocalConnectionDebuggingTips} for tips.`);
super(
`Unable to connect to local Mongo DB instance. Make sure it is started correctly. See ${Links.LocalConnectionDebuggingTips} for tips.`,
);
}
}

Просмотреть файл

@ -3,12 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ANTLRErrorListener } from 'antlr4ts/ANTLRErrorListener';
import { RecognitionException } from 'antlr4ts/RecognitionException';
import { Recognizer } from 'antlr4ts/Recognizer';
import { Token } from 'antlr4ts/Token';
import * as vscode from "vscode";
import { ErrorDescription } from './MongoCommand';
import { type ANTLRErrorListener } from 'antlr4ts/ANTLRErrorListener';
import { type RecognitionException } from 'antlr4ts/RecognitionException';
import { type Recognizer } from 'antlr4ts/Recognizer';
import { type Token } from 'antlr4ts/Token';
import * as vscode from 'vscode';
import { type ErrorDescription } from './MongoCommand';
export class ParserErrorListener implements ANTLRErrorListener<Token> {
private _errors: ErrorDescription[] = [];
@ -24,15 +24,15 @@ export class ParserErrorListener implements ANTLRErrorListener<Token> {
line: number,
charPositionInLine: number,
msg: string,
e: RecognitionException | undefined): void {
e: RecognitionException | undefined,
): void {
const position = new vscode.Position(line - 1, charPositionInLine); // Symbol lines are 1-indexed. Position lines are 0-indexed
const range = new vscode.Range(position, position);
const error: ErrorDescription = {
message: msg,
range: range,
exception: e
exception: e,
};
this._errors.push(error);
}
@ -52,15 +52,15 @@ export class LexerErrorListener implements ANTLRErrorListener<number> {
line: number,
charPositionInLine: number,
msg: string,
e: RecognitionException | undefined): void {
e: RecognitionException | undefined,
): void {
const position = new vscode.Position(line - 1, charPositionInLine); // Symbol lines are 1-indexed. Position lines are 0-indexed
const range = new vscode.Range(position, position);
const error: ErrorDescription = {
message: msg,
range: range,
exception: e
exception: e,
};
this._errors.push(error);
}

Просмотреть файл

@ -9,19 +9,17 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import { ATN } from 'antlr4ts/atn/ATN';
import { type ATN } from 'antlr4ts/atn/ATN';
import { ATNDeserializer } from 'antlr4ts/atn/ATNDeserializer';
import { LexerATNSimulator } from 'antlr4ts/atn/LexerATNSimulator';
import { CharStream } from 'antlr4ts/CharStream';
import { type CharStream } from 'antlr4ts/CharStream';
import { NotNull, Override } from 'antlr4ts/Decorators';
import { Lexer } from 'antlr4ts/Lexer';
import * as Utils from 'antlr4ts/misc/Utils';
import { RuleContext } from 'antlr4ts/RuleContext';
import { Vocabulary } from 'antlr4ts/Vocabulary';
import { type RuleContext } from 'antlr4ts/RuleContext';
import { type Vocabulary } from 'antlr4ts/Vocabulary';
import { VocabularyImpl } from 'antlr4ts/VocabularyImpl';
export class mongoLexer extends Lexer {
public static readonly T__0 = 1;
public static readonly T__1 = 2;
@ -47,32 +45,95 @@ export class mongoLexer extends Lexer {
public static readonly DOUBLE_QUOTED_STRING_LITERAL = 22;
public static readonly SINGLE_QUOTED_STRING_LITERAL = 23;
public static readonly WHITESPACE = 24;
public static readonly modeNames: string[] = [
"DEFAULT_MODE"
];
public static readonly modeNames: string[] = ['DEFAULT_MODE'];
public static readonly ruleNames: string[] = [
"T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "RegexLiteral",
"RegexFlag", "SingleLineComment", "MultiLineComment", "StringLiteral",
"NullLiteral", "BooleanLiteral", "NumericLiteral", "DecimalLiteral", "LineTerminator",
"SEMICOLON", "DOT", "DB", "IDENTIFIER", "DOUBLE_QUOTED_STRING_LITERAL",
"SINGLE_QUOTED_STRING_LITERAL", "STRING_ESCAPE", "DecimalIntegerLiteral",
"ExponentPart", "DecimalDigit", "WHITESPACE"
'T__0',
'T__1',
'T__2',
'T__3',
'T__4',
'T__5',
'T__6',
'T__7',
'RegexLiteral',
'RegexFlag',
'SingleLineComment',
'MultiLineComment',
'StringLiteral',
'NullLiteral',
'BooleanLiteral',
'NumericLiteral',
'DecimalLiteral',
'LineTerminator',
'SEMICOLON',
'DOT',
'DB',
'IDENTIFIER',
'DOUBLE_QUOTED_STRING_LITERAL',
'SINGLE_QUOTED_STRING_LITERAL',
'STRING_ESCAPE',
'DecimalIntegerLiteral',
'ExponentPart',
'DecimalDigit',
'WHITESPACE',
];
private static readonly _LITERAL_NAMES: (string | undefined)[] = [
undefined, "'('", "','", "')'", "'{'", "'}'", "'['", "']'", "':'", undefined,
undefined, undefined, undefined, "'null'", undefined, undefined, undefined,
undefined, "';'", "'.'", "'db'"
undefined,
"'('",
"','",
"')'",
"'{'",
"'}'",
"'['",
"']'",
"':'",
undefined,
undefined,
undefined,
undefined,
"'null'",
undefined,
undefined,
undefined,
undefined,
"';'",
"'.'",
"'db'",
];
private static readonly _SYMBOLIC_NAMES: (string | undefined)[] = [
undefined, undefined, undefined, undefined, undefined, undefined, undefined,
undefined, undefined, "RegexLiteral", "SingleLineComment", "MultiLineComment",
"StringLiteral", "NullLiteral", "BooleanLiteral", "NumericLiteral", "DecimalLiteral",
"LineTerminator", "SEMICOLON", "DOT", "DB", "IDENTIFIER", "DOUBLE_QUOTED_STRING_LITERAL",
"SINGLE_QUOTED_STRING_LITERAL", "WHITESPACE"
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
'RegexLiteral',
'SingleLineComment',
'MultiLineComment',
'StringLiteral',
'NullLiteral',
'BooleanLiteral',
'NumericLiteral',
'DecimalLiteral',
'LineTerminator',
'SEMICOLON',
'DOT',
'DB',
'IDENTIFIER',
'DOUBLE_QUOTED_STRING_LITERAL',
'SINGLE_QUOTED_STRING_LITERAL',
'WHITESPACE',
];
public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(mongoLexer._LITERAL_NAMES, mongoLexer._SYMBOLIC_NAMES, []);
public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(
mongoLexer._LITERAL_NAMES,
mongoLexer._SYMBOLIC_NAMES,
[],
);
@Override
@NotNull
@ -80,28 +141,34 @@ export class mongoLexer extends Lexer {
return mongoLexer.VOCABULARY;
}
private isExternalIdentifierText(text) {
return text === 'db';
}
constructor(input: CharStream) {
super(input);
this._interp = new LexerATNSimulator(mongoLexer._ATN, this);
}
@Override
public get grammarFileName(): string { return "mongo.g4"; }
public get grammarFileName(): string {
return 'mongo.g4';
}
@Override
public get ruleNames(): string[] { return mongoLexer.ruleNames; }
public get ruleNames(): string[] {
return mongoLexer.ruleNames;
}
@Override
public get serializedATN(): string { return mongoLexer._serializedATN; }
public get serializedATN(): string {
return mongoLexer._serializedATN;
}
@Override
public get modeNames(): string[] { return mongoLexer.modeNames; }
public get modeNames(): string[] {
return mongoLexer.modeNames;
}
@Override
public sempred(_localctx: RuleContext, ruleIndex: number, predIndex: number): boolean {
@ -114,126 +181,125 @@ export class mongoLexer extends Lexer {
private IDENTIFIER_sempred(_localctx: RuleContext, predIndex: number): boolean {
switch (predIndex) {
case 0:
return !this.isExternalIdentifierText(this.text)
;
return !this.isExternalIdentifierText(this.text);
}
return true;
}
public static readonly _serializedATN: string =
"\x03\uAF6F\u8320\u479D\uB75C\u4880\u1605\u191C\uAB37\x02\x1A\xF2\b\x01" +
"\x04\x02\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04\x06\t\x06" +
"\x04\x07\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f\t\f\x04\r" +
"\t\r\x04\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11\x04\x12\t" +
"\x12\x04\x13\t\x13\x04\x14\t\x14\x04\x15\t\x15\x04\x16\t\x16\x04\x17\t" +
"\x17\x04\x18\t\x18\x04\x19\t\x19\x04\x1A\t\x1A\x04\x1B\t\x1B\x04\x1C\t" +
"\x1C\x04\x1D\t\x1D\x04\x1E\t\x1E\x03\x02\x03\x02\x03\x03\x03\x03\x03\x04" +
"\x03\x04\x03\x05\x03\x05\x03\x06\x03\x06\x03\x07\x03\x07\x03\b\x03\b\x03" +
"\t\x03\t\x03\n\x03\n\x03\n\x03\n\x05\nR\n\n\x03\n\x03\n\x03\n\x07\nW\n" +
"\n\f\n\x0E\nZ\v\n\x03\n\x03\n\x07\n^\n\n\f\n\x0E\na\v\n\x03\v\x03\v\x03" +
"\f\x03\f\x03\f\x03\f\x07\fi\n\f\f\f\x0E\fl\v\f\x03\f\x03\f\x03\r\x03\r" +
"\x03\r\x03\r\x07\rt\n\r\f\r\x0E\rw\v\r\x03\r\x03\r\x03\r\x03\r\x03\r\x03" +
"\x0E\x03\x0E\x05\x0E\x80\n\x0E\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x0F" +
"\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10" +
"\x05\x10\x90\n\x10\x03\x11\x05\x11\x93\n\x11\x03\x11\x03\x11\x03\x12\x03" +
"\x12\x03\x12\x06\x12\x9A\n\x12\r\x12\x0E\x12\x9B\x03\x12\x05\x12\x9F\n" +
"\x12\x03\x12\x03\x12\x06\x12\xA3\n\x12\r\x12\x0E\x12\xA4\x03\x12\x05\x12" +
"\xA8\n\x12\x03\x12\x03\x12\x05\x12\xAC\n\x12\x05\x12\xAE\n\x12\x03\x13" +
"\x03\x13\x03\x13\x03\x13\x03\x14\x03\x14\x03\x15\x03\x15\x03\x16\x03\x16" +
"\x03\x16\x03\x17\x03\x17\x06\x17\xBD\n\x17\r\x17\x0E\x17\xBE\x03\x17\x03" +
"\x17\x03\x18\x03\x18\x03\x18\x07\x18\xC6\n\x18\f\x18\x0E\x18\xC9\v\x18" +
"\x03\x18\x03\x18\x03\x19\x03\x19\x03\x19\x07\x19\xD0\n\x19\f\x19\x0E\x19" +
"\xD3\v\x19\x03\x19\x03\x19\x03\x1A\x03\x1A\x03\x1A\x03\x1B\x03\x1B\x03" +
"\x1B\x07\x1B\xDD\n\x1B\f\x1B\x0E\x1B\xE0\v\x1B\x05\x1B\xE2\n\x1B\x03\x1C" +
"\x03\x1C\x05\x1C\xE6\n\x1C\x03\x1C\x06\x1C\xE9\n\x1C\r\x1C\x0E\x1C\xEA" +
"\x03\x1D\x03\x1D\x03\x1E\x03\x1E\x03\x1E\x03\x1E\x03u\x02\x02\x1F\x03" +
"\x02\x03\x05\x02\x04\x07\x02\x05\t\x02\x06\v\x02\x07\r\x02\b\x0F\x02\t" +
"\x11\x02\n\x13\x02\v\x15\x02\x02\x17\x02\f\x19\x02\r\x1B\x02\x0E\x1D\x02" +
"\x0F\x1F\x02\x10!\x02\x11#\x02\x12%\x02\x13\'\x02\x14)\x02\x15+\x02\x16" +
"-\x02\x17/\x02\x181\x02\x193\x02\x025\x02\x027\x02\x029\x02\x02;\x02\x1A" +
"\x03\x02\x0F\x06\x02\f\f\x0F\x0F,,11\x05\x02\f\f\x0F\x0F11\x07\x02iik" +
"kooww{{\x05\x02\f\f\x0F\x0F\u202A\u202B\f\x02\v\f\x0F\x0F\"\"$$)+.0<=" +
"]_}}\x7F\x7F\x04\x02$$^^\x04\x02))^^\x05\x02$$))^^\x03\x023;\x04\x02G" +
"Ggg\x04\x02--//\x03\x022;\x04\x02\v\v\"\"\u0106\x02\x03\x03\x02\x02\x02" +
"\x02\x05\x03\x02\x02\x02\x02\x07\x03\x02\x02\x02\x02\t\x03\x02\x02\x02" +
"\x02\v\x03\x02\x02\x02\x02\r\x03\x02\x02\x02\x02\x0F\x03\x02\x02\x02\x02" +
"\x11\x03\x02\x02\x02\x02\x13\x03\x02\x02\x02\x02\x17\x03\x02\x02\x02\x02" +
"\x19\x03\x02\x02\x02\x02\x1B\x03\x02\x02\x02\x02\x1D\x03\x02\x02\x02\x02" +
"\x1F\x03\x02\x02\x02\x02!\x03\x02\x02\x02\x02#\x03\x02\x02\x02\x02%\x03" +
"\x02\x02\x02\x02\'\x03\x02\x02\x02\x02)\x03\x02\x02\x02\x02+\x03\x02\x02" +
"\x02\x02-\x03\x02\x02\x02\x02/\x03\x02\x02\x02\x021\x03\x02\x02\x02\x02" +
";\x03\x02\x02\x02\x03=\x03\x02\x02\x02\x05?\x03\x02\x02\x02\x07A\x03\x02" +
"\x02\x02\tC\x03\x02\x02\x02\vE\x03\x02\x02\x02\rG\x03\x02\x02\x02\x0F" +
"I\x03\x02\x02\x02\x11K\x03\x02\x02\x02\x13M\x03\x02\x02\x02\x15b\x03\x02" +
"\x02\x02\x17d\x03\x02\x02\x02\x19o\x03\x02\x02\x02\x1B\x7F\x03\x02\x02" +
"\x02\x1D\x81\x03\x02\x02\x02\x1F\x8F\x03\x02\x02\x02!\x92\x03\x02\x02" +
"\x02#\xAD\x03\x02\x02\x02%\xAF\x03\x02\x02\x02\'\xB3\x03\x02\x02\x02)" +
"\xB5\x03\x02\x02\x02+\xB7\x03\x02\x02\x02-\xBC\x03\x02\x02\x02/\xC2\x03" +
"\x02\x02\x021\xCC\x03\x02\x02\x023\xD6\x03\x02\x02\x025\xE1\x03\x02\x02" +
"\x027\xE3\x03\x02\x02\x029\xEC\x03\x02\x02\x02;\xEE\x03\x02\x02\x02=>" +
"\x07*\x02\x02>\x04\x03\x02\x02\x02?@\x07.\x02\x02@\x06\x03\x02\x02\x02" +
"AB\x07+\x02\x02B\b\x03\x02\x02\x02CD\x07}\x02\x02D\n\x03\x02\x02\x02E" +
"F\x07\x7F\x02\x02F\f\x03\x02\x02\x02GH\x07]\x02\x02H\x0E\x03\x02\x02\x02" +
"IJ\x07_\x02\x02J\x10\x03\x02\x02\x02KL\x07<\x02\x02L\x12\x03\x02\x02\x02" +
"MQ\x071\x02\x02NR\n\x02\x02\x02OP\x07^\x02\x02PR\x071\x02\x02QN\x03\x02" +
"\x02\x02QO\x03\x02\x02\x02RX\x03\x02\x02\x02SW\n\x03\x02\x02TU\x07^\x02" +
"\x02UW\x071\x02\x02VS\x03\x02\x02\x02VT\x03\x02\x02\x02WZ\x03\x02\x02" +
"\x02XV\x03\x02\x02\x02XY\x03\x02\x02\x02Y[\x03\x02\x02\x02ZX\x03\x02\x02" +
"\x02[_\x071\x02\x02\\^\x05\x15\v\x02]\\\x03\x02\x02\x02^a\x03\x02\x02" +
"\x02_]\x03\x02\x02\x02_`\x03\x02\x02\x02`\x14\x03\x02\x02\x02a_\x03\x02" +
"\x02\x02bc\t\x04\x02\x02c\x16\x03\x02\x02\x02de\x071\x02\x02ef\x071\x02" +
"\x02fj\x03\x02\x02\x02gi\n\x05\x02\x02hg\x03\x02\x02\x02il\x03\x02\x02" +
"\x02jh\x03\x02\x02\x02jk\x03\x02\x02\x02km\x03\x02\x02\x02lj\x03\x02\x02" +
"\x02mn\b\f\x02\x02n\x18\x03\x02\x02\x02op\x071\x02\x02pq\x07,\x02\x02" +
"qu\x03\x02\x02\x02rt\v\x02\x02\x02sr\x03\x02\x02\x02tw\x03\x02\x02\x02" +
"uv\x03\x02\x02\x02us\x03\x02\x02\x02vx\x03\x02\x02\x02wu\x03\x02\x02\x02" +
"xy\x07,\x02\x02yz\x071\x02\x02z{\x03\x02\x02\x02{|\b\r\x02\x02|\x1A\x03" +
"\x02\x02\x02}\x80\x051\x19\x02~\x80\x05/\x18\x02\x7F}\x03\x02\x02\x02" +
"\x7F~\x03\x02\x02\x02\x80\x1C\x03\x02\x02\x02\x81\x82\x07p\x02\x02\x82" +
"\x83\x07w\x02\x02\x83\x84\x07n\x02\x02\x84\x85\x07n\x02\x02\x85\x1E\x03" +
"\x02\x02\x02\x86\x87\x07v\x02\x02\x87\x88\x07t\x02\x02\x88\x89\x07w\x02" +
"\x02\x89\x90\x07g\x02\x02\x8A\x8B\x07h\x02\x02\x8B\x8C\x07c\x02\x02\x8C" +
"\x8D\x07n\x02\x02\x8D\x8E\x07u\x02\x02\x8E\x90\x07g\x02\x02\x8F\x86\x03" +
"\x02\x02\x02\x8F\x8A\x03\x02\x02\x02\x90 \x03\x02\x02\x02\x91\x93\x07" +
"/\x02\x02\x92\x91\x03\x02\x02\x02\x92\x93\x03\x02\x02\x02\x93\x94\x03" +
"\x02\x02\x02\x94\x95\x05#\x12\x02\x95\"\x03\x02\x02\x02\x96\x97\x055\x1B" +
"\x02\x97\x99\x070\x02\x02\x98\x9A\x059\x1D\x02\x99\x98\x03\x02\x02\x02" +
"\x9A\x9B\x03\x02\x02\x02\x9B\x99\x03\x02\x02\x02\x9B\x9C\x03\x02\x02\x02" +
"\x9C\x9E\x03\x02\x02\x02\x9D\x9F\x057\x1C\x02\x9E\x9D\x03\x02\x02\x02" +
"\x9E\x9F\x03\x02\x02\x02\x9F\xAE\x03\x02\x02\x02\xA0\xA2\x070\x02\x02" +
"\xA1\xA3\x059\x1D\x02\xA2\xA1\x03\x02\x02\x02\xA3\xA4\x03\x02\x02\x02" +
"\xA4\xA2\x03\x02\x02\x02\xA4\xA5\x03\x02\x02\x02\xA5\xA7\x03\x02\x02\x02" +
"\xA6\xA8\x057\x1C\x02\xA7\xA6\x03\x02\x02\x02\xA7\xA8\x03\x02\x02\x02" +
"\xA8\xAE\x03\x02\x02\x02\xA9\xAB\x055\x1B\x02\xAA\xAC\x057\x1C\x02\xAB" +
"\xAA\x03\x02\x02\x02\xAB\xAC\x03\x02\x02\x02\xAC\xAE\x03\x02\x02\x02\xAD" +
"\x96\x03\x02\x02\x02\xAD\xA0\x03\x02\x02\x02\xAD\xA9\x03\x02\x02\x02\xAE" +
"$\x03\x02\x02\x02\xAF\xB0\t\x05\x02\x02\xB0\xB1\x03\x02\x02\x02\xB1\xB2" +
"\b\x13\x02\x02\xB2&\x03\x02\x02\x02\xB3\xB4\x07=\x02\x02\xB4(\x03\x02" +
"\x02\x02\xB5\xB6\x070\x02\x02\xB6*\x03\x02\x02\x02\xB7\xB8\x07f\x02\x02" +
"\xB8\xB9\x07d\x02\x02\xB9,\x03\x02\x02\x02\xBA\xBD\n\x06\x02\x02\xBB\xBD" +
"\x053\x1A\x02\xBC\xBA\x03\x02\x02\x02\xBC\xBB\x03\x02\x02\x02\xBD\xBE" +
"\x03\x02\x02\x02\xBE\xBC\x03\x02\x02\x02\xBE\xBF\x03\x02\x02\x02\xBF\xC0" +
"\x03\x02\x02\x02\xC0\xC1\x06\x17\x02\x02\xC1.\x03\x02\x02\x02\xC2\xC7" +
"\x07$\x02\x02\xC3\xC6\n\x07\x02\x02\xC4\xC6\x053\x1A\x02\xC5\xC3\x03\x02" +
"\x02\x02\xC5\xC4\x03\x02\x02\x02\xC6\xC9\x03\x02\x02\x02\xC7\xC5\x03\x02" +
"\x02\x02\xC7\xC8\x03\x02\x02\x02\xC8\xCA\x03\x02\x02\x02\xC9\xC7\x03\x02" +
"\x02\x02\xCA\xCB\x07$\x02\x02\xCB0\x03\x02\x02\x02\xCC\xD1\x07)\x02\x02" +
"\xCD\xD0\n\b\x02\x02\xCE\xD0\x053\x1A\x02\xCF\xCD\x03\x02\x02\x02\xCF" +
"\xCE\x03\x02\x02\x02\xD0\xD3\x03\x02\x02\x02\xD1\xCF\x03\x02\x02\x02\xD1" +
"\xD2\x03\x02\x02\x02\xD2\xD4\x03\x02\x02\x02\xD3\xD1\x03\x02\x02\x02\xD4" +
"\xD5\x07)\x02\x02\xD52\x03\x02\x02\x02\xD6\xD7\x07^\x02\x02\xD7\xD8\t" +
"\t\x02\x02\xD84\x03\x02\x02\x02\xD9\xE2\x072\x02\x02\xDA\xDE\t\n\x02\x02" +
"\xDB\xDD\x059\x1D\x02\xDC\xDB\x03\x02\x02\x02\xDD\xE0\x03\x02\x02\x02" +
"\xDE\xDC\x03\x02\x02\x02\xDE\xDF\x03\x02\x02\x02\xDF\xE2\x03\x02\x02\x02" +
"\xE0\xDE\x03\x02\x02\x02\xE1\xD9\x03\x02\x02\x02\xE1\xDA\x03\x02\x02\x02" +
"\xE26\x03\x02\x02\x02\xE3\xE5\t\v\x02\x02\xE4\xE6\t\f\x02\x02\xE5\xE4" +
"\x03\x02\x02\x02\xE5\xE6\x03\x02\x02\x02\xE6\xE8\x03\x02\x02\x02\xE7\xE9" +
"\x059\x1D\x02\xE8\xE7\x03\x02\x02\x02\xE9\xEA\x03\x02\x02\x02\xEA\xE8" +
"\x03\x02\x02\x02\xEA\xEB\x03\x02\x02\x02\xEB8\x03\x02\x02\x02\xEC\xED" +
"\t\r\x02\x02\xED:\x03\x02\x02\x02\xEE\xEF\t\x0E\x02\x02\xEF\xF0\x03\x02" +
"\x02\x02\xF0\xF1\b\x1E\x03\x02\xF1<\x03\x02\x02\x02\x1C\x02QVX_ju\x7F" +
"\x8F\x92\x9B\x9E\xA4\xA7\xAB\xAD\xBC\xBE\xC5\xC7\xCF\xD1\xDE\xE1\xE5\xEA" +
"\x04\x02\x03\x02\b\x02\x02";
'\x03\uAF6F\u8320\u479D\uB75C\u4880\u1605\u191C\uAB37\x02\x1A\xF2\b\x01' +
'\x04\x02\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04\x06\t\x06' +
'\x04\x07\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f\t\f\x04\r' +
'\t\r\x04\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11\x04\x12\t' +
'\x12\x04\x13\t\x13\x04\x14\t\x14\x04\x15\t\x15\x04\x16\t\x16\x04\x17\t' +
'\x17\x04\x18\t\x18\x04\x19\t\x19\x04\x1A\t\x1A\x04\x1B\t\x1B\x04\x1C\t' +
'\x1C\x04\x1D\t\x1D\x04\x1E\t\x1E\x03\x02\x03\x02\x03\x03\x03\x03\x03\x04' +
'\x03\x04\x03\x05\x03\x05\x03\x06\x03\x06\x03\x07\x03\x07\x03\b\x03\b\x03' +
'\t\x03\t\x03\n\x03\n\x03\n\x03\n\x05\nR\n\n\x03\n\x03\n\x03\n\x07\nW\n' +
'\n\f\n\x0E\nZ\v\n\x03\n\x03\n\x07\n^\n\n\f\n\x0E\na\v\n\x03\v\x03\v\x03' +
'\f\x03\f\x03\f\x03\f\x07\fi\n\f\f\f\x0E\fl\v\f\x03\f\x03\f\x03\r\x03\r' +
'\x03\r\x03\r\x07\rt\n\r\f\r\x0E\rw\v\r\x03\r\x03\r\x03\r\x03\r\x03\r\x03' +
'\x0E\x03\x0E\x05\x0E\x80\n\x0E\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x0F' +
'\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10' +
'\x05\x10\x90\n\x10\x03\x11\x05\x11\x93\n\x11\x03\x11\x03\x11\x03\x12\x03' +
'\x12\x03\x12\x06\x12\x9A\n\x12\r\x12\x0E\x12\x9B\x03\x12\x05\x12\x9F\n' +
'\x12\x03\x12\x03\x12\x06\x12\xA3\n\x12\r\x12\x0E\x12\xA4\x03\x12\x05\x12' +
'\xA8\n\x12\x03\x12\x03\x12\x05\x12\xAC\n\x12\x05\x12\xAE\n\x12\x03\x13' +
'\x03\x13\x03\x13\x03\x13\x03\x14\x03\x14\x03\x15\x03\x15\x03\x16\x03\x16' +
'\x03\x16\x03\x17\x03\x17\x06\x17\xBD\n\x17\r\x17\x0E\x17\xBE\x03\x17\x03' +
'\x17\x03\x18\x03\x18\x03\x18\x07\x18\xC6\n\x18\f\x18\x0E\x18\xC9\v\x18' +
'\x03\x18\x03\x18\x03\x19\x03\x19\x03\x19\x07\x19\xD0\n\x19\f\x19\x0E\x19' +
'\xD3\v\x19\x03\x19\x03\x19\x03\x1A\x03\x1A\x03\x1A\x03\x1B\x03\x1B\x03' +
'\x1B\x07\x1B\xDD\n\x1B\f\x1B\x0E\x1B\xE0\v\x1B\x05\x1B\xE2\n\x1B\x03\x1C' +
'\x03\x1C\x05\x1C\xE6\n\x1C\x03\x1C\x06\x1C\xE9\n\x1C\r\x1C\x0E\x1C\xEA' +
'\x03\x1D\x03\x1D\x03\x1E\x03\x1E\x03\x1E\x03\x1E\x03u\x02\x02\x1F\x03' +
'\x02\x03\x05\x02\x04\x07\x02\x05\t\x02\x06\v\x02\x07\r\x02\b\x0F\x02\t' +
'\x11\x02\n\x13\x02\v\x15\x02\x02\x17\x02\f\x19\x02\r\x1B\x02\x0E\x1D\x02' +
"\x0F\x1F\x02\x10!\x02\x11#\x02\x12%\x02\x13'\x02\x14)\x02\x15+\x02\x16" +
'-\x02\x17/\x02\x181\x02\x193\x02\x025\x02\x027\x02\x029\x02\x02;\x02\x1A' +
'\x03\x02\x0F\x06\x02\f\f\x0F\x0F,,11\x05\x02\f\f\x0F\x0F11\x07\x02iik' +
'kooww{{\x05\x02\f\f\x0F\x0F\u202A\u202B\f\x02\v\f\x0F\x0F""$$)+.0<=' +
']_}}\x7F\x7F\x04\x02$$^^\x04\x02))^^\x05\x02$$))^^\x03\x023;\x04\x02G' +
'Ggg\x04\x02--//\x03\x022;\x04\x02\v\v""\u0106\x02\x03\x03\x02\x02\x02' +
'\x02\x05\x03\x02\x02\x02\x02\x07\x03\x02\x02\x02\x02\t\x03\x02\x02\x02' +
'\x02\v\x03\x02\x02\x02\x02\r\x03\x02\x02\x02\x02\x0F\x03\x02\x02\x02\x02' +
'\x11\x03\x02\x02\x02\x02\x13\x03\x02\x02\x02\x02\x17\x03\x02\x02\x02\x02' +
'\x19\x03\x02\x02\x02\x02\x1B\x03\x02\x02\x02\x02\x1D\x03\x02\x02\x02\x02' +
'\x1F\x03\x02\x02\x02\x02!\x03\x02\x02\x02\x02#\x03\x02\x02\x02\x02%\x03' +
"\x02\x02\x02\x02'\x03\x02\x02\x02\x02)\x03\x02\x02\x02\x02+\x03\x02\x02" +
'\x02\x02-\x03\x02\x02\x02\x02/\x03\x02\x02\x02\x021\x03\x02\x02\x02\x02' +
';\x03\x02\x02\x02\x03=\x03\x02\x02\x02\x05?\x03\x02\x02\x02\x07A\x03\x02' +
'\x02\x02\tC\x03\x02\x02\x02\vE\x03\x02\x02\x02\rG\x03\x02\x02\x02\x0F' +
'I\x03\x02\x02\x02\x11K\x03\x02\x02\x02\x13M\x03\x02\x02\x02\x15b\x03\x02' +
'\x02\x02\x17d\x03\x02\x02\x02\x19o\x03\x02\x02\x02\x1B\x7F\x03\x02\x02' +
'\x02\x1D\x81\x03\x02\x02\x02\x1F\x8F\x03\x02\x02\x02!\x92\x03\x02\x02' +
"\x02#\xAD\x03\x02\x02\x02%\xAF\x03\x02\x02\x02'\xB3\x03\x02\x02\x02)" +
'\xB5\x03\x02\x02\x02+\xB7\x03\x02\x02\x02-\xBC\x03\x02\x02\x02/\xC2\x03' +
'\x02\x02\x021\xCC\x03\x02\x02\x023\xD6\x03\x02\x02\x025\xE1\x03\x02\x02' +
'\x027\xE3\x03\x02\x02\x029\xEC\x03\x02\x02\x02;\xEE\x03\x02\x02\x02=>' +
'\x07*\x02\x02>\x04\x03\x02\x02\x02?@\x07.\x02\x02@\x06\x03\x02\x02\x02' +
'AB\x07+\x02\x02B\b\x03\x02\x02\x02CD\x07}\x02\x02D\n\x03\x02\x02\x02E' +
'F\x07\x7F\x02\x02F\f\x03\x02\x02\x02GH\x07]\x02\x02H\x0E\x03\x02\x02\x02' +
'IJ\x07_\x02\x02J\x10\x03\x02\x02\x02KL\x07<\x02\x02L\x12\x03\x02\x02\x02' +
'MQ\x071\x02\x02NR\n\x02\x02\x02OP\x07^\x02\x02PR\x071\x02\x02QN\x03\x02' +
'\x02\x02QO\x03\x02\x02\x02RX\x03\x02\x02\x02SW\n\x03\x02\x02TU\x07^\x02' +
'\x02UW\x071\x02\x02VS\x03\x02\x02\x02VT\x03\x02\x02\x02WZ\x03\x02\x02' +
'\x02XV\x03\x02\x02\x02XY\x03\x02\x02\x02Y[\x03\x02\x02\x02ZX\x03\x02\x02' +
'\x02[_\x071\x02\x02\\^\x05\x15\v\x02]\\\x03\x02\x02\x02^a\x03\x02\x02' +
'\x02_]\x03\x02\x02\x02_`\x03\x02\x02\x02`\x14\x03\x02\x02\x02a_\x03\x02' +
'\x02\x02bc\t\x04\x02\x02c\x16\x03\x02\x02\x02de\x071\x02\x02ef\x071\x02' +
'\x02fj\x03\x02\x02\x02gi\n\x05\x02\x02hg\x03\x02\x02\x02il\x03\x02\x02' +
'\x02jh\x03\x02\x02\x02jk\x03\x02\x02\x02km\x03\x02\x02\x02lj\x03\x02\x02' +
'\x02mn\b\f\x02\x02n\x18\x03\x02\x02\x02op\x071\x02\x02pq\x07,\x02\x02' +
'qu\x03\x02\x02\x02rt\v\x02\x02\x02sr\x03\x02\x02\x02tw\x03\x02\x02\x02' +
'uv\x03\x02\x02\x02us\x03\x02\x02\x02vx\x03\x02\x02\x02wu\x03\x02\x02\x02' +
'xy\x07,\x02\x02yz\x071\x02\x02z{\x03\x02\x02\x02{|\b\r\x02\x02|\x1A\x03' +
'\x02\x02\x02}\x80\x051\x19\x02~\x80\x05/\x18\x02\x7F}\x03\x02\x02\x02' +
'\x7F~\x03\x02\x02\x02\x80\x1C\x03\x02\x02\x02\x81\x82\x07p\x02\x02\x82' +
'\x83\x07w\x02\x02\x83\x84\x07n\x02\x02\x84\x85\x07n\x02\x02\x85\x1E\x03' +
'\x02\x02\x02\x86\x87\x07v\x02\x02\x87\x88\x07t\x02\x02\x88\x89\x07w\x02' +
'\x02\x89\x90\x07g\x02\x02\x8A\x8B\x07h\x02\x02\x8B\x8C\x07c\x02\x02\x8C' +
'\x8D\x07n\x02\x02\x8D\x8E\x07u\x02\x02\x8E\x90\x07g\x02\x02\x8F\x86\x03' +
'\x02\x02\x02\x8F\x8A\x03\x02\x02\x02\x90 \x03\x02\x02\x02\x91\x93\x07' +
'/\x02\x02\x92\x91\x03\x02\x02\x02\x92\x93\x03\x02\x02\x02\x93\x94\x03' +
'\x02\x02\x02\x94\x95\x05#\x12\x02\x95"\x03\x02\x02\x02\x96\x97\x055\x1B' +
'\x02\x97\x99\x070\x02\x02\x98\x9A\x059\x1D\x02\x99\x98\x03\x02\x02\x02' +
'\x9A\x9B\x03\x02\x02\x02\x9B\x99\x03\x02\x02\x02\x9B\x9C\x03\x02\x02\x02' +
'\x9C\x9E\x03\x02\x02\x02\x9D\x9F\x057\x1C\x02\x9E\x9D\x03\x02\x02\x02' +
'\x9E\x9F\x03\x02\x02\x02\x9F\xAE\x03\x02\x02\x02\xA0\xA2\x070\x02\x02' +
'\xA1\xA3\x059\x1D\x02\xA2\xA1\x03\x02\x02\x02\xA3\xA4\x03\x02\x02\x02' +
'\xA4\xA2\x03\x02\x02\x02\xA4\xA5\x03\x02\x02\x02\xA5\xA7\x03\x02\x02\x02' +
'\xA6\xA8\x057\x1C\x02\xA7\xA6\x03\x02\x02\x02\xA7\xA8\x03\x02\x02\x02' +
'\xA8\xAE\x03\x02\x02\x02\xA9\xAB\x055\x1B\x02\xAA\xAC\x057\x1C\x02\xAB' +
'\xAA\x03\x02\x02\x02\xAB\xAC\x03\x02\x02\x02\xAC\xAE\x03\x02\x02\x02\xAD' +
'\x96\x03\x02\x02\x02\xAD\xA0\x03\x02\x02\x02\xAD\xA9\x03\x02\x02\x02\xAE' +
'$\x03\x02\x02\x02\xAF\xB0\t\x05\x02\x02\xB0\xB1\x03\x02\x02\x02\xB1\xB2' +
'\b\x13\x02\x02\xB2&\x03\x02\x02\x02\xB3\xB4\x07=\x02\x02\xB4(\x03\x02' +
'\x02\x02\xB5\xB6\x070\x02\x02\xB6*\x03\x02\x02\x02\xB7\xB8\x07f\x02\x02' +
'\xB8\xB9\x07d\x02\x02\xB9,\x03\x02\x02\x02\xBA\xBD\n\x06\x02\x02\xBB\xBD' +
'\x053\x1A\x02\xBC\xBA\x03\x02\x02\x02\xBC\xBB\x03\x02\x02\x02\xBD\xBE' +
'\x03\x02\x02\x02\xBE\xBC\x03\x02\x02\x02\xBE\xBF\x03\x02\x02\x02\xBF\xC0' +
'\x03\x02\x02\x02\xC0\xC1\x06\x17\x02\x02\xC1.\x03\x02\x02\x02\xC2\xC7' +
'\x07$\x02\x02\xC3\xC6\n\x07\x02\x02\xC4\xC6\x053\x1A\x02\xC5\xC3\x03\x02' +
'\x02\x02\xC5\xC4\x03\x02\x02\x02\xC6\xC9\x03\x02\x02\x02\xC7\xC5\x03\x02' +
'\x02\x02\xC7\xC8\x03\x02\x02\x02\xC8\xCA\x03\x02\x02\x02\xC9\xC7\x03\x02' +
'\x02\x02\xCA\xCB\x07$\x02\x02\xCB0\x03\x02\x02\x02\xCC\xD1\x07)\x02\x02' +
'\xCD\xD0\n\b\x02\x02\xCE\xD0\x053\x1A\x02\xCF\xCD\x03\x02\x02\x02\xCF' +
'\xCE\x03\x02\x02\x02\xD0\xD3\x03\x02\x02\x02\xD1\xCF\x03\x02\x02\x02\xD1' +
'\xD2\x03\x02\x02\x02\xD2\xD4\x03\x02\x02\x02\xD3\xD1\x03\x02\x02\x02\xD4' +
'\xD5\x07)\x02\x02\xD52\x03\x02\x02\x02\xD6\xD7\x07^\x02\x02\xD7\xD8\t' +
'\t\x02\x02\xD84\x03\x02\x02\x02\xD9\xE2\x072\x02\x02\xDA\xDE\t\n\x02\x02' +
'\xDB\xDD\x059\x1D\x02\xDC\xDB\x03\x02\x02\x02\xDD\xE0\x03\x02\x02\x02' +
'\xDE\xDC\x03\x02\x02\x02\xDE\xDF\x03\x02\x02\x02\xDF\xE2\x03\x02\x02\x02' +
'\xE0\xDE\x03\x02\x02\x02\xE1\xD9\x03\x02\x02\x02\xE1\xDA\x03\x02\x02\x02' +
'\xE26\x03\x02\x02\x02\xE3\xE5\t\v\x02\x02\xE4\xE6\t\f\x02\x02\xE5\xE4' +
'\x03\x02\x02\x02\xE5\xE6\x03\x02\x02\x02\xE6\xE8\x03\x02\x02\x02\xE7\xE9' +
'\x059\x1D\x02\xE8\xE7\x03\x02\x02\x02\xE9\xEA\x03\x02\x02\x02\xEA\xE8' +
'\x03\x02\x02\x02\xEA\xEB\x03\x02\x02\x02\xEB8\x03\x02\x02\x02\xEC\xED' +
'\t\r\x02\x02\xED:\x03\x02\x02\x02\xEE\xEF\t\x0E\x02\x02\xEF\xF0\x03\x02' +
'\x02\x02\xF0\xF1\b\x1E\x03\x02\xF1<\x03\x02\x02\x02\x1C\x02QVX_ju\x7F' +
'\x8F\x92\x9B\x9E\xA4\xA7\xAB\xAD\xBC\xBE\xC5\xC7\xCF\xD1\xDE\xE1\xE5\xEA' +
'\x04\x02\x03\x02\b\x02\x02';
public static __ATN: ATN;
public static get _ATN(): ATN {
if (!mongoLexer.__ATN) {
@ -242,6 +308,4 @@ export class mongoLexer extends Lexer {
return mongoLexer.__ATN;
}
}

Просмотреть файл

@ -1,208 +1,220 @@
// Generated from ./grammar/mongo.g4 by ANTLR 4.6-SNAPSHOT
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ParseTreeListener } from 'antlr4ts/tree/ParseTreeListener';
import { ArgumentContext, ArgumentsContext, ArrayLiteralContext, CollectionContext, CommandContext, CommandsContext, CommentContext, ElementListContext, EmptyCommandContext, FunctionCallContext, LiteralContext, MongoCommandsContext, ObjectLiteralContext, PropertyAssignmentContext, PropertyNameAndValueListContext, PropertyNameContext, PropertyValueContext } from './mongoParser';
import { type ParseTreeListener } from 'antlr4ts/tree/ParseTreeListener';
import {
type ArgumentContext,
type ArgumentsContext,
type ArrayLiteralContext,
type CollectionContext,
type CommandContext,
type CommandsContext,
type CommentContext,
type ElementListContext,
type EmptyCommandContext,
type FunctionCallContext,
type LiteralContext,
type MongoCommandsContext,
type ObjectLiteralContext,
type PropertyAssignmentContext,
type PropertyNameAndValueListContext,
type PropertyNameContext,
type PropertyValueContext,
} from './mongoParser';
/**
* This interface defines a complete listener for a parse tree produced by
* `mongoParser`.
*/
export interface mongoListener extends ParseTreeListener {
/**
* Enter a parse tree produced by `mongoParser.mongoCommands`.
* @param ctx the parse tree
*/
enterMongoCommands?: (ctx: MongoCommandsContext) => void;
/**
* Exit a parse tree produced by `mongoParser.mongoCommands`.
* @param ctx the parse tree
*/
exitMongoCommands?: (ctx: MongoCommandsContext) => void;
/**
* Enter a parse tree produced by `mongoParser.mongoCommands`.
* @param ctx the parse tree
*/
enterMongoCommands?: (ctx: MongoCommandsContext) => void;
/**
* Exit a parse tree produced by `mongoParser.mongoCommands`.
* @param ctx the parse tree
*/
exitMongoCommands?: (ctx: MongoCommandsContext) => void;
/**
* Enter a parse tree produced by `mongoParser.commands`.
* @param ctx the parse tree
*/
enterCommands?: (ctx: CommandsContext) => void;
/**
* Exit a parse tree produced by `mongoParser.commands`.
* @param ctx the parse tree
*/
exitCommands?: (ctx: CommandsContext) => void;
/**
* Enter a parse tree produced by `mongoParser.commands`.
* @param ctx the parse tree
*/
enterCommands?: (ctx: CommandsContext) => void;
/**
* Exit a parse tree produced by `mongoParser.commands`.
* @param ctx the parse tree
*/
exitCommands?: (ctx: CommandsContext) => void;
/**
* Enter a parse tree produced by `mongoParser.command`.
* @param ctx the parse tree
*/
enterCommand?: (ctx: CommandContext) => void;
/**
* Exit a parse tree produced by `mongoParser.command`.
* @param ctx the parse tree
*/
exitCommand?: (ctx: CommandContext) => void;
/**
* Enter a parse tree produced by `mongoParser.command`.
* @param ctx the parse tree
*/
enterCommand?: (ctx: CommandContext) => void;
/**
* Exit a parse tree produced by `mongoParser.command`.
* @param ctx the parse tree
*/
exitCommand?: (ctx: CommandContext) => void;
/**
* Enter a parse tree produced by `mongoParser.emptyCommand`.
* @param ctx the parse tree
*/
enterEmptyCommand?: (ctx: EmptyCommandContext) => void;
/**
* Exit a parse tree produced by `mongoParser.emptyCommand`.
* @param ctx the parse tree
*/
exitEmptyCommand?: (ctx: EmptyCommandContext) => void;
/**
* Enter a parse tree produced by `mongoParser.emptyCommand`.
* @param ctx the parse tree
*/
enterEmptyCommand?: (ctx: EmptyCommandContext) => void;
/**
* Exit a parse tree produced by `mongoParser.emptyCommand`.
* @param ctx the parse tree
*/
exitEmptyCommand?: (ctx: EmptyCommandContext) => void;
/**
* Enter a parse tree produced by `mongoParser.collection`.
* @param ctx the parse tree
*/
enterCollection?: (ctx: CollectionContext) => void;
/**
* Exit a parse tree produced by `mongoParser.collection`.
* @param ctx the parse tree
*/
exitCollection?: (ctx: CollectionContext) => void;
/**
* Enter a parse tree produced by `mongoParser.collection`.
* @param ctx the parse tree
*/
enterCollection?: (ctx: CollectionContext) => void;
/**
* Exit a parse tree produced by `mongoParser.collection`.
* @param ctx the parse tree
*/
exitCollection?: (ctx: CollectionContext) => void;
/**
* Enter a parse tree produced by `mongoParser.functionCall`.
* @param ctx the parse tree
*/
enterFunctionCall?: (ctx: FunctionCallContext) => void;
/**
* Exit a parse tree produced by `mongoParser.functionCall`.
* @param ctx the parse tree
*/
exitFunctionCall?: (ctx: FunctionCallContext) => void;
/**
* Enter a parse tree produced by `mongoParser.functionCall`.
* @param ctx the parse tree
*/
enterFunctionCall?: (ctx: FunctionCallContext) => void;
/**
* Exit a parse tree produced by `mongoParser.functionCall`.
* @param ctx the parse tree
*/
exitFunctionCall?: (ctx: FunctionCallContext) => void;
/**
* Enter a parse tree produced by `mongoParser.arguments`.
* @param ctx the parse tree
*/
enterArguments?: (ctx: ArgumentsContext) => void;
/**
* Exit a parse tree produced by `mongoParser.arguments`.
* @param ctx the parse tree
*/
exitArguments?: (ctx: ArgumentsContext) => void;
/**
* Enter a parse tree produced by `mongoParser.arguments`.
* @param ctx the parse tree
*/
enterArguments?: (ctx: ArgumentsContext) => void;
/**
* Exit a parse tree produced by `mongoParser.arguments`.
* @param ctx the parse tree
*/
exitArguments?: (ctx: ArgumentsContext) => void;
/**
* Enter a parse tree produced by `mongoParser.argument`.
* @param ctx the parse tree
*/
enterArgument?: (ctx: ArgumentContext) => void;
/**
* Exit a parse tree produced by `mongoParser.argument`.
* @param ctx the parse tree
*/
exitArgument?: (ctx: ArgumentContext) => void;
/**
* Enter a parse tree produced by `mongoParser.argument`.
* @param ctx the parse tree
*/
enterArgument?: (ctx: ArgumentContext) => void;
/**
* Exit a parse tree produced by `mongoParser.argument`.
* @param ctx the parse tree
*/
exitArgument?: (ctx: ArgumentContext) => void;
/**
* Enter a parse tree produced by `mongoParser.objectLiteral`.
* @param ctx the parse tree
*/
enterObjectLiteral?: (ctx: ObjectLiteralContext) => void;
/**
* Exit a parse tree produced by `mongoParser.objectLiteral`.
* @param ctx the parse tree
*/
exitObjectLiteral?: (ctx: ObjectLiteralContext) => void;
/**
* Enter a parse tree produced by `mongoParser.objectLiteral`.
* @param ctx the parse tree
*/
enterObjectLiteral?: (ctx: ObjectLiteralContext) => void;
/**
* Exit a parse tree produced by `mongoParser.objectLiteral`.
* @param ctx the parse tree
*/
exitObjectLiteral?: (ctx: ObjectLiteralContext) => void;
/**
* Enter a parse tree produced by `mongoParser.arrayLiteral`.
* @param ctx the parse tree
*/
enterArrayLiteral?: (ctx: ArrayLiteralContext) => void;
/**
* Exit a parse tree produced by `mongoParser.arrayLiteral`.
* @param ctx the parse tree
*/
exitArrayLiteral?: (ctx: ArrayLiteralContext) => void;
/**
* Enter a parse tree produced by `mongoParser.arrayLiteral`.
* @param ctx the parse tree
*/
enterArrayLiteral?: (ctx: ArrayLiteralContext) => void;
/**
* Exit a parse tree produced by `mongoParser.arrayLiteral`.
* @param ctx the parse tree
*/
exitArrayLiteral?: (ctx: ArrayLiteralContext) => void;
/**
* Enter a parse tree produced by `mongoParser.elementList`.
* @param ctx the parse tree
*/
enterElementList?: (ctx: ElementListContext) => void;
/**
* Exit a parse tree produced by `mongoParser.elementList`.
* @param ctx the parse tree
*/
exitElementList?: (ctx: ElementListContext) => void;
/**
* Enter a parse tree produced by `mongoParser.elementList`.
* @param ctx the parse tree
*/
enterElementList?: (ctx: ElementListContext) => void;
/**
* Exit a parse tree produced by `mongoParser.elementList`.
* @param ctx the parse tree
*/
exitElementList?: (ctx: ElementListContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyNameAndValueList`.
* @param ctx the parse tree
*/
enterPropertyNameAndValueList?: (ctx: PropertyNameAndValueListContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyNameAndValueList`.
* @param ctx the parse tree
*/
exitPropertyNameAndValueList?: (ctx: PropertyNameAndValueListContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyNameAndValueList`.
* @param ctx the parse tree
*/
enterPropertyNameAndValueList?: (ctx: PropertyNameAndValueListContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyNameAndValueList`.
* @param ctx the parse tree
*/
exitPropertyNameAndValueList?: (ctx: PropertyNameAndValueListContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyAssignment`.
* @param ctx the parse tree
*/
enterPropertyAssignment?: (ctx: PropertyAssignmentContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyAssignment`.
* @param ctx the parse tree
*/
exitPropertyAssignment?: (ctx: PropertyAssignmentContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyAssignment`.
* @param ctx the parse tree
*/
enterPropertyAssignment?: (ctx: PropertyAssignmentContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyAssignment`.
* @param ctx the parse tree
*/
exitPropertyAssignment?: (ctx: PropertyAssignmentContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyValue`.
* @param ctx the parse tree
*/
enterPropertyValue?: (ctx: PropertyValueContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyValue`.
* @param ctx the parse tree
*/
exitPropertyValue?: (ctx: PropertyValueContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyValue`.
* @param ctx the parse tree
*/
enterPropertyValue?: (ctx: PropertyValueContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyValue`.
* @param ctx the parse tree
*/
exitPropertyValue?: (ctx: PropertyValueContext) => void;
/**
* Enter a parse tree produced by `mongoParser.literal`.
* @param ctx the parse tree
*/
enterLiteral?: (ctx: LiteralContext) => void;
/**
* Exit a parse tree produced by `mongoParser.literal`.
* @param ctx the parse tree
*/
exitLiteral?: (ctx: LiteralContext) => void;
/**
* Enter a parse tree produced by `mongoParser.literal`.
* @param ctx the parse tree
*/
enterLiteral?: (ctx: LiteralContext) => void;
/**
* Exit a parse tree produced by `mongoParser.literal`.
* @param ctx the parse tree
*/
exitLiteral?: (ctx: LiteralContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyName`.
* @param ctx the parse tree
*/
enterPropertyName?: (ctx: PropertyNameContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyName`.
* @param ctx the parse tree
*/
exitPropertyName?: (ctx: PropertyNameContext) => void;
/**
* Enter a parse tree produced by `mongoParser.propertyName`.
* @param ctx the parse tree
*/
enterPropertyName?: (ctx: PropertyNameContext) => void;
/**
* Exit a parse tree produced by `mongoParser.propertyName`.
* @param ctx the parse tree
*/
exitPropertyName?: (ctx: PropertyNameContext) => void;
/**
* Enter a parse tree produced by `mongoParser.comment`.
* @param ctx the parse tree
*/
enterComment?: (ctx: CommentContext) => void;
/**
* Exit a parse tree produced by `mongoParser.comment`.
* @param ctx the parse tree
*/
exitComment?: (ctx: CommentContext) => void;
/**
* Enter a parse tree produced by `mongoParser.comment`.
* @param ctx the parse tree
*/
enterComment?: (ctx: CommentContext) => void;
/**
* Exit a parse tree produced by `mongoParser.comment`.
* @param ctx the parse tree
*/
exitComment?: (ctx: CommentContext) => void;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,17 +1,30 @@
// Generated from ./grammar/mongo.g4 by ANTLR 4.6-SNAPSHOT
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ParseTreeVisitor } from 'antlr4ts/tree/ParseTreeVisitor';
import { ArgumentContext, ArgumentsContext, ArrayLiteralContext, CollectionContext, CommandContext, CommandsContext, CommentContext, ElementListContext, EmptyCommandContext, FunctionCallContext, LiteralContext, MongoCommandsContext, ObjectLiteralContext, PropertyAssignmentContext, PropertyNameAndValueListContext, PropertyNameContext, PropertyValueContext } from './mongoParser';
import { type ParseTreeVisitor } from 'antlr4ts/tree/ParseTreeVisitor';
import {
type ArgumentContext,
type ArgumentsContext,
type ArrayLiteralContext,
type CollectionContext,
type CommandContext,
type CommandsContext,
type CommentContext,
type ElementListContext,
type EmptyCommandContext,
type FunctionCallContext,
type LiteralContext,
type MongoCommandsContext,
type ObjectLiteralContext,
type PropertyAssignmentContext,
type PropertyNameAndValueListContext,
type PropertyNameContext,
type PropertyValueContext,
} from './mongoParser';
/**
* This interface defines a complete generic visitor for a parse tree produced
@ -21,123 +34,122 @@ import { ArgumentContext, ArgumentsContext, ArrayLiteralContext, CollectionConte
* operations with no return type.
*/
export interface mongoVisitor<Result> extends ParseTreeVisitor<Result> {
/**
* Visit a parse tree produced by `mongoParser.mongoCommands`.
* @param ctx the parse tree
* @return the visitor result
*/
visitMongoCommands?: (ctx: MongoCommandsContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.mongoCommands`.
* @param ctx the parse tree
* @return the visitor result
*/
visitMongoCommands?: (ctx: MongoCommandsContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.commands`.
* @param ctx the parse tree
* @return the visitor result
*/
visitCommands?: (ctx: CommandsContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.commands`.
* @param ctx the parse tree
* @return the visitor result
*/
visitCommands?: (ctx: CommandsContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.command`.
* @param ctx the parse tree
* @return the visitor result
*/
visitCommand?: (ctx: CommandContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.command`.
* @param ctx the parse tree
* @return the visitor result
*/
visitCommand?: (ctx: CommandContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.emptyCommand`.
* @param ctx the parse tree
* @return the visitor result
*/
visitEmptyCommand?: (ctx: EmptyCommandContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.emptyCommand`.
* @param ctx the parse tree
* @return the visitor result
*/
visitEmptyCommand?: (ctx: EmptyCommandContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.collection`.
* @param ctx the parse tree
* @return the visitor result
*/
visitCollection?: (ctx: CollectionContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.collection`.
* @param ctx the parse tree
* @return the visitor result
*/
visitCollection?: (ctx: CollectionContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.functionCall`.
* @param ctx the parse tree
* @return the visitor result
*/
visitFunctionCall?: (ctx: FunctionCallContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.functionCall`.
* @param ctx the parse tree
* @return the visitor result
*/
visitFunctionCall?: (ctx: FunctionCallContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.arguments`.
* @param ctx the parse tree
* @return the visitor result
*/
visitArguments?: (ctx: ArgumentsContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.arguments`.
* @param ctx the parse tree
* @return the visitor result
*/
visitArguments?: (ctx: ArgumentsContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.argument`.
* @param ctx the parse tree
* @return the visitor result
*/
visitArgument?: (ctx: ArgumentContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.argument`.
* @param ctx the parse tree
* @return the visitor result
*/
visitArgument?: (ctx: ArgumentContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.objectLiteral`.
* @param ctx the parse tree
* @return the visitor result
*/
visitObjectLiteral?: (ctx: ObjectLiteralContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.objectLiteral`.
* @param ctx the parse tree
* @return the visitor result
*/
visitObjectLiteral?: (ctx: ObjectLiteralContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.arrayLiteral`.
* @param ctx the parse tree
* @return the visitor result
*/
visitArrayLiteral?: (ctx: ArrayLiteralContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.arrayLiteral`.
* @param ctx the parse tree
* @return the visitor result
*/
visitArrayLiteral?: (ctx: ArrayLiteralContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.elementList`.
* @param ctx the parse tree
* @return the visitor result
*/
visitElementList?: (ctx: ElementListContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.elementList`.
* @param ctx the parse tree
* @return the visitor result
*/
visitElementList?: (ctx: ElementListContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyNameAndValueList`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyNameAndValueList?: (ctx: PropertyNameAndValueListContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyNameAndValueList`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyNameAndValueList?: (ctx: PropertyNameAndValueListContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyAssignment`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyAssignment?: (ctx: PropertyAssignmentContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyAssignment`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyAssignment?: (ctx: PropertyAssignmentContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyValue`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyValue?: (ctx: PropertyValueContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyValue`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyValue?: (ctx: PropertyValueContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.literal`.
* @param ctx the parse tree
* @return the visitor result
*/
visitLiteral?: (ctx: LiteralContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.literal`.
* @param ctx the parse tree
* @return the visitor result
*/
visitLiteral?: (ctx: LiteralContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyName`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyName?: (ctx: PropertyNameContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.propertyName`.
* @param ctx the parse tree
* @return the visitor result
*/
visitPropertyName?: (ctx: PropertyNameContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.comment`.
* @param ctx the parse tree
* @return the visitor result
*/
visitComment?: (ctx: CommentContext) => Result;
/**
* Visit a parse tree produced by `mongoParser.comment`.
* @param ctx the parse tree
* @return the visitor result
*/
visitComment?: (ctx: CommentContext) => Result;
}

Просмотреть файл

@ -3,15 +3,22 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ParserRuleContext } from 'antlr4ts/ParserRuleContext';
import { ErrorNode } from 'antlr4ts/tree/ErrorNode';
import { ParseTree } from 'antlr4ts/tree/ParseTree';
import { TerminalNode } from 'antlr4ts/tree/TerminalNode';
import { ArgumentContext, ArgumentsContext, CollectionContext, CommandContext, CommandsContext, FunctionCallContext, MongoCommandsContext } from './mongoParser';
import { mongoVisitor } from './mongoVisitor';
import { type ParserRuleContext } from 'antlr4ts/ParserRuleContext';
import { type ErrorNode } from 'antlr4ts/tree/ErrorNode';
import { type ParseTree } from 'antlr4ts/tree/ParseTree';
import { type TerminalNode } from 'antlr4ts/tree/TerminalNode';
import {
type ArgumentContext,
type ArgumentsContext,
type CollectionContext,
type CommandContext,
type CommandsContext,
type FunctionCallContext,
type MongoCommandsContext,
} from './mongoParser';
import { type mongoVisitor } from './mongoVisitor';
export class MongoVisitor<T> implements mongoVisitor<T> {
visitMongoCommands(ctx: MongoCommandsContext): T {
return this.visitChildren(ctx);
}
@ -46,7 +53,7 @@ export class MongoVisitor<T> implements mongoVisitor<T> {
visitChildren(ctx: ParserRuleContext): T {
let result = this.defaultResult(ctx);
const n = ctx.childCount
const n = ctx.childCount;
for (let i = 0; i < n; i++) {
if (!this.shouldVisitNextChild(ctx, result)) {
break;
@ -69,7 +76,7 @@ export class MongoVisitor<T> implements mongoVisitor<T> {
protected defaultResult(_node: ParseTree): T {
// grandfathered-in. Unclear why this is null instead of type T
return <T><unknown>null;
return <T>(<unknown>null);
}
protected aggregateResult(aggregate: T, nextResult: T): T {

Просмотреть файл

@ -4,22 +4,21 @@
*--------------------------------------------------------------------------------------------*/
import { appendExtensionUserAgent } from '@microsoft/vscode-azext-utils';
import * as path from 'path';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
import { LanguageClient, TransportKind, type LanguageClientOptions, type ServerOptions } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
import { ext } from '../extensionVariables';
import { IConnectionParams } from './services/IConnectionParams';
import { type IConnectionParams } from './services/IConnectionParams';
const localize = nls.loadMessageBundle();
export class MongoDBLanguageClient {
public client: LanguageClient;
constructor() {
// The server is implemented in node
const serverModule = ext.ignoreBundle ?
ext.context.asAbsolutePath(path.join('out', 'src', 'mongo', 'languageServer.js')) :
ext.context.asAbsolutePath(path.join('dist', 'mongo-languageServer.bundle.js'));
const serverModule = ext.ignoreBundle
? ext.context.asAbsolutePath(path.join('out', 'src', 'mongo', 'languageServer.js'))
: ext.context.asAbsolutePath(path.join('dist', 'mongo-languageServer.bundle.js'));
// The debug options for the server
const debugOptions = { execArgv: ['--nolazy', '--inspect=6005'] };
@ -27,7 +26,7 @@ export class MongoDBLanguageClient {
// Otherwise the run options are used
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions },
};
// Options to control the language client
@ -35,12 +34,17 @@ export class MongoDBLanguageClient {
// Register the server for mongo javascript documents
documentSelector: [
{ language: 'mongo', scheme: 'file' },
{ language: 'mongo', scheme: 'untitled' }
]
{ language: 'mongo', scheme: 'untitled' },
],
};
// Create the language client and start the client.
this.client = new LanguageClient('mongo', localize('mongo.server.name', 'Mongo Language Server'), serverOptions, clientOptions);
this.client = new LanguageClient(
'mongo',
localize('mongo.server.name', 'Mongo Language Server'),
serverOptions,
clientOptions,
);
const disposable = this.client.start();
// Push the disposable to the context's subscriptions so that the
@ -49,7 +53,11 @@ export class MongoDBLanguageClient {
}
public async connect(connectionString: string, databaseName: string): Promise<void> {
await this.client.sendRequest('connect', <IConnectionParams>{ connectionString: connectionString, databaseName: databaseName, extensionUserAgent: appendExtensionUserAgent() });
await this.client.sendRequest('connect', <IConnectionParams>{
connectionString: connectionString,
databaseName: databaseName,
extensionUserAgent: appendExtensionUserAgent(),
});
}
public async disconnect(): Promise<void> {

Просмотреть файл

@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createConnection, IConnection } from 'vscode-languageserver';
import { createConnection, type IConnection } from 'vscode-languageserver';
import { LanguageService } from './services/languageService';
//

Просмотреть файл

@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { appendExtensionUserAgent, IParsedError, parseError } from "@microsoft/vscode-azext-utils";
import { MongoClient } from "mongodb";
import { ParsedConnectionString } from "../ParsedConnectionString";
import { nonNullValue } from "../utils/nonNull";
import { connectToMongoClient } from "./connectToMongoClient";
import { appendExtensionUserAgent, parseError, type IParsedError } from '@microsoft/vscode-azext-utils';
import { type MongoClient } from 'mongodb';
import { ParsedConnectionString } from '../ParsedConnectionString';
import { nonNullValue } from '../utils/nonNull';
import { connectToMongoClient } from './connectToMongoClient';
// Connection strings follow the following format (https://docs.mongodb.com/manual/reference/connection-string/):
// mongodb[+srv]://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
@ -20,15 +20,18 @@ import { connectToMongoClient } from "./connectToMongoClient";
// mongodb[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]]
// [database]
const parsePrefix = '([a-zA-Z]+:\/\/[^\/]*)';
const parseDatabaseName = '\/?([^/?]+)?';
const parsePrefix = '([a-zA-Z]+://[^/]*)';
const parseDatabaseName = '/?([^/?]+)?';
const mongoConnectionStringRegExp = new RegExp(parsePrefix + parseDatabaseName);
export function getDatabaseNameFromConnectionString(connectionString: string): string | undefined {
try {
const [, , databaseName] = nonNullValue(connectionString.match(mongoConnectionStringRegExp), 'databaseNameMatch');
const [, , databaseName] = nonNullValue(
connectionString.match(mongoConnectionStringRegExp),
'databaseNameMatch',
);
return databaseName;
} catch (error) {
} catch {
// Shouldn't happen, but ignore if does
}
@ -37,15 +40,14 @@ export function getDatabaseNameFromConnectionString(connectionString: string): s
export function addDatabaseToAccountConnectionString(connectionString: string, databaseName: string): string {
try {
return connectionString.replace(mongoConnectionStringRegExp, `$1\/${encodeURIComponent(databaseName)}`);
} catch (error) {
return connectionString.replace(mongoConnectionStringRegExp, `$1/${encodeURIComponent(databaseName)}`);
} catch {
// Shouldn't happen, but ignore if does. Original connection string could be in a format we don't expect, but might already have the db name or might still work without it
return connectionString;
}
}
export async function parseMongoConnectionString(connectionString: string): Promise<ParsedMongoConnectionString> {
let mongoClient: MongoClient;
try {
mongoClient = await connectToMongoClient(connectionString, appendExtensionUserAgent());
@ -62,7 +64,12 @@ export async function parseMongoConnectionString(connectionString: string): Prom
const { host, port } = mongoClient.options.hosts[0];
return new ParsedMongoConnectionString(connectionString, host as string, (port as number).toString(), getDatabaseNameFromConnectionString(connectionString));
return new ParsedMongoConnectionString(
connectionString,
host as string,
(port as number).toString(),
getDatabaseNameFromConnectionString(connectionString),
);
}
export class ParsedMongoConnectionString extends ParsedConnectionString {

Просмотреть файл

@ -3,25 +3,32 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { callWithTelemetryAndErrorHandling, IActionContext, IErrorHandlerContext, registerCommandWithTreeNodeUnwrapping, registerErrorHandler, registerEvent } from "@microsoft/vscode-azext-utils";
import {
callWithTelemetryAndErrorHandling,
registerCommandWithTreeNodeUnwrapping,
registerErrorHandler,
registerEvent,
type IActionContext,
type IErrorHandlerContext,
} from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { ext } from "../extensionVariables";
import { connectMongoDatabase, loadPersistedMongoDB } from "./commands/connectMongoDatabase";
import { createMongoCollection } from "./commands/createMongoCollection";
import { createMongoDatabase } from "./commands/createMongoDatabase";
import { createMongoDocument } from "./commands/createMongoDocument";
import { createMongoSrapbook } from "./commands/createMongoScrapbook";
import { deleteMongoCollection } from "./commands/deleteMongoCollection";
import { deleteMongoDB } from "./commands/deleteMongoDatabase";
import { deleteMongoDocument } from "./commands/deleteMongoDocument";
import { executeAllMongoCommand } from "./commands/executeAllMongoCommand";
import { executeMongoCommand } from "./commands/executeMongoCommand";
import { launchMongoShell } from "./commands/launchMongoShell";
import { openMongoCollection } from "./commands/openMongoCollection";
import { ext } from '../extensionVariables';
import { connectMongoDatabase, loadPersistedMongoDB } from './commands/connectMongoDatabase';
import { createMongoCollection } from './commands/createMongoCollection';
import { createMongoDatabase } from './commands/createMongoDatabase';
import { createMongoDocument } from './commands/createMongoDocument';
import { createMongoSrapbook } from './commands/createMongoScrapbook';
import { deleteMongoCollection } from './commands/deleteMongoCollection';
import { deleteMongoDB } from './commands/deleteMongoDatabase';
import { deleteMongoDocument } from './commands/deleteMongoDocument';
import { executeAllMongoCommand } from './commands/executeAllMongoCommand';
import { executeMongoCommand } from './commands/executeMongoCommand';
import { launchMongoShell } from './commands/launchMongoShell';
import { openMongoCollection } from './commands/openMongoCollection';
import { MongoConnectError } from './connectToMongoClient';
import { MongoDBLanguageClient } from "./languageClient";
import { getAllErrorsFromTextDocument } from "./MongoScrapbook";
import { MongoCodeLensProvider } from "./services/MongoCodeLensProvider";
import { MongoDBLanguageClient } from './languageClient';
import { getAllErrorsFromTextDocument } from './MongoScrapbook';
import { MongoCodeLensProvider } from './services/MongoCodeLensProvider';
let diagnosticsCollection: vscode.DiagnosticCollection;
const mongoLanguageId: string = 'mongo';
@ -30,7 +37,9 @@ export function registerMongoCommands(): void {
ext.mongoLanguageClient = new MongoDBLanguageClient();
ext.mongoCodeLensProvider = new MongoCodeLensProvider();
ext.context.subscriptions.push(vscode.languages.registerCodeLensProvider(mongoLanguageId, ext.mongoCodeLensProvider));
ext.context.subscriptions.push(
vscode.languages.registerCodeLensProvider(mongoLanguageId, ext.mongoCodeLensProvider),
);
diagnosticsCollection = vscode.languages.createDiagnosticCollection('cosmosDB.mongo');
ext.context.subscriptions.push(diagnosticsCollection);
@ -74,14 +83,16 @@ export function registerMongoCommands(): void {
function setUpErrorReporting(): void {
// Update errors immediately in case a scrapbook is already open
void callWithTelemetryAndErrorHandling(
"initialUpdateErrorsInActiveDocument",
async (context: IActionContext) => {
updateErrorsInScrapbook(context, vscode.window.activeTextEditor?.document);
});
void callWithTelemetryAndErrorHandling('initialUpdateErrorsInActiveDocument', async (context: IActionContext) => {
updateErrorsInScrapbook(context, vscode.window.activeTextEditor?.document);
});
// Update errors when document opened/changed
registerEvent('vscode.workspace.onDidOpenTextDocument', vscode.workspace.onDidOpenTextDocument, updateErrorsInScrapbook);
registerEvent(
'vscode.workspace.onDidOpenTextDocument',
vscode.workspace.onDidOpenTextDocument,
updateErrorsInScrapbook,
);
registerEvent(
'vscode.workspace.onDidChangeTextDocument',
vscode.workspace.onDidChangeTextDocument,
@ -90,7 +101,8 @@ function setUpErrorReporting(): void {
context.telemetry.suppressIfSuccessful = true;
updateErrorsInScrapbook(context, event.document);
});
},
);
registerEvent(
'vscode.workspace.onDidCloseTextDocument',
vscode.workspace.onDidCloseTextDocument,
@ -101,7 +113,8 @@ function setUpErrorReporting(): void {
} else {
context.telemetry.suppressIfSuccessful = true;
}
});
},
);
registerErrorHandler((context: IErrorHandlerContext) => {
if (context.error instanceof MongoConnectError) {

Просмотреть файл

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { callWithTelemetryAndErrorHandling, IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from "vscode";
import { getAllCommandsFromTextDocument } from "../MongoScrapbook";
import { callWithTelemetryAndErrorHandling, type IActionContext } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import { getAllCommandsFromTextDocument } from '../MongoScrapbook';
export class MongoCodeLensProvider implements vscode.CodeLensProvider {
private _onDidChangeEmitter: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
@ -22,8 +22,11 @@ export class MongoCodeLensProvider implements vscode.CodeLensProvider {
this._onDidChangeEmitter.fire();
}
public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
return callWithTelemetryAndErrorHandling("mongo.provideCodeLenses", (context: IActionContext) => {
public provideCodeLenses(
document: vscode.TextDocument,
_token: vscode.CancellationToken,
): vscode.ProviderResult<vscode.CodeLens[]> {
return callWithTelemetryAndErrorHandling('mongo.provideCodeLenses', (context: IActionContext) => {
// Suppress except for errors - this can fire on every keystroke
context.telemetry.suppressIfSuccessful = true;
@ -35,24 +38,24 @@ export class MongoCodeLensProvider implements vscode.CodeLensProvider {
// Allow displaying and changing connected database
lenses.push(<vscode.CodeLens>{
command: {
title: !isInitialized ?
'Initializing...' :
isConnected ?
`Connected to ${database}` :
`Connect to a database`,
command: isInitialized && 'cosmosDB.connectMongoDB'
title: !isInitialized
? 'Initializing...'
: isConnected
? `Connected to ${database}`
: `Connect to a database`,
command: isInitialized && 'cosmosDB.connectMongoDB',
},
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0))
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)),
});
if (isConnected) {
// Run all
lenses.push(<vscode.CodeLens>{
command: {
title: "Execute All",
command: 'cosmosDB.executeAllMongoCommands'
title: 'Execute All',
command: 'cosmosDB.executeAllMongoCommands',
},
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0))
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)),
});
const commands = getAllCommandsFromTextDocument(document);
@ -60,11 +63,11 @@ export class MongoCodeLensProvider implements vscode.CodeLensProvider {
// run individual
lenses.push(<vscode.CodeLens>{
command: {
title: "Execute",
title: 'Execute',
command: 'cosmosDB.executeMongoCommand',
arguments: [cmd.range.start]
arguments: [cmd.range.start],
},
range: cmd.range
range: cmd.range,
});
}
}

Просмотреть файл

@ -7,16 +7,16 @@
import { ParserRuleContext } from 'antlr4ts/ParserRuleContext';
import { ErrorNode } from 'antlr4ts/tree/ErrorNode';
import { ParseTree } from 'antlr4ts/tree/ParseTree';
import { type ParseTree } from 'antlr4ts/tree/ParseTree';
import { TerminalNode } from 'antlr4ts/tree/TerminalNode';
import { Db } from 'mongodb';
import { LanguageService as JsonLanguageService } from 'vscode-json-languageservice';
import { CompletionItem, CompletionItemKind, Position, Range } from 'vscode-languageserver';
import { type Db } from 'mongodb';
import { type LanguageService as JsonLanguageService } from 'vscode-json-languageservice';
import { CompletionItemKind, Position, Range, type CompletionItem } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { mongoLexer } from './../grammar/mongoLexer';
import * as mongoParser from './../grammar/mongoParser';
import { MongoVisitor } from './../grammar/visitors';
import { SchemaService } from './schemaService';
import { type SchemaService } from './schemaService';
export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[]>> {
private at: Position;
@ -26,7 +26,7 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
private db: Db,
private offset: number,
private schemaService: SchemaService,
private jsonLanguageService: JsonLanguageService
private jsonLanguageService: JsonLanguageService,
) {
super();
this.at = this.textDocument.positionAt(this.offset);
@ -53,8 +53,13 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
}
public visitCollection(ctx: mongoParser.CollectionContext): Promise<CompletionItem[]> {
return Promise.all([this.createCollectionCompletions(this.createRange(ctx)), this.createDbFunctionCompletions(this.createRange(ctx))])
.then(([collectionCompletions, dbFunctionCompletions]) => [...collectionCompletions, ...dbFunctionCompletions]);
return Promise.all([
this.createCollectionCompletions(this.createRange(ctx)),
this.createDbFunctionCompletions(this.createRange(ctx)),
]).then(([collectionCompletions, dbFunctionCompletions]) => [
...collectionCompletions,
...dbFunctionCompletions,
]);
}
public visitFunctionCall(ctx: mongoParser.FunctionCallContext): Promise<CompletionItem[]> {
@ -81,8 +86,23 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
const functionName = this.getFunctionName(ctx);
const collectionName = this.getCollectionName(ctx);
if (collectionName && functionName) {
if (['find', 'findOne', 'findOneAndDelete', 'findOneAndUpdate', 'findOneAndReplace', 'deleteOne', 'deleteMany', 'remove'].indexOf(functionName) !== -1) {
return this.getArgumentCompletionItems(this.schemaService.queryDocumentUri(collectionName), collectionName, ctx);
if (
[
'find',
'findOne',
'findOneAndDelete',
'findOneAndUpdate',
'findOneAndReplace',
'deleteOne',
'deleteMany',
'remove',
].indexOf(functionName) !== -1
) {
return this.getArgumentCompletionItems(
this.schemaService.queryDocumentUri(collectionName),
collectionName,
ctx,
);
}
}
return ctx.parent!.accept(this);
@ -93,7 +113,11 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
const collectionName = this.getCollectionName(ctx);
if (collectionName && functionName) {
if (['aggregate'].indexOf(functionName) !== -1) {
return this.getArgumentCompletionItems(this.schemaService.aggregateDocumentUri(collectionName), collectionName, ctx);
return this.getArgumentCompletionItems(
this.schemaService.aggregateDocumentUri(collectionName),
collectionName,
ctx,
);
}
}
return ctx.parent!.accept(this);
@ -131,18 +155,31 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
return ctx.parent!.accept(this);
}
private getArgumentCompletionItems(documentUri: string, _collectionName: string, ctx: ParserRuleContext): Thenable<CompletionItem[]> {
private getArgumentCompletionItems(
documentUri: string,
_collectionName: string,
ctx: ParserRuleContext,
): Thenable<CompletionItem[]> {
const text = this.textDocument.getText();
const document = TextDocument.create(documentUri, 'json', 1, text.substring(ctx.start.startIndex, ctx.stop!.stopIndex + 1));
const document = TextDocument.create(
documentUri,
'json',
1,
text.substring(ctx.start.startIndex, ctx.stop!.stopIndex + 1),
);
const positionOffset = this.textDocument.offsetAt(this.at);
const contextOffset = ctx.start.startIndex;
const position = document.positionAt(positionOffset - contextOffset);
return this.jsonLanguageService.doComplete(document, position, this.jsonLanguageService.parseJSONDocument(document))
.then(list => {
return list!.items.map(item => {
return this.jsonLanguageService
.doComplete(document, position, this.jsonLanguageService.parseJSONDocument(document))
.then((list) => {
return list!.items.map((item) => {
const startPositionOffset = document.offsetAt(item.textEdit!.range.start);
const endPositionOffset = document.offsetAt(item.textEdit!.range.end);
item.textEdit!.range = Range.create(this.textDocument.positionAt(startPositionOffset + contextOffset), this.textDocument.positionAt(contextOffset + endPositionOffset));
item.textEdit!.range = Range.create(
this.textDocument.positionAt(startPositionOffset + contextOffset),
this.textDocument.positionAt(contextOffset + endPositionOffset),
);
return item;
});
});
@ -198,8 +235,13 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
const previousNode = this.getPreviousNode(node);
if (previousNode && previousNode instanceof TerminalNode) {
if (previousNode._symbol.type === mongoParser.mongoParser.DB) {
return Promise.all([this.createCollectionCompletions(this.createRangeAfter(node)), this.createDbFunctionCompletions(this.createRangeAfter(node))])
.then(([collectionCompletions, dbFunctionCompletions]) => [...collectionCompletions, ...dbFunctionCompletions]);
return Promise.all([
this.createCollectionCompletions(this.createRangeAfter(node)),
this.createDbFunctionCompletions(this.createRangeAfter(node)),
]).then(([collectionCompletions, dbFunctionCompletions]) => [
...collectionCompletions,
...dbFunctionCompletions,
]);
}
}
if (previousNode instanceof mongoParser.CollectionContext) {
@ -219,7 +261,15 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
}
private getLastTerminalNode(ctx: ParserRuleContext): TerminalNode {
return ctx.children ? <TerminalNode>ctx.children.slice().reverse().filter(node => node instanceof TerminalNode && node.symbol.stopIndex > -1 && node.symbol.stopIndex < this.offset)[0] : null!;
return ctx.children ? <TerminalNode>ctx.children
.slice()
.reverse()
.filter(
(node) =>
node instanceof TerminalNode &&
node.symbol.stopIndex > -1 &&
node.symbol.stopIndex < this.offset,
)[0] : null!;
}
private getPreviousNode(node: ParseTree): ParseTree {
@ -239,10 +289,10 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
return {
textEdit: {
newText: 'db',
range
range,
},
kind: CompletionItemKind.Keyword,
label: 'db'
label: 'db',
};
}
@ -296,23 +346,26 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
this.createFunctionCompletion('setVerboseShell', range),
this.createFunctionCompletion('shotdownServer', range),
this.createFunctionCompletion('stats', range),
this.createFunctionCompletion('version', range)
this.createFunctionCompletion('version', range),
);
}
private createCollectionCompletions(range: Range): Promise<CompletionItem[]> {
if (this.db) {
return <Promise<CompletionItem[]>>this.db.collections().then(collections => {
return collections.map(collection => (<CompletionItem>{
textEdit: {
newText: collection.collectionName,
range
},
label: collection.collectionName,
kind: CompletionItemKind.Property,
filterText: collection.collectionName,
sortText: `1:${collection.collectionName}`
}));
return <Promise<CompletionItem[]>>this.db.collections().then((collections) => {
return collections.map(
(collection) =>
<CompletionItem>{
textEdit: {
newText: collection.collectionName,
range,
},
label: collection.collectionName,
kind: CompletionItemKind.Property,
filterText: collection.collectionName,
sortText: `1:${collection.collectionName}`,
},
);
});
}
return Promise.resolve([]);
@ -368,7 +421,7 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
this.createFunctionCompletion('getWriteConcern', range),
this.createFunctionCompletion('setWriteConcern', range),
this.createFunctionCompletion('unsetWriteConcern', range),
this.createFunctionCompletion('latencyStats', range)
this.createFunctionCompletion('latencyStats', range),
);
}
@ -376,11 +429,11 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
return {
textEdit: {
newText: label,
range
range,
},
kind: CompletionItemKind.Function,
label,
sortText: `2:${label}`
sortText: `2:${label}`,
};
}
@ -435,5 +488,4 @@ export class CompletionItemsVisitor extends MongoVisitor<Promise<CompletionItem[
private thenable(...completionItems: CompletionItem[]): Promise<CompletionItem[]> {
return Promise.resolve(completionItems || []);
}
}

Просмотреть файл

@ -5,17 +5,28 @@
// NOTE: This file may not take a dependencey on vscode or anything that takes a dependency on it (such as @microsoft/vscode-azext-utils)
import { Db } from 'mongodb';
import { getLanguageService, LanguageService as JsonLanguageService, SchemaConfiguration } from 'vscode-json-languageservice';
import { CompletionItem, IConnection, InitializeParams, InitializeResult, TextDocumentPositionParams, TextDocuments, TextDocumentSyncKind } from 'vscode-languageserver';
import { type Db } from 'mongodb';
import {
getLanguageService,
type LanguageService as JsonLanguageService,
type SchemaConfiguration,
} from 'vscode-json-languageservice';
import {
TextDocuments,
TextDocumentSyncKind,
type CompletionItem,
type IConnection,
type InitializeParams,
type InitializeResult,
type TextDocumentPositionParams,
} from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { connectToMongoClient } from '../connectToMongoClient';
import { IConnectionParams } from './IConnectionParams';
import { type IConnectionParams } from './IConnectionParams';
import { MongoScriptDocumentManager } from './mongoScript';
import { SchemaService } from './schemaService';
export class LanguageService {
private textDocuments: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
private readonly mongoDocumentsManager: MongoScriptDocumentManager;
private db: Db;
@ -25,7 +36,6 @@ export class LanguageService {
private schemas: SchemaConfiguration[];
constructor(connection: IConnection) {
this.schemaService = new SchemaService();
this.textDocuments.listen(connection);
@ -35,24 +45,24 @@ export class LanguageService {
return {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Full, // Tell the client that the server works in FULL text document sync mode
completionProvider: { triggerCharacters: ['.'] }
}
completionProvider: { triggerCharacters: ['.'] },
},
};
});
connection.onCompletion(textDocumentPosition => {
connection.onCompletion((textDocumentPosition) => {
return this.provideCompletionItems(textDocumentPosition);
});
connection.onRequest('connect', (connectionParams: IConnectionParams) => {
void connectToMongoClient(connectionParams.connectionString, connectionParams.extensionUserAgent)
.then(account => {
void connectToMongoClient(connectionParams.connectionString, connectionParams.extensionUserAgent).then(
(account) => {
this.db = account.db(connectionParams.databaseName);
void this.schemaService.registerSchemas(this.db)
.then(schemas => {
this.configureSchemas(schemas);
});
});
void this.schemaService.registerSchemas(this.db).then((schemas) => {
this.configureSchemas(schemas);
});
},
);
});
connection.onRequest('disconnect', () => {
@ -64,8 +74,8 @@ export class LanguageService {
});
this.jsonLanguageService = getLanguageService({
schemaRequestService: uri => this.schemaService.resolveSchema(uri),
contributions: []
schemaRequestService: (uri) => this.schemaService.resolveSchema(uri),
contributions: [],
});
this.mongoDocumentsManager = new MongoScriptDocumentManager(this.schemaService, this.jsonLanguageService);
@ -84,7 +94,7 @@ export class LanguageService {
public configureSchemas(schemas: SchemaConfiguration[]): void {
this.jsonLanguageService.configure({
schemas
schemas,
});
}
}

Просмотреть файл

@ -6,25 +6,23 @@ import { ANTLRInputStream as InputStream } from 'antlr4ts/ANTLRInputStream';
import { CommonTokenStream } from 'antlr4ts/CommonTokenStream';
import { Interval } from 'antlr4ts/misc/Interval';
import { ParserRuleContext } from 'antlr4ts/ParserRuleContext';
import { ParseTree } from 'antlr4ts/tree/ParseTree';
import { type ParseTree } from 'antlr4ts/tree/ParseTree';
import { TerminalNode } from 'antlr4ts/tree/TerminalNode';
import { Db } from 'mongodb';
import { LanguageService as JsonLanguageService } from 'vscode-json-languageservice';
import { CompletionItem, Position } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { type Db } from 'mongodb';
import { type LanguageService as JsonLanguageService } from 'vscode-json-languageservice';
import { type CompletionItem, type Position } from 'vscode-languageserver';
import { type TextDocument } from 'vscode-languageserver-textdocument';
import { mongoLexer } from './../grammar/mongoLexer';
import * as mongoParser from './../grammar/mongoParser';
import { MongoVisitor } from './../grammar/visitors';
import { CompletionItemsVisitor } from './completionItemProvider';
import { SchemaService } from './schemaService';
import { type SchemaService } from './schemaService';
export class MongoScriptDocumentManager {
constructor(
private schemaService: SchemaService,
private jsonLanguageService: JsonLanguageService
) {
}
private jsonLanguageService: JsonLanguageService,
) {}
public getDocument(textDocument: TextDocument, db: Db): MongoScriptDocument {
return new MongoScriptDocument(textDocument, db, this.schemaService, this.jsonLanguageService);
@ -32,14 +30,13 @@ export class MongoScriptDocumentManager {
}
export class MongoScriptDocument {
private readonly _lexer: mongoLexer;
constructor(
private textDocument: TextDocument,
private db: Db,
private schemaService: SchemaService,
private jsonLanguageService: JsonLanguageService
private jsonLanguageService: JsonLanguageService,
) {
this._lexer = new mongoLexer(new InputStream(textDocument.getText()));
this._lexer.removeErrorListeners();
@ -52,14 +49,19 @@ export class MongoScriptDocument {
const offset = this.textDocument.offsetAt(position);
const lastNode = new NodeFinder(offset).visit(parser.commands());
if (lastNode) {
return new CompletionItemsVisitor(this.textDocument, this.db, offset, this.schemaService, this.jsonLanguageService).visit(lastNode);
return new CompletionItemsVisitor(
this.textDocument,
this.db,
offset,
this.schemaService,
this.jsonLanguageService,
).visit(lastNode);
}
return Promise.resolve([]);
}
}
class NodeFinder extends MongoVisitor<ParseTree> {
constructor(private offset: number) {
super();
}
@ -86,12 +88,28 @@ class NodeFinder extends MongoVisitor<ParseTree> {
protected aggregateResult(aggregate: ParseTree, nextResult: ParseTree): ParseTree {
if (aggregate && nextResult) {
const aggregateStart = aggregate instanceof ParserRuleContext ? aggregate.start.startIndex : (<TerminalNode>aggregate).symbol.startIndex;
const aggregateStop = aggregate instanceof ParserRuleContext ? aggregate.start.stopIndex : (<TerminalNode>aggregate).symbol.stopIndex;
const nextResultStart = nextResult instanceof ParserRuleContext ? nextResult.start.startIndex : (<TerminalNode>nextResult).symbol.startIndex;
const nextResultStop = nextResult instanceof ParserRuleContext ? nextResult.start.stopIndex : (<TerminalNode>nextResult).symbol.stopIndex;
const aggregateStart =
aggregate instanceof ParserRuleContext
? aggregate.start.startIndex
: (<TerminalNode>aggregate).symbol.startIndex;
const aggregateStop =
aggregate instanceof ParserRuleContext
? aggregate.start.stopIndex
: (<TerminalNode>aggregate).symbol.stopIndex;
const nextResultStart =
nextResult instanceof ParserRuleContext
? nextResult.start.startIndex
: (<TerminalNode>nextResult).symbol.startIndex;
const nextResultStop =
nextResult instanceof ParserRuleContext
? nextResult.start.stopIndex
: (<TerminalNode>nextResult).symbol.stopIndex;
if (Interval.of(aggregateStart, aggregateStop).properlyContains(Interval.of(nextResultStart, nextResultStop))) {
if (
Interval.of(aggregateStart, aggregateStop).properlyContains(
Interval.of(nextResultStart, nextResultStop),
)
) {
return aggregate;
}
return nextResult;

Просмотреть файл

@ -5,33 +5,36 @@
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unsafe-member-access */
import { Db, FindCursor } from 'mongodb';
import { SchemaConfiguration } from 'vscode-json-languageservice';
import { type Db, type FindCursor } from 'mongodb';
import { type SchemaConfiguration } from 'vscode-json-languageservice';
// eslint-disable-next-line import/no-internal-modules
import { JSONSchema } from 'vscode-json-languageservice/lib/umd/jsonSchema';
import { type JSONSchema } from 'vscode-json-languageservice/lib/umd/jsonSchema';
export class SchemaService {
private _db: Db;
private _schemasCache: Map<string, string> = new Map<string, string>();
public registerSchemas(db: Db): Thenable<SchemaConfiguration[]> {
this._db = db;
this._schemasCache.clear();
return this._db.collections()
.then(collections => {
const schemas: SchemaConfiguration[] = [];
for (const collection of collections) {
schemas.push(...[{
uri: this.queryCollectionSchema(collection.collectionName),
fileMatch: [this.queryDocumentUri(collection.collectionName)]
}, {
uri: this.aggregateCollectionSchema(collection.collectionName),
fileMatch: [this.aggregateDocumentUri(collection.collectionName)]
}]);
}
return schemas;
});
return this._db.collections().then((collections) => {
const schemas: SchemaConfiguration[] = [];
for (const collection of collections) {
schemas.push(
...[
{
uri: this.queryCollectionSchema(collection.collectionName),
fileMatch: [this.queryDocumentUri(collection.collectionName)],
},
{
uri: this.aggregateCollectionSchema(collection.collectionName),
fileMatch: [this.aggregateDocumentUri(collection.collectionName)],
},
],
);
}
return schemas;
});
}
public queryCollectionSchema(collectionName: string): string {
@ -56,18 +59,21 @@ export class SchemaService {
return Promise.resolve(schema);
}
if (uri.startsWith('mongo://query/')) {
return this._resolveQueryCollectionSchema(uri.substring('mongo://query/'.length, uri.length - '.schema'.length), uri)
.then(sch => {
this._schemasCache.set(uri, sch);
return sch;
});
return this._resolveQueryCollectionSchema(
uri.substring('mongo://query/'.length, uri.length - '.schema'.length),
uri,
).then((sch) => {
this._schemasCache.set(uri, sch);
return sch;
});
}
if (uri.startsWith('mongo://aggregate/')) {
return this._resolveAggregateCollectionSchema(uri.substring('mongo://aggregate/'.length, uri.length - '.schema'.length))
.then(sch => {
this._schemasCache.set(uri, sch);
return sch;
});
return this._resolveAggregateCollectionSchema(
uri.substring('mongo://aggregate/'.length, uri.length - '.schema'.length),
).then((sch) => {
this._schemasCache.set(uri, sch);
return sch;
});
}
return Promise.resolve('');
}
@ -79,7 +85,7 @@ export class SchemaService {
this.readNext([], cursor, 10, (result) => {
const schema: JSONSchema = {
type: 'object',
properties: {}
properties: {},
};
for (const document of result) {
this.setSchemaForDocument(null!, document, schema);
@ -98,7 +104,7 @@ export class SchemaService {
this.readNext([], cursor, 10, (_result) => {
const schema: JSONSchema = {
type: 'array',
items: this.getAggregateStagePropertiesSchema(this.queryCollectionSchema(collectionName))
items: this.getAggregateStagePropertiesSchema(this.queryCollectionSchema(collectionName)),
};
resolve(JSON.stringify(schema));
});
@ -106,14 +112,14 @@ export class SchemaService {
}
private getMongoDocumentType(document: any): string {
return Array.isArray(document) ? 'array' : (document === null ? 'null' : typeof document);
return Array.isArray(document) ? 'array' : document === null ? 'null' : typeof document;
}
private setSchemaForDocument(parent: string, document: any, schema: JSONSchema): void {
if (this.getMongoDocumentType(document) === 'object') {
//eslint-disable-next-line @typescript-eslint/no-unsafe-argument
for (const property of Object.keys(document)) {
if (!parent &&
['_id'].indexOf(property) !== -1) {
if (!parent && ['_id'].indexOf(property) !== -1) {
continue;
}
this.setSchemaForDocumentProperty(parent, property, document, schema);
@ -128,7 +134,7 @@ export class SchemaService {
const type = this.getMongoDocumentType(value);
const propertySchema: JSONSchema = {
type: [type, 'object']
type: [type, 'object'],
};
this.setOperatorProperties(type, propertySchema);
schema.properties![scopedProperty] = propertySchema;
@ -151,57 +157,62 @@ export class SchemaService {
properties: {
$search: <JSONSchema>{
type: 'string',
description: 'A string of terms that MongoDB parses and uses to query the text index. MongoDB performs a logical OR search of the terms unless specified as a phrase'
description:
'A string of terms that MongoDB parses and uses to query the text index. MongoDB performs a logical OR search of the terms unless specified as a phrase',
},
$language: {
type: 'string',
description: 'Optional. The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer. If not specified, the search uses the default language of the index.\nIf you specify a language value of "none", then the text search uses simple tokenization with no list of stop words and no stemming'
description:
'Optional. The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer. If not specified, the search uses the default language of the index.\nIf you specify a language value of "none", then the text search uses simple tokenization with no list of stop words and no stemming',
},
$caseSensitive: {
type: 'boolean',
description: 'Optional. A boolean flag to enable or disable case sensitive search. Defaults to false; i.e. the search defers to the case insensitivity of the text index'
description:
'Optional. A boolean flag to enable or disable case sensitive search. Defaults to false; i.e. the search defers to the case insensitivity of the text index',
},
$diacriticSensitive: {
type: 'boolean',
description: `Optional. A boolean flag to enable or disable diacritic sensitive search against version 3 text indexes.Defaults to false; i.e.the search defers to the diacritic insensitivity of the text index
Text searches against earlier versions of the text index are inherently diacritic sensitive and cannot be diacritic insensitive. As such, the $diacriticSensitive option has no effect with earlier versions of the text index`
}
Text searches against earlier versions of the text index are inherently diacritic sensitive and cannot be diacritic insensitive. As such, the $diacriticSensitive option has no effect with earlier versions of the text index`,
},
},
required: ['$search']
required: ['$search'],
};
schema.properties!.$where = {
type: 'string',
description: `Matches documents that satisfy a JavaScript expression.
Use the $where operator to pass either a string containing a JavaScript expression or a full JavaScript function to the query system`
Use the $where operator to pass either a string containing a JavaScript expression or a full JavaScript function to the query system`,
};
schema.properties!.$comment = {
type: 'string',
description: 'Adds a comment to a query predicate'
description: 'Adds a comment to a query predicate',
};
}
private setLogicalOperatorProperties(schema: JSONSchema, schemaUri: string): void {
schema.properties!.$or = {
type: 'array',
description: 'Joins query clauses with a logical OR returns all documents that match the conditions of either clause',
description:
'Joins query clauses with a logical OR returns all documents that match the conditions of either clause',
items: <JSONSchema>{
$ref: schemaUri
}
$ref: schemaUri,
},
};
schema.properties!.$and = {
type: 'array',
description: 'Joins query clauses with a logical AND returns all documents that match the conditions of both clauses',
description:
'Joins query clauses with a logical AND returns all documents that match the conditions of both clauses',
items: <JSONSchema>{
$ref: schemaUri
}
$ref: schemaUri,
},
};
schema.properties!.$nor = {
type: 'array',
description: 'Joins query clauses with a logical NOR returns all documents that fail to match both clauses',
items: <JSONSchema>{
$ref: schemaUri
}
$ref: schemaUri,
},
};
}
@ -212,62 +223,63 @@ Use the $where operator to pass either a string containing a JavaScript expressi
const expressionSchema = {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
properties: <any>{}
properties: <any>{},
};
// Comparison operators
expressionSchema.properties.$eq = {
type: type,
description: 'Matches values that are equal to a specified value'
description: 'Matches values that are equal to a specified value',
};
expressionSchema.properties.$gt = {
type: type,
description: 'Matches values that are greater than a specified value'
description: 'Matches values that are greater than a specified value',
};
expressionSchema.properties.$gte = {
type: type,
description: 'Matches values that are greater than or equal to a specified value'
description: 'Matches values that are greater than or equal to a specified value',
};
expressionSchema.properties.$lt = {
type: type,
description: 'Matches values that are less than a specified value'
description: 'Matches values that are less than a specified value',
};
expressionSchema.properties.$lte = {
type: type,
description: 'Matches values that are less than or equal to a specified value'
description: 'Matches values that are less than or equal to a specified value',
};
expressionSchema.properties.$ne = {
type: type,
description: 'Matches all values that are not equal to a specified value'
description: 'Matches all values that are not equal to a specified value',
};
expressionSchema.properties.$in = {
type: 'array',
description: 'Matches any of the values specified in an array'
description: 'Matches any of the values specified in an array',
};
expressionSchema.properties.$nin = {
type: 'array',
description: 'Matches none of the values specified in an array'
description: 'Matches none of the values specified in an array',
};
// Element operators
expressionSchema.properties.$exists = {
type: 'boolean',
description: 'Matches documents that have the specified field'
description: 'Matches documents that have the specified field',
};
expressionSchema.properties.$type = {
type: 'string',
description: 'Selects documents if a field is of the specified type'
description: 'Selects documents if a field is of the specified type',
};
// Evaluation operators
expressionSchema.properties.$mod = {
type: 'array',
description: 'Performs a modulo operation on the value of a field and selects documents with a specified result',
description:
'Performs a modulo operation on the value of a field and selects documents with a specified result',
maxItems: 2,
default: [2, 0]
default: [2, 0],
};
expressionSchema.properties.$regex = {
type: 'string',
description: 'Selects documents where values match a specified regular expression'
description: 'Selects documents where values match a specified regular expression',
};
// Geospatial
@ -276,117 +288,124 @@ Use the $where operator to pass either a string containing a JavaScript expressi
properties: {
type: {
type: 'string',
default: 'GeoJSON object type'
default: 'GeoJSON object type',
},
coordinates: {
type: 'array'
type: 'array',
},
crs: {
type: 'object',
properties: {
type: {
type: 'string'
type: 'string',
},
properties: {
type: 'object'
}
}
}
}
type: 'object',
},
},
},
},
};
expressionSchema.properties.$geoWithin = {
type: 'object',
description: 'Selects geometries within a bounding GeoJSON geometry. The 2dsphere and 2d indexes support $geoWithin',
description:
'Selects geometries within a bounding GeoJSON geometry. The 2dsphere and 2d indexes support $geoWithin',
properties: {
$geometry: geometryPropertySchema,
$box: {
type: 'array'
type: 'array',
},
$polygon: {
type: 'array'
type: 'array',
},
$center: {
type: 'array'
type: 'array',
},
$centerSphere: {
type: 'array'
}
}
type: 'array',
},
},
};
expressionSchema.properties.$geoIntersects = {
type: 'object',
description: 'Selects geometries that intersect with a GeoJSON geometry. The 2dsphere index supports $geoIntersects',
description:
'Selects geometries that intersect with a GeoJSON geometry. The 2dsphere index supports $geoIntersects',
properties: {
$geometry: geometryPropertySchema
}
$geometry: geometryPropertySchema,
},
};
expressionSchema.properties.$near = {
type: 'object',
description: 'Returns geospatial objects in proximity to a point. Requires a geospatial index. The 2dsphere and 2d indexes support $near',
description:
'Returns geospatial objects in proximity to a point. Requires a geospatial index. The 2dsphere and 2d indexes support $near',
properties: {
$geometry: geometryPropertySchema,
$maxDistance: {
type: 'number'
type: 'number',
},
$minDistance: {
type: 'number'
}
}
type: 'number',
},
},
};
expressionSchema.properties.$nearSphere = {
type: 'object',
description: 'Returns geospatial objects in proximity to a point. Requires a geospatial index. The 2dsphere and 2d indexes support $near',
description:
'Returns geospatial objects in proximity to a point. Requires a geospatial index. The 2dsphere and 2d indexes support $near',
properties: {
$geometry: geometryPropertySchema,
$maxDistance: {
type: 'number'
type: 'number',
},
$minDistance: {
type: 'number'
}
}
type: 'number',
},
},
};
// Array operatos
if (type === 'array') {
expressionSchema.properties.$all = {
type: 'array',
description: 'Matches arrays that contain all elements specified in the query'
description: 'Matches arrays that contain all elements specified in the query',
};
expressionSchema.properties.$size = {
type: 'number',
description: 'Selects documents if the array field is a specified size'
description: 'Selects documents if the array field is a specified size',
};
}
// Bit operators
expressionSchema.properties.$bitsAllSet = {
type: 'array',
description: 'Matches numeric or binary values in which a set of bit positions all have a value of 1'
description: 'Matches numeric or binary values in which a set of bit positions all have a value of 1',
};
expressionSchema.properties.$bitsAnySet = {
type: 'array',
description: 'Matches numeric or binary values in which any bit from a set of bit positions has a value of 1'
description:
'Matches numeric or binary values in which any bit from a set of bit positions has a value of 1',
};
expressionSchema.properties.$bitsAllClear = {
type: 'array',
description: 'Matches numeric or binary values in which a set of bit positions all have a value of 0'
description: 'Matches numeric or binary values in which a set of bit positions all have a value of 0',
};
expressionSchema.properties.$bitsAnyClear = {
type: 'array',
description: 'Matches numeric or binary values in which any bit from a set of bit positions has a value of 0'
description:
'Matches numeric or binary values in which any bit from a set of bit positions has a value of 0',
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
schema.properties = { ...expressionSchema.properties };
schema.properties!.$not = {
type: 'object',
description: 'Inverts the effect of a query expression and returns documents that do not match the query expression',
description:
'Inverts the effect of a query expression and returns documents that do not match the query expression',
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
properties: { ...expressionSchema.properties }
properties: { ...expressionSchema.properties },
};
schema.properties!.$elemMatch = {
type: 'object'
type: 'object',
};
}
@ -397,232 +416,253 @@ Use the $where operator to pass either a string containing a JavaScript expressi
properties: {
$collStats: {
type: 'object',
description: 'Returns statistics regarding a collection or view'
}
}
description: 'Returns statistics regarding a collection or view',
},
},
});
schemas.push({
type: 'object',
properties: {
$project: {
type: 'object',
description: 'Reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document'
}
}
description:
'Reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document',
},
},
});
schemas.push({
type: 'object',
properties: {
$match: {
type: 'object',
description: 'Filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage. $match uses standard MongoDB queries. For each input document, outputs either one document (a match) or zero documents (no match)',
$ref: querySchemaUri
}
}
description:
'Filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage. $match uses standard MongoDB queries. For each input document, outputs either one document (a match) or zero documents (no match)',
$ref: querySchemaUri,
},
},
});
schemas.push({
type: 'object',
properties: {
$redact: {
type: 'object',
description: 'Reshapes each document in the stream by restricting the content for each document based on information stored in the documents themselves. Incorporates the functionality of $project and $match. Can be used to implement field level redaction. For each input document, outputs either one or zero documents'
}
}
description:
'Reshapes each document in the stream by restricting the content for each document based on information stored in the documents themselves. Incorporates the functionality of $project and $match. Can be used to implement field level redaction. For each input document, outputs either one or zero documents',
},
},
});
schemas.push({
type: 'object',
properties: {
$limit: {
type: 'object',
description: 'Passes the first n documents unmodified to the pipeline where n is the specified limit. For each input document, outputs either one document (for the first n documents) or zero documents (after the first n documents).'
}
}
description:
'Passes the first n documents unmodified to the pipeline where n is the specified limit. For each input document, outputs either one document (for the first n documents) or zero documents (after the first n documents).',
},
},
});
schemas.push({
type: 'object',
properties: {
$skip: {
type: 'object',
description: 'Skips the first n documents where n is the specified skip number and passes the remaining documents unmodified to the pipeline. For each input document, outputs either zero documents (for the first n documents) or one document (if after the first n documents)'
}
}
description:
'Skips the first n documents where n is the specified skip number and passes the remaining documents unmodified to the pipeline. For each input document, outputs either zero documents (for the first n documents) or one document (if after the first n documents)',
},
},
});
schemas.push({
type: 'object',
properties: {
$unwind: {
type: 'object',
description: 'Deconstructs an array field from the input documents to output a document for each element. Each output document replaces the array with an element value. For each input document, outputs n documents where n is the number of array elements and can be zero for an empty array'
}
}
description:
'Deconstructs an array field from the input documents to output a document for each element. Each output document replaces the array with an element value. For each input document, outputs n documents where n is the number of array elements and can be zero for an empty array',
},
},
});
schemas.push({
type: 'object',
properties: {
$group: {
type: 'object',
description: 'Groups input documents by a specified identifier expression and applies the accumulator expression(s), if specified, to each group. Consumes all input documents and outputs one document per each distinct group. The output documents only contain the identifier field and, if specified, accumulated fields.',
description:
'Groups input documents by a specified identifier expression and applies the accumulator expression(s), if specified, to each group. Consumes all input documents and outputs one document per each distinct group. The output documents only contain the identifier field and, if specified, accumulated fields.',
properties: {
_id: {
type: ['string', 'object']
}
type: ['string', 'object'],
},
},
additionalProperties: {
type: 'object'
}
}
}
type: 'object',
},
},
},
});
schemas.push({
type: 'object',
properties: {
$sample: {
type: 'object',
description: 'Randomly selects the specified number of documents from its input'
}
}
description: 'Randomly selects the specified number of documents from its input',
},
},
});
schemas.push({
type: 'object',
properties: {
$sort: {
type: 'object',
description: 'Reorders the document stream by a specified sort key. Only the order changes; the documents remain unmodified. For each input document, outputs one document.'
}
}
description:
'Reorders the document stream by a specified sort key. Only the order changes; the documents remain unmodified. For each input document, outputs one document.',
},
},
});
schemas.push({
type: 'object',
properties: {
$geoNear: {
type: 'object',
description: 'Returns an ordered stream of documents based on the proximity to a geospatial point. Incorporates the functionality of $match, $sort, and $limit for geospatial data. The output documents include an additional distance field and can include a location identifier field.'
}
}
description:
'Returns an ordered stream of documents based on the proximity to a geospatial point. Incorporates the functionality of $match, $sort, and $limit for geospatial data. The output documents include an additional distance field and can include a location identifier field.',
},
},
});
schemas.push({
type: 'object',
properties: {
$lookup: {
type: 'object',
description: 'Performs a left outer join to another collection in the same database to filter in documents from the “joined” collection for processing'
}
}
description:
'Performs a left outer join to another collection in the same database to filter in documents from the “joined” collection for processing',
},
},
});
schemas.push({
type: 'object',
properties: {
$out: {
type: 'object',
description: 'Writes the resulting documents of the aggregation pipeline to a collection. To use the $out stage, it must be the last stage in the pipeline'
}
}
description:
'Writes the resulting documents of the aggregation pipeline to a collection. To use the $out stage, it must be the last stage in the pipeline',
},
},
});
schemas.push({
type: 'object',
properties: {
$indexStats: {
type: 'object',
description: 'Returns statistics regarding the use of each index for the collection'
}
}
description: 'Returns statistics regarding the use of each index for the collection',
},
},
});
schemas.push({
type: 'object',
properties: {
$facet: {
type: 'object',
description: 'Processes multiple aggregation pipelines within a single stage on the same set of input documents. Enables the creation of multi-faceted aggregations capable of characterizing data across multiple dimensions, or facets, in a single stage'
}
}
description:
'Processes multiple aggregation pipelines within a single stage on the same set of input documents. Enables the creation of multi-faceted aggregations capable of characterizing data across multiple dimensions, or facets, in a single stage',
},
},
});
schemas.push({
type: 'object',
properties: {
$bucket: {
type: 'object',
description: 'Categorizes incoming documents into groups, called buckets, based on a specified expression and bucket boundaries'
}
}
description:
'Categorizes incoming documents into groups, called buckets, based on a specified expression and bucket boundaries',
},
},
});
schemas.push({
type: 'object',
properties: {
$bucketAuto: {
type: 'object',
description: 'Categorizes incoming documents into a specific number of groups, called buckets, based on a specified expression. Bucket boundaries are automatically determined in an attempt to evenly distribute the documents into the specified number of buckets'
}
}
description:
'Categorizes incoming documents into a specific number of groups, called buckets, based on a specified expression. Bucket boundaries are automatically determined in an attempt to evenly distribute the documents into the specified number of buckets',
},
},
});
schemas.push({
type: 'object',
properties: {
$sortByCount: {
type: 'object',
description: 'Groups incoming documents based on the value of a specified expression, then computes the count of documents in each distinct group'
}
}
description:
'Groups incoming documents based on the value of a specified expression, then computes the count of documents in each distinct group',
},
},
});
schemas.push({
type: 'object',
properties: {
$addFields: {
type: 'object',
description: 'Adds new fields to documents. Outputs documents that contain all existing fields from the input documents and newly added fields'
}
}
description:
'Adds new fields to documents. Outputs documents that contain all existing fields from the input documents and newly added fields',
},
},
});
schemas.push({
type: 'object',
properties: {
$replaceRoot: {
type: 'object',
description: 'Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level'
}
}
description:
'Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level',
},
},
});
schemas.push({
type: 'object',
properties: {
$count: {
type: 'object',
description: 'Returns a count of the number of documents at this stage of the aggregation pipeline'
}
}
description: 'Returns a count of the number of documents at this stage of the aggregation pipeline',
},
},
});
schemas.push({
type: 'object',
properties: {
$graphLookup: {
type: 'object',
description: 'Performs a recursive search on a collection. To each output document, adds a new array field that contains the traversal results of the recursive search for that document'
}
}
description:
'Performs a recursive search on a collection. To each output document, adds a new array field that contains the traversal results of the recursive search for that document',
},
},
});
return {
type: 'object',
oneOf: schemas
oneOf: schemas,
};
}
private readNext(result: any[], cursor: FindCursor<any>, batchSize: number, callback: (result: any[]) => void): void {
private readNext(
result: any[],
cursor: FindCursor<any>,
batchSize: number,
callback: (result: any[]) => void,
): void {
if (result.length === batchSize) {
callback(result);
return;
}
void cursor.hasNext().then(hasNext => {
void cursor.hasNext().then((hasNext) => {
if (!hasNext) {
callback(result);
return;
}
void cursor.next().then(doc => {
void cursor.next().then((doc) => {
result.push(doc);
this.readNext(result, cursor, batchSize, callback);
});
});
}
}

Просмотреть файл

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ext } from "../extensionVariables";
import { MongoDatabaseTreeItem } from "./tree/MongoDatabaseTreeItem";
import { ext } from '../extensionVariables';
import { type MongoDatabaseTreeItem } from './tree/MongoDatabaseTreeItem';
export function setConnectedNode(node: MongoDatabaseTreeItem | undefined): void {
ext.connectedMongoDB = node;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше