diff --git a/package.json b/package.json index 1646663b..87031c68 100644 --- a/package.json +++ b/package.json @@ -608,7 +608,7 @@ "command": "vscode-couchbase.runQuery", "key": "ctrl+shift+e", "mac": "cmd+shift+e", - "when": "editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/ " + "when": "(editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/) && !isKVCluster" } ], "menus": { @@ -616,7 +616,7 @@ { "command": "vscode-couchbase.runQuery", "category": "Couchbase", - "when": "editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/ ", + "when": "(editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/) && !isKVCluster", "group": "navigation@1" }, { @@ -624,21 +624,21 @@ "command": "vscode-couchbase.queryContext", "category": "Couchbase", "group": "navigation@2", - "when": "editorLangId == sqlpp || resourceFilename =~ /\\.sqlpp$/" + "when": "(editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/) && !isKVCluster" }, { "title": "Show Favorite Queries", "command": "vscode-couchbase.showFavoriteQueries", "category": "Couchbase", "group": "navigation@3", - "when": "editorLangId == sqlpp || resourceFilename =~ /\\.sqlpp$/" + "when": "(editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/) && !isKVCluster" }, { "title": "Mark Favorite Query", "command": "vscode-couchbase.markFavoriteQuery", "category": "Couchbase", "group": "navigation@4", - "when": "editorLangId == sqlpp || resourceFilename =~ /\\.sqlpp$/" + "when": "(editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/) && !isKVCluster" }, { "title": "Refresh Cluster Overview", @@ -651,7 +651,7 @@ "editor/context": [ { "command": "vscode-couchbase.runQuery", - "when": "editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/ ", + "when": "(editorLangId == sqlpp || resourceFilename =~ /.sqlpp$/) && !isKVCluster", "group": "navigation@1" } ], @@ -802,22 +802,18 @@ }, { "command": "vscode-couchbase.openQueryNotebook", - "when": "view == couchbase && viewItem == active_connection", + "when": "view == couchbase && viewItem == active_connection && !isKVCluster", "group": "workbench@2" }, { "command": "vscode-couchbase.openQueryWorkbench", - "when": "view == couchbase && viewItem == active_connection", + "when": "view == couchbase && viewItem == active_connection && !isKVCluster", "group": "workbench@1" }, { "submenu": "vscode-couchbase.toolsMenu", "when": "view == couchbase && viewItem == active_connection" }, - { - "submenu": "vscode-couchbase.toolsMenu", - "when": "view == couchbase && viewItem == active_connection" - }, { "command": "vscode-couchbase.useClusterConnection", "when": "view == couchbase && viewItem == connection" @@ -834,7 +830,7 @@ }, { "command": "vscode-couchbase.filterDocuments", - "when": "view == couchbase && (viewItem == collection || viewItem == default_collection)", + "when": "view == couchbase && (viewItem == collection || viewItem == default_collection) && !isKVCluster", "group": "secondary" }, { @@ -898,12 +894,12 @@ }, { "command": "vscode-couchbase.deleteQueryHistoryItem", - "when": "view == query-history", + "when": "view == query-history && !isKVCluster", "group": "inline" }, { "command": "vscode-couchbase.copyQueryHistoryItem", - "when": "view == query-history", + "when": "view == query-history && !isKVCluster", "group": "inline" } ], diff --git a/src/commands/documents/getDocumentMetaData.ts b/src/commands/documents/getDocumentMetaData.ts index 6200fdaa..2df33531 100644 --- a/src/commands/documents/getDocumentMetaData.ts +++ b/src/commands/documents/getDocumentMetaData.ts @@ -21,6 +21,8 @@ import { logger } from "../../logger/logger"; import { getDocumentMetaDataView } from "../../webViews/metaData.webview"; import DocumentNode from "../../model/DocumentNode"; import { Constants } from "../../util/constants"; +import { hasQueryService } from "../../util/common"; +import { CouchbaseRestAPI } from "../../util/apis/CouchbaseRestAPI"; export const getDocumentMetaData = async (node: DocumentNode, context: vscode.ExtensionContext) => { const connection = Memory.state.get(Constants.ACTIVE_CONNECTION); @@ -28,9 +30,21 @@ export const getDocumentMetaData = async (node: DocumentNode, context: vscode.Ex return; } try { - const result = await connection.cluster?.query( - `SELECT META(b).* FROM \`${node.bucketName}\`.\`${node.scopeName}\`.\`${node.collectionName}\` b WHERE META(b).id = \"${node.documentName}\"` - ); + let result; + if (hasQueryService(connection.services)) { + try { + result = await connection.cluster?.query( + `SELECT META(b).* FROM \`${node.bucketName}\`.\`${node.scopeName}\`.\`${node.collectionName}\` b WHERE META(b).id = \"${node.documentName}\"` + ); + result = result?.rows; + } catch { + const couchbaseRestAPI = new CouchbaseRestAPI(connection); + result = await couchbaseRestAPI.getKVDocumentMetaData(node.bucketName, node.scopeName, node.collectionName, node.documentName); + } + } else { + const couchbaseRestAPI = new CouchbaseRestAPI(connection); + result = await couchbaseRestAPI.getKVDocumentMetaData(node.bucketName, node.scopeName, node.collectionName, node.documentName); + } const viewType = `${connection.url}.${node.bucketName}.${node.scopeName}.${node.collectionName}.${node.documentName}`; let currentPanel: vscode.WebviewPanel | undefined = vscode.window.createWebviewPanel( viewType, @@ -44,8 +58,7 @@ export const getDocumentMetaData = async (node: DocumentNode, context: vscode.Ex light: vscode.Uri.file(path.join(__filename, "..", "..", "images", "light", "document.svg")), dark: vscode.Uri.file(path.join(__filename, "..", "..", "images", "dark", "document.svg")) }; - currentPanel.webview.html = getDocumentMetaDataView(result?.rows); - + currentPanel.webview.html = getDocumentMetaDataView(result); currentPanel.onDidDispose( () => { currentPanel = undefined; diff --git a/src/commands/documents/openDocument.ts b/src/commands/documents/openDocument.ts index 49e937c5..ce088495 100644 --- a/src/commands/documents/openDocument.ts +++ b/src/commands/documents/openDocument.ts @@ -29,17 +29,27 @@ export const openDocument = async (documentNode: DocumentNode, clusterConnection .scope(documentNode.scopeName) .collection(documentNode.collectionName) .get(documentNode.documentName); + + if (result?.content instanceof Uint8Array || result?.content instanceof Uint16Array || result?.content instanceof Uint32Array) { + vscode.window.showInformationMessage("Unable to open document: It is not a valid JSON document", { modal: true }); + return false; + } const uri = vscode.Uri.parse( `couchbase:/${documentNode.bucketName}/${documentNode.scopeName}/Collections/${documentNode.collectionName}/${documentNode.documentName}.json` ); if (result) { uriToCasMap.set(uri.toString(), result.cas.toString()); } - memFs.writeFile( - uri, - Buffer.from(JSON.stringify(result?.content, null, 2)), - { create: true, overwrite: true } - ); + try { + memFs.writeFile( + uri, + Buffer.from(JSON.stringify(result?.content, null, 2)), + { create: true, overwrite: true } + ); + } catch (error) { + vscode.window.showInformationMessage("Unable to open document: It is not a valid JSON document", { modal: true }); + return false; + } const document = await vscode.workspace.openTextDocument(uri); await vscode.window.showTextDocument(document, { preview: false }); return true; diff --git a/src/model/CollectionNode.ts b/src/model/CollectionNode.ts index 95b4be4f..16cf102d 100644 --- a/src/model/CollectionNode.ts +++ b/src/model/CollectionNode.ts @@ -19,7 +19,7 @@ import { IConnection } from "../types/IConnection"; import { INode } from "../types/INode"; import DocumentNode from "./DocumentNode"; import { PagerNode } from "./PagerNode"; -import { abbreviateCount } from "../util/common"; +import { abbreviateCount, hasQueryService } from "../util/common"; import { ParsingFailureError, PlanningFailureError } from "couchbase"; import InformationNode from "./InformationNode"; import { Memory } from "../util/util"; @@ -29,6 +29,7 @@ import { getActiveConnection } from "../util/connections"; import { Commands } from "../commands/extensionCommands/commands"; import { IndexDirectory } from "./IndexDirectory"; import { logger } from "../logger/logger"; +import { CouchbaseRestAPI } from "../util/apis/CouchbaseRestAPI"; export default class CollectionNode implements INode { constructor( @@ -89,18 +90,24 @@ export default class CollectionNode implements INode { [], vscode.TreeItemCollapsibleState.None ); - - documentList.push(indexItem); - documentList.push( - new SchemaDirectory( - this, - this.connection, - "Schema", - this.bucketName, - this.scopeName, - this.collectionName - ) - ); + const connection = getActiveConnection(); + if (!connection) { + return []; + } + const isQueryServicesEnable = hasQueryService(connection?.services); + if (isQueryServicesEnable) { + documentList.push(indexItem); + documentList.push( + new SchemaDirectory( + this, + this.connection, + "Schema", + this.bucketName, + this.scopeName, + this.collectionName + ) + ); + } // TODO: default limit could be managed as user settings / preference let result; // A primary index is required for database querying. If one is present, a result will be obtained. @@ -112,27 +119,35 @@ export default class CollectionNode implements INode { if (docFilter && docFilter.filter.length > 0) { filter = docFilter.filter; } - const connection = getActiveConnection(); - try { - result = await connection?.cluster?.query( - `SELECT RAW META().id FROM \`${this.bucketName}\`.\`${this.scopeName - }\`.\`${this.collectionName}\` ${filter.length > 0 ? "WHERE " + filter : "" - } LIMIT ${this.limit}` - ); - } catch (err) { - if (err instanceof PlanningFailureError) { - const infoNode: InformationNode = new InformationNode( - "No indexes available, click to create one", - "No indexes available to list the documents in this collection", - { - command: Commands.checkAndCreatePrimaryIndex, - title: "Create Primary Index", - arguments: [this], - } + const couchbbaseRestAPI = new CouchbaseRestAPI(connection); + if (!hasQueryService(connection?.services!)) { + result = await couchbbaseRestAPI.getKVDocuments(this.bucketName, this.scopeName, this.collectionName, 0, this.limit); + result.rows = result.rows.map((item: any) => item.id); + } + else { + try { + result = await connection?.cluster?.query( + `SELECT RAW META().id FROM \`${this.bucketName}\`.\`${this.scopeName + }\`.\`${this.collectionName}\` ${filter.length > 0 ? "WHERE " + filter : "" + } LIMIT ${this.limit}` ); - documentList.push(infoNode); - } else if (err instanceof ParsingFailureError) { - logger.error(`In Collection Node: ${this.collectionName}: Parsing Failed: Incorrect filter definition`); + } catch (err) { + if (err instanceof PlanningFailureError) { + const infoNode: InformationNode = new InformationNode( + "No indexes available, click to create one", + "No indexes available to list the documents in this collection", + { + command: Commands.checkAndCreatePrimaryIndex, + title: "Create Primary Index", + arguments: [this], + } + ); + documentList.push(infoNode); + result = await couchbbaseRestAPI.getKVDocuments(this.bucketName, this.scopeName, this.collectionName, 0, this.limit); + result.rows = result.rows.map((item: any) => item.id); + } else if (err instanceof ParsingFailureError) { + logger.error(`In Collection Node: ${this.collectionName}: Parsing Failed: Incorrect filter definition`); + } } } result?.rows.forEach((documentName: string) => { @@ -151,7 +166,7 @@ export default class CollectionNode implements INode { // TODO: add local only (un-synchronized) files to documentList // Checking document list length with 2 as Schema and index Directory are always present - if (documentList.length === 2) { + if (((!isQueryServicesEnable && documentList.length === 0)) || documentList.length === 2) { documentList.push(new InformationNode("No Documents found")); } else if (this.documentCount > documentList.length) { documentList.push(new PagerNode(this)); diff --git a/src/model/ScopeNode.ts b/src/model/ScopeNode.ts index 85b3c662..7cc3206e 100644 --- a/src/model/ScopeNode.ts +++ b/src/model/ScopeNode.ts @@ -24,6 +24,8 @@ import CollectionNode from "./CollectionNode"; import { logger } from "../logger/logger"; import InformationNode from "./InformationNode"; import { ParsingFailureError, PlanningFailureError } from "couchbase"; +import { hasQueryService } from "../util/common"; +import { CouchbaseRestAPI } from "../util/apis/CouchbaseRestAPI"; export class ScopeNode implements INode { constructor( @@ -62,7 +64,14 @@ export class ScopeNode implements INode { * @returns Two Directory one contains Index definitions and other contains Collections * */ public async getChildren(): Promise { + const connection = getActiveConnection(); + if (!connection) { + return []; + } const collectionList: any[] = []; + const couchbaseRestAPI = new CouchbaseRestAPI(connection); + const KVCollectionCount: Map = await couchbaseRestAPI.getKVDocumentCount(this.bucketName, this.scopeName); + for (const collection of this.collections) { try { const docFilter = Memory.state.get( @@ -70,15 +79,19 @@ export class ScopeNode implements INode { ); const filter: string = docFilter && docFilter.filter.length > 0 ? docFilter.filter : ""; - const connection = getActiveConnection(); let rowCount = 0; try { - const queryResult = await connection?.cluster?.query( - `select count(1) as count from \`${this.bucketName}\`.\`${this.scopeName - }\`.\`${collection.name}\` ${filter.length > 0 ? "WHERE " + filter : "" - };` - ); - rowCount = queryResult?.rows[0].count; + if (!hasQueryService(connection?.services) || filter === "") { + rowCount = KVCollectionCount.get(`kv_collection_item_count-${this.bucketName}-${this.scopeName}-${collection.name}`) ?? 0; + } + else { + const queryResult = await connection?.cluster?.query( + `select count(1) as count from \`${this.bucketName}\`.\`${this.scopeName + }\`.\`${collection.name}\` ${filter.length > 0 ? "WHERE " + filter : "" + };` + ); + rowCount = queryResult?.rows[0].count; + } } catch (err: any) { if (err instanceof PlanningFailureError) { vscode.window.showErrorMessage( diff --git a/src/types/IConnection.ts b/src/types/IConnection.ts index c93a2538..4998d9e2 100644 --- a/src/types/IConnection.ts +++ b/src/types/IConnection.ts @@ -21,5 +21,6 @@ export interface IConnection { readonly password?: string; readonly connectionIdentifier: string; readonly isSecure: boolean; + services?: string[]; cluster?: Cluster; } diff --git a/src/util/OverviewClusterUtils/ClusterOverviewGeneralTab.ts b/src/util/OverviewClusterUtils/ClusterOverviewGeneralTab.ts index eef5426b..e24ab47b 100644 --- a/src/util/OverviewClusterUtils/ClusterOverviewGeneralTab.ts +++ b/src/util/OverviewClusterUtils/ClusterOverviewGeneralTab.ts @@ -1,5 +1,20 @@ +/* + * Copyright 2011-2020 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { IKeyValuePair } from "../../types/IKeyValuePair"; -import { fmtByte, formatServices, mbToGb } from "./OverviewClusterHelper"; +import { fmtByte, formatServices, mbToGb } from "./OverviewClusterHelper"; import { ServerOverview } from "../apis/ServerOverview"; import { Constants } from "../constants"; @@ -23,7 +38,7 @@ export const getGeneralClusterDetails = (serverOverview: ServerOverview | undefi // Services details.push({ key: Constants.SERVICES, - value: formatServices(serverOverview.getNodes()[0].services.join(', ').toString()) || "NA" + value: formatServices(getServices(serverOverview).join(', ').toString()) || "NA" }); // Nodes @@ -45,6 +60,10 @@ export const getGeneralClusterDetails = (serverOverview: ServerOverview | undefi return details; }; +export const getServices = (serverOverview: ServerOverview): string[] => { + return serverOverview.getNodes()[0].services; +}; + export const getGeneralQuotaDetails = (serverOverview: ServerOverview | undefined): IKeyValuePair[] => { let details: IKeyValuePair[] = []; if (serverOverview === undefined) { diff --git a/src/util/OverviewClusterUtils/getOverviewClusterData.ts b/src/util/OverviewClusterUtils/getOverviewClusterData.ts index 97d0ac9b..9f20c76e 100644 --- a/src/util/OverviewClusterUtils/getOverviewClusterData.ts +++ b/src/util/OverviewClusterUtils/getOverviewClusterData.ts @@ -1,3 +1,18 @@ +/* + * Copyright 2011-2020 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { BucketSettings, Bucket } from "couchbase"; import { logger } from "../../logger/logger"; import { IConnection } from "../../types/IConnection"; @@ -27,7 +42,7 @@ const fetchBucketNames = (bucketsSettings: BucketSettings[] | undefined, connect return allBuckets; }; -export const getClusterOverviewData = async (refreshRequired: boolean = false): Promise => { +export const getClusterOverviewData = async (refreshRequired: boolean = false): Promise => { let clusterOverviewData: IClusterOverview = { generalDetails: null, buckets: null, diff --git a/src/util/apis/CouchbaseRestAPI.ts b/src/util/apis/CouchbaseRestAPI.ts index e851e9cf..c12892ea 100644 --- a/src/util/apis/CouchbaseRestAPI.ts +++ b/src/util/apis/CouchbaseRestAPI.ts @@ -1,3 +1,18 @@ +/* + * Copyright 2011-2020 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import axios from "axios"; import * as keytar from "keytar"; import { Constants } from "../constants"; @@ -37,7 +52,7 @@ export class CouchbaseRestAPI { }); process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; let status: string = content.status.toString(); - if(!(status.length === 3 && status.charAt(0) === '2')){ + if (!(status.length === 3 && status.charAt(0) === '2')) { logger.error(`Failed to fetch overview data, url:${url}, status:${status}`); } let serverOverview = plainToClass(ServerOverview, (content.data)); @@ -68,9 +83,119 @@ export class CouchbaseRestAPI { process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; let bucketOverview = plainToClass(BucketOverview, (content.data)); let status: string = content.status.toString(); - if(!(status.length === 3 && status.charAt(0) === '2')){ + if (!(status.length === 3 && status.charAt(0) === '2')) { logger.error(`Failed to fetch bucket data, bucketName:${bucketName} , url:${url}, status:${status}`); } return bucketOverview; } + + public async getKVDocuments(bucketName: string, scopeName: string, collectionName: string, skip: number, limit: number) { + const username = this.connection.username; + const password = await keytar.getPassword(Constants.extensionID, getConnectionId(this.connection)); + if (!password) { + return undefined; + } + let url = (await getServerURL(this.connection.url))[0]; + url = (this.connection.isSecure ? `https://${url}:18091` : `http://${url}:8091`); + url += `/pools/default/buckets/${bucketName}/scopes/${scopeName}/collections/${collectionName}/docs?skip=${skip}&limit=${limit}&include_doc=false`; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + let content = await axios.get(url, { + method: "GET", + headers: { + Authorization: `Basic ${btoa(`${username}:${password}`)}` + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: false, + }) + }); + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; + return content.data; + } + + public async getKVDocumentCount(bucketName: string, scopeName: string): Promise> { + const username = this.connection.username; + const password = await keytar.getPassword(Constants.extensionID, getConnectionId(this.connection)); + const KVCollectionCount = new Map; + if (!password) { + return KVCollectionCount; + } + let url = (await getServerURL(this.connection.url))[0]; + url = (this.connection.isSecure ? `https://${url}:18091` : `http://${url}:8091`); + url += `/pools/default/stats/range/`; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + + const payload = JSON.stringify([ + { + "step": 3, + "timeWindow": 360, + "start": -3, + "metric": [ + { + "label": "name", + "value": "kv_collection_item_count" + }, + { + "label": "bucket", + "value": bucketName + }, + { + "label": "scope", + "value": scopeName + } + ], + "nodesAggregation": "sum" + } + ]); + let content = await axios.post( + url, + payload, + { + headers: { + Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`, + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: false, + }), + } + ); + + const result = content.data; + + for (const i of result[0].data) { + if (i.metric.name === 'kv_collection_item_count') { + const value = Number(i.values[0][1]); + KVCollectionCount.set(`kv_collection_item_count-${bucketName}-${scopeName}-${i.metric.collection}`, value); + } + } + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; + return KVCollectionCount; + } + + public async getKVDocumentMetaData(bucketName: string, scopeName: string, collectionName: string, documentId: string) { + const username = this.connection.username; + const password = await keytar.getPassword(Constants.extensionID, getConnectionId(this.connection)); + if (!password) { + return undefined; + } + let url = (await getServerURL(this.connection.url))[0]; + url = (this.connection.isSecure ? `https://${url}:18091` : `http://${url}:8091`); + url += `/pools/default/buckets/${bucketName}/scopes/${scopeName}/collections/${collectionName}/docs/${documentId}`; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + let content = await axios.get(url, { + method: "GET", + headers: { + Authorization: `Basic ${btoa(`${username}:${password}`)}` + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: false, + }) + }); + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; + const metaData = { + meta: content.data.meta, + xattrs: content.data.xattrs + }; + return metaData; + } } + diff --git a/src/util/common.ts b/src/util/common.ts index 42fbbab4..6949b2f5 100644 --- a/src/util/common.ts +++ b/src/util/common.ts @@ -49,4 +49,11 @@ export const getNonce = (): string => { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; +}; + +export const hasQueryService = (services: string[] | undefined) => { + if (!services) { + return false; + } + return services.includes('n1ql'); }; \ No newline at end of file diff --git a/src/util/connections.ts b/src/util/connections.ts index 95b22a6f..ecdc80eb 100644 --- a/src/util/connections.ts +++ b/src/util/connections.ts @@ -23,6 +23,9 @@ import { AuthenticationFailureError, Cluster } from "couchbase"; import { getClusterConnectingFormView } from "../webViews/connectionScreen.webview"; import ClusterConnectionTreeProvider from "../tree/ClusterConnectionTreeProvider"; import { logger } from "../logger/logger"; +import { getServices } from "./OverviewClusterUtils/ClusterOverviewGeneralTab"; +import { CouchbaseRestAPI } from "./apis/CouchbaseRestAPI"; +import { hasQueryService } from "./common"; export function getConnectionId(connection: IConnection) { const { url, username } = connection; @@ -182,6 +185,12 @@ export async function useConnection(connection: IConnection): Promise { try { connection.cluster = await Cluster.connect(connection.url, { username: connection.username, password: password, configProfile: 'wanDevelopment' }); setActiveConnection(connection); + // Set Services + const couchbaseRestAPI = new CouchbaseRestAPI(connection); + const serviceOverview = await couchbaseRestAPI.getOverview(); + connection.services = getServices(serviceOverview!); + // Set the isKVCluster context + vscode.commands.executeCommand('setContext', 'isKVCluster', !hasQueryService(connection.services)); status = true; vscode.window.showInformationMessage("Connection established successfully!"); logger.info(`Connection established successfully with ${connection.connectionIdentifier}`);