Skip to content

Commit

Permalink
Move connections pane into core positron (#4967)
Browse files Browse the repository at this point in the history
Addresses #4874 

Needs codicons from:
posit-dev/positron-codicons#6
Needs ark side: posit-dev/ark#589

This PR is an initial implementation of the connections pane into
Positron core. It's still missing a lot of functionality, but will
eventually be equivalent to the current positron-connections extension.

TODO:

- [x] Workspace storage of previous connections
- [x] Setting to opt-in for the new implementation
- [x] Button to open data explorer for a `Table` or `View`
- [x] Codicons for most common DB data types (schema, collections, etc)
and for creating a new connection
- [x] Review event listeners disposing
- [x] Implement Python side of new GetMetadata RPC
- [x] Implement refresh connections
- [x] Display the field type 
- [x] Display the language for each connection
- [x] Review error handling
- [x] Fix smoke tests (smoke tests are passing locally)
- [x] Fix integration tests
- [x] Rename 'Connections Core' to just 'Connections'
- [x] Support for the focus event


Demo:



https://github.com/user-attachments/assets/9e7b7905-3c1b-421d-a9e4-6c8e5d3b3b94

Must be enabled with:


![image](https://github.com/user-attachments/assets/b3870a00-9fe2-4144-b54e-d74b7521945f)
  • Loading branch information
dfalbel authored Oct 16, 2024
1 parent 638de70 commit 67cf79b
Show file tree
Hide file tree
Showing 26 changed files with 2,313 additions and 7 deletions.
8 changes: 8 additions & 0 deletions build/lib/i18n.resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@
"name": "vs/workbench/contrib/positronPlots",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/positronConnections",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/positronOutputWebview",
"project": "vscode-workbench"
Expand Down Expand Up @@ -526,6 +530,10 @@
"name": "vs/workbench/services/positronConsole",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/services/positronConnections",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/services/positronDataExplorer",
"project": "vscode-workbench"
Expand Down
6 changes: 4 additions & 2 deletions extensions/positron-connections/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
{
"id": "positron-connections",
"title": "%view.title%",
"icon": "media/database.svg"
"icon": "media/database.svg",
"when": "positron-connections.connectionsEnabled"
}
]
},
Expand All @@ -32,7 +33,8 @@
"id": "connections",
"name": "%view.title%",
"icon": "media/database.svg",
"contextualTitle": "%view.description%"
"contextualTitle": "%view.description%",
"when": "positron-connections.connectionsEnabled"
}
]
},
Expand Down
37 changes: 36 additions & 1 deletion extensions/positron-connections/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,41 @@ import * as positron from 'positron';
import { ConnectionItem, ConnectionItemsProvider, isActiveConnectionItem, DatabaseConnectionItem, DisconnectedConnectionItem } from './connection';
import { PositronConnectionsComm } from './comms/ConnectionsComms';


export function activate(context: vscode.ExtensionContext) {

const config = vscode.workspace.getConfiguration('positron');
const enabled = !config.get<boolean>('connections', false);
vscode.commands.executeCommand('setContext', 'positron-connections.connectionsEnabled', enabled);

vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('positron.connections')) {
const config = vscode.workspace.getConfiguration('positron');
const enabled = !config.get<boolean>('connections', false);
if (enabled) {
activateImpl(context);
} else {
deactivate(context);
}
vscode.commands.executeCommand(
'setContext',
'positron-connections.connectionsEnabled',
enabled
);
}
});

if (enabled) {
return activateImpl(context);
}
}

/**
* Activates the extension.
*
* @param context An ExtensionContext that contains the extention context.
*/
export function activate(context: vscode.ExtensionContext) {
export function activateImpl(context: vscode.ExtensionContext) {
const viewId = 'connections';
const connectionProvider = new ConnectionItemsProvider(context);
const connectionTreeView = vscode.window.createTreeView(viewId, { treeDataProvider: connectionProvider });
Expand Down Expand Up @@ -106,3 +135,9 @@ export function activate(context: vscode.ExtensionContext) {
// to acccess the ConnectionItemsProvider instance
return connectionProvider;
}

function deactivate(context: vscode.ExtensionContext) {
context.subscriptions.forEach((e) => {
e.dispose();
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
ListObjectsRequest,
ObjectSchema,
PreviewObjectRequest,
MetadataSchema,
GetMetadataRequest,
)
from .positron_comm import CommMessage, JsonRpcErrorCode, PositronComm
from .third_party import pd_, sqlalchemy_
Expand Down Expand Up @@ -137,6 +139,25 @@ def preview_object(self, path: List[ObjectSchema]) -> Any:
"""
raise NotImplementedError()

def get_metadata(self) -> MetadataSchema:
"""
Returns metadata about the connection.
The metadata object must contain the following properties:
- name: The name of the connection.
- language_id: The language ID for the connection. Essentially just R or python.
- host: The host of the connection.
- type: The type of the connection.
- code: The code used to recreate the connection.
"""
return MetadataSchema(
name=self.display_name,
language_id="python",
host=self.host,
type=self.type,
code=self.code,
)


class ConnectionsService:
"""
Expand Down Expand Up @@ -439,6 +460,8 @@ def _handle_msg(
elif isinstance(request, PreviewObjectRequest):
self.handle_preview_object_request(connection, request)
result = None
elif isinstance(request, GetMetadataRequest):
result = self.handle_get_metadata_request(connection, request) # type: ignore
else:
raise NotImplementedError(f"Unhandled request: {request}")

Expand All @@ -450,7 +473,10 @@ def handle_contains_data_request(self, conn: Connection, request: ContainsDataRe
return False

object_types: Dict[str, Any] = conn.list_object_types()
contains = object_types[path[-1].kind].get("contains", "not_data")
try:
contains = object_types[path[-1].kind].get("contains", "not_data")
except KeyError:
contains = "not_data"
return isinstance(contains, str) and contains == "data"

def handle_get_icon_request(self, conn: Connection, request: GetIconRequest) -> str:
Expand All @@ -461,7 +487,10 @@ def handle_get_icon_request(self, conn: Connection, request: GetIconRequest) ->
icon = getattr(conn, "icon", None)
else:
object_types: Dict[str, Any] = conn.list_object_types()
icon = object_types[path[-1].kind].get("icon", "")
try:
icon = object_types[path[-1].kind].get("icon", None)
except KeyError:
pass

if icon is None:
return ""
Expand All @@ -484,6 +513,12 @@ def handle_preview_object_request(
title = request.params.path[-1].name
self._kernel.data_explorer_service.register_table(res, title)

def handle_get_metadata_request(
self, conn: Connection, request: GetMetadataRequest
) -> MetadataSchema:
res = conn.get_metadata()
return res


class SQLite3Connection(Connection):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,35 @@ class FieldSchema(BaseModel):
)


class MetadataSchema(BaseModel):
"""
MetadataSchema in Schemas
"""

name: StrictStr = Field(
description="Connection name",
)

language_id: StrictStr = Field(
description="Language ID for the connections. Essentially just R or python",
)

host: Optional[StrictStr] = Field(
default=None,
description="Connection host",
)

type: Optional[StrictStr] = Field(
default=None,
description="Connection type",
)

code: Optional[StrictStr] = Field(
default=None,
description="Code used to re-create the connection",
)


@enum.unique
class ConnectionsBackendRequest(str, enum.Enum):
"""
Expand All @@ -67,6 +96,9 @@ class ConnectionsBackendRequest(str, enum.Enum):
# Preview object data
PreviewObject = "preview_object"

# Gets metadata from the connections
GetMetadata = "get_metadata"


class ListObjectsParams(BaseModel):
"""
Expand Down Expand Up @@ -215,6 +247,35 @@ class PreviewObjectRequest(BaseModel):
)


class GetMetadataParams(BaseModel):
"""
A connection has tied metadata such as an icon, the host, etc.
"""

comm_id: StrictStr = Field(
description="The comm_id of the client we want to retrieve metdata for.",
)


class GetMetadataRequest(BaseModel):
"""
A connection has tied metadata such as an icon, the host, etc.
"""

params: GetMetadataParams = Field(
description="Parameters to the GetMetadata method",
)

method: Literal[ConnectionsBackendRequest.GetMetadata] = Field(
description="The JSON-RPC method name (get_metadata)",
)

jsonrpc: str = Field(
default="2.0",
description="The JSON-RPC version specifier",
)


class ConnectionsBackendMessageContent(BaseModel):
comm_id: str
data: Union[
Expand All @@ -223,6 +284,7 @@ class ConnectionsBackendMessageContent(BaseModel):
ContainsDataRequest,
GetIconRequest,
PreviewObjectRequest,
GetMetadataRequest,
] = Field(..., discriminator="method")


Expand All @@ -243,6 +305,8 @@ class ConnectionsFrontendEvent(str, enum.Enum):

FieldSchema.update_forward_refs()

MetadataSchema.update_forward_refs()

ListObjectsParams.update_forward_refs()

ListObjectsRequest.update_forward_refs()
Expand All @@ -262,3 +326,7 @@ class ConnectionsFrontendEvent(str, enum.Enum):
PreviewObjectParams.update_forward_refs()

PreviewObjectRequest.update_forward_refs()

GetMetadataParams.update_forward_refs()

GetMetadataRequest.update_forward_refs()
2 changes: 1 addition & 1 deletion extensions/positron-r/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@
},
"positron": {
"binaryDependencies": {
"ark": "0.1.144"
"ark": "0.1.145"
},
"minimumRVersion": "4.2.0",
"minimumRenvVersion": "1.0.9"
Expand Down
50 changes: 50 additions & 0 deletions positron/comms/connections-backend-openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,28 @@
"type": "null"
}
}
},
{
"name": "get_metadata",
"summary": "Gets metadata from the connections",
"description": "A connection has tied metadata such as an icon, the host, etc.",
"params": [
{
"name": "comm_id",
"description": "The comm_id of the client we want to retrieve metdata for.",
"required": true,
"schema": {
"type": "string"
}
}
],
"result": {
"schema": {
"$ref": "#/components/schemas/metadata_schema"
}
}
}

],
"components": {
"contentDescriptors": {},
Expand Down Expand Up @@ -172,6 +193,35 @@
"description": "The field data type"
}
}
},
"metadata_schema": {
"type": "object",
"required": [
"name",
"language_id"
],
"properties": {
"name": {
"type": "string",
"description": "Connection name"
},
"language_id": {
"type": "string",
"description": "Language ID for the connections. Essentially just R or python"
},
"host": {
"type": "string",
"description": "Connection host"
},
"type": {
"type": "string",
"description": "Connection type"
},
"code": {
"type": "string",
"description": "Code used to re-create the connection"
}
}
}
}
}
Expand Down
Binary file modified src/vs/base/browser/ui/codicons/codicon/codicon.ttf
Binary file not shown.
7 changes: 7 additions & 0 deletions src/vs/base/common/codiconsLibrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,4 +663,11 @@ export const codiconsLibrary = {
positronSelectRow: register('positron-select-row', 0xf281),
positronDataTypeObject: register('positron-data-type-object', 0xf282),
positronSizeToFit: register('positron-size-to-fit', 0xf283),
positronDatabaseConnection: register('positron-database-connection', 0xf284),
positronDisconnectConnection: register('positron-disconnect-connection', 0xf285),
positronNewConnection: register('positron-new-connection', 0xf286),
positronSchemaConnection: register('positron-schema-connection', 0xf287),
positronTableConnection: register('positron-table-connection', 0xf288),
positronCatalogConnection: register('positron-catalog-connection', 0xf289),
positronViewConnection: register('positron-view-connection', 0xf28a),
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { IRuntimeStartupService, RuntimeStartupPhase } from 'vs/workbench/servic
import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { isWebviewReplayMessage } from 'vs/workbench/contrib/positronWebviewPreloads/browser/utils';
import { IPositronWebviewPreloadService } from 'vs/workbench/services/positronWebviewPreloads/common/positronWebviewPreloadService';
import { IPositronConnectionsService } from 'vs/workbench/services/positronConnections/browser/interfaces/positronConnectionsService';

/**
* Represents a language runtime event (for example a message or state change)
Expand Down Expand Up @@ -1135,6 +1136,7 @@ export class MainThreadLanguageRuntime
@IPositronPlotsService private readonly _positronPlotService: IPositronPlotsService,
@IPositronIPyWidgetsService private readonly _positronIPyWidgetsService: IPositronIPyWidgetsService,
@IPositronWebviewPreloadService private readonly _positronWebviewPreloadService: IPositronWebviewPreloadService,
@IPositronConnectionsService private readonly _positronConnectionsService: IPositronConnectionsService,
@INotificationService private readonly _notificationService: INotificationService,
@ILogService private readonly _logService: ILogService,
@ICommandService private readonly _commandService: ICommandService,
Expand All @@ -1151,6 +1153,7 @@ export class MainThreadLanguageRuntime
this._positronPlotService.initialize();
this._positronIPyWidgetsService.initialize();
this._positronWebviewPreloadService.initialize();
this._positronConnectionsService.initialize();
this._proxy = extHostContext.getProxy(ExtHostPositronContext.ExtHostLanguageRuntime);
this._id = MainThreadLanguageRuntime.MAX_ID++;

Expand Down
Loading

0 comments on commit 67cf79b

Please sign in to comment.