Skip to content

feat: add multi-mode view for Flink Artifacts/UDFs #2313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,18 @@
"command": "confluent.flink.statements.search.clear",
"title": "Clear Search",
"category": "Confluent: Flink Statements View"
},
{
"command": "confluent.flink.setUDFsViewMode",
"title": "Switch to Flink UDFs",
"icon": "$(code)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These icons make sense given what we have to work with, but I'm still not feeling that the functionality is obvious. Plus, as you mentioned, there was the idea of cycling through 3 modalities as well. I'll be interested to hear what design has to say

"category": "Confluent: Flink Artifacts/UDFs View"
},
{
"command": "confluent.flink.setArtifactsViewMode",
"title": "Switch to Flink Artifacts",
"icon": "$(folder-library)",
"category": "Confluent: Flink Artifacts/UDFs View"
}
],
"configuration": [
Expand Down Expand Up @@ -1348,6 +1360,14 @@
{
"command": "confluent.resources.ccloudEnvironment.setPrivateNetworkEndpoint",
"when": "false"
},
{
"command": "confluent.flink.setArtifactsViewMode",
"when": "confluent.flinkArtifactsUDFsViewMode == 'UDFs'"
},
{
"command": "confluent.flink.setUDFsViewMode",
"when": "confluent.flinkArtifactsUDFsViewMode == 'artifacts'"
}
],
"editor/title": [
Expand Down Expand Up @@ -1521,6 +1541,16 @@
"command": "confluent.artifacts.flink-compute-pool.select",
"when": "view == confluent-flink-artifacts && confluent.ccloudConnectionAvailable && config.confluent.flink.enableFlinkArtifacts",
"group": "navigation@1"
},
{
"command": "confluent.flink.setArtifactsViewMode",
"when": "view == confluent-flink-artifacts && confluent.ccloudConnectionAvailable && config.confluent.flink.enableFlinkArtifacts && confluent.flinkArtifactsPoolSelected && confluent.flinkArtifactsUDFsViewMode == 'UDFs'",
"group": "navigation@2"
},
{
"command": "confluent.flink.setUDFsViewMode",
"when": "view == confluent-flink-artifacts && confluent.ccloudConnectionAvailable && config.confluent.flink.enableFlinkArtifacts && confluent.flinkArtifactsPoolSelected && confluent.flinkArtifactsUDFsViewMode == 'artifacts'",
"group": "navigation@2"
}
],
"view/item/context": [
Expand Down
22 changes: 22 additions & 0 deletions src/commands/flinkArtifacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Disposable } from "vscode";
import { registerCommandWithLogging } from ".";
import { ContextValues, setContextValue } from "../context/values";
import { flinkArtifactUDFViewMode } from "../emitters";
import { FlinkArtifactsViewProviderMode } from "../viewProviders/multiViewDelegates/constants";

export async function setFlinkArtifactsViewModeCommand() {
flinkArtifactUDFViewMode.fire(FlinkArtifactsViewProviderMode.Artifacts);
await setContextValue(
ContextValues.flinkArtifactsUDFsViewMode,
FlinkArtifactsViewProviderMode.Artifacts,
);
}

export function registerFlinkArtifactCommands(): Disposable[] {
return [
registerCommandWithLogging(
"confluent.flink.setArtifactsViewMode",
setFlinkArtifactsViewModeCommand,
),
];
}
19 changes: 19 additions & 0 deletions src/commands/flinkUDFs.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Cerchie what do you think about if we moved the uploadUDF command into here as a more generalized command area for UDFs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that jives with what we have for Flink statements etc... sure!

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Disposable } from "vscode";
import { registerCommandWithLogging } from ".";
import { ContextValues, setContextValue } from "../context/values";
import { flinkArtifactUDFViewMode } from "../emitters";
import { FlinkArtifactsViewProviderMode } from "../viewProviders/multiViewDelegates/constants";

export async function setFlinkUDFViewModeCommand() {
flinkArtifactUDFViewMode.fire(FlinkArtifactsViewProviderMode.UDFs);
await setContextValue(
ContextValues.flinkArtifactsUDFsViewMode,
FlinkArtifactsViewProviderMode.UDFs,
);
}

export function registerFlinkUDFCommands(): Disposable[] {
return [
registerCommandWithLogging("confluent.flink.setUDFsViewMode", setFlinkUDFViewModeCommand),
];
}
11 changes: 9 additions & 2 deletions src/context/values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,19 @@ export enum ContextValues {
directKafkaClusterAvailable = "confluent.directKafkaClusterAvailable",
/** A direct connection has been made and a Schema Registry is available for selecting in the Schemas view. */
directSchemaRegistryAvailable = "confluent.directSchemaRegistryAvailable",

/** A resource has been selected for comparison. */
resourceSelectedForCompare = "confluent.resourceSelectedForCompare",
/** The user clicked a Kafka cluster tree item. */

/** The user focused a Kafka cluster for the Topics view. */
kafkaClusterSelected = "confluent.kafkaClusterSelected",
/** The user clicked a Schema Registry tree item. */
/** The user focused a Schema Registry for the Schemas view. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is for key-navigation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, these comment changes were just for clarity/accuracy since the event can fire if the "Select Kafka Cluster / Schema Registry" can be triggered from a quickpick as well as clicking on their respective tree items

schemaRegistrySelected = "confluent.schemaRegistrySelected",
/** The user focused a Flink compute pool for the Flink Statements view. */
flinkStatementsPoolSelected = "confluent.flinkStatementsPoolSelected",
/** The user focused a Flink compute pool for the Flink Artifacts view. */
flinkArtifactsPoolSelected = "confluent.flinkArtifactsPoolSelected",

/** The user applied a search string to the Resources view. */
resourceSearchApplied = "confluent.resourceSearchApplied",
/** The user applied a search string to the Topics view. */
Expand All @@ -74,6 +77,10 @@ export enum ContextValues {
flinkStatementsSearchApplied = "confluent.flinkStatementsSearchApplied",
/** The user applied a search string to the Flink Artifacts view. */
flinkArtifactsSearchApplied = "confluent.flinkArtifactsSearchApplied",

/** The user changed the mode of the Flink Artifacts/UDFs view. */
flinkArtifactsUDFsViewMode = "confluent.flinkArtifactsUDFsViewMode",

/**
* EXPERIMENTAL: Is the chat participant enabled?
* (This should go away once the `confluent.experimental.enableChatParticipant` setting is removed.)
Expand Down
4 changes: 4 additions & 0 deletions src/emitters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { KafkaCluster } from "./models/kafkaCluster";
import { ConnectionId, EnvironmentId } from "./models/resource";
import { Subject, SubjectWithSchemas } from "./models/schema";
import { SchemaRegistry } from "./models/schemaRegistry";
import { FlinkArtifactsViewProviderMode } from "./viewProviders/multiViewDelegates/constants";

// NOTE: these are kept at the global level to allow for easy access from any file and track where
// we .fire() events and where we react to them via .event()
Expand Down Expand Up @@ -135,3 +136,6 @@ export const projectScaffoldUri = new vscode.EventEmitter<vscode.Uri>();

/** Metadata for a given {@link vscode.Uri} has been updated. */
export const uriMetadataSet = new vscode.EventEmitter<vscode.Uri>();

/** Event emitter for switching Flink artifact/UDF view modes. */
export const flinkArtifactUDFViewMode = new vscode.EventEmitter<FlinkArtifactsViewProviderMode>();
15 changes: 13 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import { registerDockerCommands } from "./commands/docker";
import { registerDocumentCommands } from "./commands/documents";
import { registerEnvironmentCommands } from "./commands/environments";
import { registerExtraCommands } from "./commands/extra";
import { registerFlinkArtifactCommands } from "./commands/flinkArtifacts";
import { registerFlinkComputePoolCommands } from "./commands/flinkComputePools";
import { registerFlinkStatementCommands } from "./commands/flinkStatements";
import { registerFlinkUDFCommands } from "./commands/flinkUDFs";
import { registerKafkaClusterCommands } from "./commands/kafkaClusters";
import { registerOrganizationCommands } from "./commands/organizations";
import { registerNewResourceViewCommands } from "./commands/resources";
Expand Down Expand Up @@ -84,8 +86,9 @@ import { getTelemetryLogger } from "./telemetry/telemetryLogger";
import { getUriHandler } from "./uriHandler";
import { WriteableTmpDir } from "./utils/file";
import { RefreshableTreeViewProvider } from "./viewProviders/baseModels/base";
import { FlinkArtifactsViewProvider } from "./viewProviders/flinkArtifacts";
import { FlinkArtifactsUDFsViewProvider } from "./viewProviders/flinkArtifacts";
import { FlinkStatementsViewProvider } from "./viewProviders/flinkStatements";
import { FlinkArtifactsViewProviderMode } from "./viewProviders/multiViewDelegates/constants";
import { NewResourceViewProvider } from "./viewProviders/newResources";
import { ResourceViewProvider } from "./viewProviders/resources";
import { SchemasViewProvider } from "./viewProviders/schemas";
Expand Down Expand Up @@ -208,7 +211,7 @@ async function _activateExtension(
const topicViewProvider = TopicViewProvider.getInstance();
const schemasViewProvider = SchemasViewProvider.getInstance();
const statementsViewProvider = FlinkStatementsViewProvider.getInstance();
const artifactsViewProvider = FlinkArtifactsViewProvider.getInstance();
const artifactsViewProvider = FlinkArtifactsUDFsViewProvider.getInstance();
const supportViewProvider = new SupportViewProvider();
const viewProviderDisposables: vscode.Disposable[] = [
resourceViewProviderInstance,
Expand Down Expand Up @@ -252,8 +255,10 @@ async function _activateExtension(
...registerExtraCommands(),
...registerDockerCommands(),
...registerProjectGenerationCommands(),
...registerFlinkArtifactCommands(),
...registerFlinkComputePoolCommands(),
...registerFlinkStatementCommands(),
...registerFlinkUDFCommands(),
...registerDocumentCommands(),
...registerSearchCommands(),
registerUploadUDFCommand(),
Expand Down Expand Up @@ -407,6 +412,11 @@ async function setupContextValues() {
SCHEMA_URI_SCHEME,
MESSAGE_URI_SCHEME,
]);
// set the initial Flink artifacts view mode to "Artifacts" so the UDF mode toggle is visible
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there's a way to set it to something like "last resource touched". Like, if the user was looking at a UDF and re-opens the sidebar, they see UDFs. Might be impossible?

const flinkViewMode = setContextValue(
ContextValues.flinkArtifactsUDFsViewMode,
FlinkArtifactsViewProviderMode.Artifacts,
);
await Promise.all([
chatParticipantEnabled,
kafkaClusterSelected,
Expand All @@ -417,6 +427,7 @@ async function setupContextValues() {
resourcesWithNames,
resourcesWithURIs,
diffableResources,
flinkViewMode,
]);
}

Expand Down
61 changes: 61 additions & 0 deletions src/models/flinkUDF.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
import { ConnectionType } from "../clients/sidecar";
import { IconNames } from "../constants";
import { IdItem } from "./main";
import { ConnectionId, EnvironmentId, IResourceBase, ISearchable } from "./resource";

export class FlinkUdf implements IResourceBase, IdItem, ISearchable {
connectionId!: ConnectionId;
connectionType!: ConnectionType;
// shoup: update this once https://github.com/confluentinc/vscode/issues/1385 is done
iconName: IconNames = "code" as IconNames;

environmentId!: EnvironmentId;

id!: string;
name!: string;
description!: string;

provider!: string; // cloud
region!: string;

constructor(
props: Pick<
FlinkUdf,
| "connectionId"
| "connectionType"
| "environmentId"
| "id"
| "name"
| "description"
| "provider"
| "region"
>,
) {
this.connectionId = props.connectionId;
this.connectionType = props.connectionType;
this.environmentId = props.environmentId;
this.id = props.id;
this.name = props.name;
this.description = props.description;
Copy link
Preview

Copilot AI Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor assigns props.provider and props.region to the instance properties, but these assignments are missing. The constructor only assigns the first 6 properties but not provider and region.

Suggested change
this.description = props.description;
this.description = props.description;
this.provider = props.provider;
this.region = props.region;

Copilot uses AI. Check for mistakes.

}

searchableText(): string {
return `${this.name} ${this.description}`;
}
}

export class FlinkUdfTreeItem extends TreeItem {
resource: FlinkUdf;

constructor(resource: FlinkUdf) {
super(resource.name, TreeItemCollapsibleState.None);

this.id = resource.id;
this.resource = resource;
this.contextValue = `${resource.connectionType.toLowerCase()}-flink-udf`;

this.iconPath = new ThemeIcon(resource.iconName);
this.description = resource.description;
}
}
Loading