diff --git a/workspaces/ballerina/ballerina-core/src/interfaces/service.ts b/workspaces/ballerina/ballerina-core/src/interfaces/service.ts index cb003e7e718..f9545b3133f 100644 --- a/workspaces/ballerina/ballerina-core/src/interfaces/service.ts +++ b/workspaces/ballerina/ballerina-core/src/interfaces/service.ts @@ -112,7 +112,8 @@ export enum Protocol { MESSAGE_BROKER = "MESSAGE_BROKER", GRAPHQL = "GRAPHQL", FTP = "FTP", - CDC = "CDC" + CDC = "CDC", + SMB = "SMB" } export interface HttpPayloadContext { diff --git a/workspaces/ballerina/ballerina-core/src/rpc-types/platform-ext/utils.ts b/workspaces/ballerina/ballerina-core/src/rpc-types/platform-ext/utils.ts index b261807b34d..452a9c576cb 100644 --- a/workspaces/ballerina/ballerina-core/src/rpc-types/platform-ext/utils.ts +++ b/workspaces/ballerina/ballerina-core/src/rpc-types/platform-ext/utils.ts @@ -20,7 +20,7 @@ import { DevantScopes } from "@wso2/wso2-platform-core"; const INTEGRATION_API_MODULES = ["http", "graphql", "tcp"]; const EVENT_INTEGRATION_MODULES = ["kafka", "rabbitmq", "salesforce", "trigger.github", "mqtt", "asb"]; -const FILE_INTEGRATION_MODULES = ["ftp", "file"]; +const FILE_INTEGRATION_MODULES = ["ftp", "smb", "file"]; const AI_AGENT_MODULE = "ai"; const MCP_MODULE = "mcp"; diff --git a/workspaces/ballerina/ballerina-extension/e2e-test/e2e-playwright-tests/file-integration/smb.spec.ts b/workspaces/ballerina/ballerina-extension/e2e-test/e2e-playwright-tests/file-integration/smb.spec.ts new file mode 100644 index 00000000000..ca7b26adb9d --- /dev/null +++ b/workspaces/ballerina/ballerina-extension/e2e-test/e2e-playwright-tests/file-integration/smb.spec.ts @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com) + * + * WSO2 LLC. licenses this file to you 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 { expect, test } from '@playwright/test'; +import { addArtifact, initTest, page } from '../utils/helpers'; +import { Form, switchToIFrame } from '@wso2/playwright-vscode-tester'; +import { ProjectExplorer } from '../utils/pages'; +import { DEFAULT_PROJECT_NAME } from '../utils/helpers/setup'; + +export default function createTests() { + const listenerName = 'smbListener'; + test.describe('SMB Integration Tests', { + tag: '@group1', + }, async () => { + test.describe.configure({ mode: 'serial' }); + initTest(); + test('Create SMB Integration', async ({ }, testInfo) => { + const testAttempt = testInfo.retry + 1; + console.log('Creating a new service in test attempt: ', testAttempt); + await addArtifact('SMB Integration', 'trigger-smb'); + const artifactWebView = await switchToIFrame('WSO2 Integrator: BI', page.page); + if (!artifactWebView) { + throw new Error('WSO2 Integrator: BI webview not found'); + } + const form = new Form(page.page, 'WSO2 Integrator: BI', artifactWebView); + await form.switchToFormView(false, artifactWebView); + await form.submit('Create'); + + const projectExplorer = new ProjectExplorer(page.page); + await projectExplorer.findItem([DEFAULT_PROJECT_NAME, `SMB Integration`], true); + + const context = artifactWebView.locator(`text=${listenerName}`); + await context.waitFor(); + }); + + test('Editing SMB Service', async ({ }, testInfo) => { + const testAttempt = testInfo.retry + 1; + console.log('Editing a service in test attempt: ', testAttempt); + const artifactWebView = await switchToIFrame('WSO2 Integrator: BI', page.page); + if (!artifactWebView) { + throw new Error('WSO2 Integrator: BI webview not found'); + } + + const editBtn = artifactWebView.locator('vscode-button[title="Edit Service"]'); + await editBtn.waitFor(); + await editBtn.click({ force: true }); + + const form = new Form(page.page, 'WSO2 Integrator: BI', artifactWebView); + await form.switchToFormView(false, artifactWebView); + + await form.fill({ + values: { + 'host': { + type: 'cmEditor', + value: `127.0.0.6`, + additionalProps: { clickLabel: true, switchMode: 'primary-mode', window: global.window } + } + } + }); + + await form.submit('Save Changes'); + + const saveChangesBtn = artifactWebView.locator('#save-changes-btn vscode-button[appearance="primary"]'); + await saveChangesBtn.waitFor({ state: 'visible' }); + await expect(saveChangesBtn).toHaveClass(/disabled/, { timeout: 5000 }); + await expect(saveChangesBtn).toHaveText('Save Changes'); + + const backBtn = artifactWebView.locator('[data-testid="back-button"]'); + await backBtn.waitFor(); + await backBtn.click(); + + await editBtn.waitFor(); + + const context = artifactWebView.locator(`text=${listenerName}`); + await context.waitFor(); + }); + + test('Delete SMB Integration', async ({ }, testInfo) => { + const testAttempt = testInfo.retry + 1; + console.log('Deleting SMB integration in test attempt: ', testAttempt); + + const artifactWebView = await switchToIFrame('WSO2 Integrator: BI', page.page); + if (!artifactWebView) { + throw new Error('WSO2 Integrator: BI webview not found'); + } + const projectExplorer = new ProjectExplorer(page.page); + const serviceTreeItem = await projectExplorer.findItem([DEFAULT_PROJECT_NAME, `SMB Integration`], true); + await serviceTreeItem.click({ button: 'right' }); + const deleteButton = page.page.getByRole('button', { name: 'Delete' }).first(); + await deleteButton.waitFor({ timeout: 5000 }); + await deleteButton.click(); + await expect(serviceTreeItem).not.toBeVisible({ timeout: 10000 }); + }); + }); +} diff --git a/workspaces/ballerina/ballerina-extension/e2e-test/e2e-playwright-tests/test.list.ts b/workspaces/ballerina/ballerina-extension/e2e-test/e2e-playwright-tests/test.list.ts index 2480e6b92ae..cd970b5ef54 100644 --- a/workspaces/ballerina/ballerina-extension/e2e-test/e2e-playwright-tests/test.list.ts +++ b/workspaces/ballerina/ballerina-extension/e2e-test/e2e-playwright-tests/test.list.ts @@ -55,6 +55,7 @@ import twillioIntegration from './event-integration/twillio.spec'; import githubIntegration from './event-integration/github.spec'; import ftpIntegration from './file-integration/ftp.spec'; +import smbIntegration from './file-integration/smb.spec'; import directoryIntegration from './file-integration/directory.spec'; import functionArtifact from './other-artifacts/function.spec'; @@ -127,6 +128,7 @@ test.describe(githubIntegration); // <----File Integration Test----> test.describe(ftpIntegration); +test.describe(smbIntegration); test.describe(directoryIntegration); // <----Other Artifacts Test----> diff --git a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts index c9d52bd6822..e80361e48f1 100644 --- a/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts +++ b/workspaces/ballerina/ballerina-extension/src/utils/project-artifacts.ts @@ -565,6 +565,8 @@ function getCustomEntryNodeIcon(type: string) { return "bi-asb"; case "ftp": return "bi-ftp"; + case "smb": + return "bi-smb"; case "file": return "bi-file"; case "mcp": diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ComponentListView/FileIntegrationPanel.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ComponentListView/FileIntegrationPanel.tsx index cc60c31c5f8..4fc3a78c0c9 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ComponentListView/FileIntegrationPanel.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ComponentListView/FileIntegrationPanel.tsx @@ -92,6 +92,8 @@ export function getCustomFileIntegrationIcon(type: string) { switch (type) { case "ftp": return ; + case "smb": + return ; case "file": return ; default: diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/Parameters/Parameters.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/Parameters/Parameters.tsx index 12ea1d09c2d..30d08334525 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/Parameters/Parameters.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/Parameters/Parameters.tsx @@ -36,6 +36,7 @@ export interface ParametersProps { onEditClick?: (param: ParameterModel) => void; showPayload: boolean; typeLabel?: string; + streamEnabled?: boolean; } const ParamLabelContainer = styled.div` diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/index.tsx index 803a6bf2217..f8a815c1c95 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/FileIntegrationForm/index.tsx @@ -178,7 +178,7 @@ export function FileIntegrationForm(props: FileIntegrationFormProps) { const [functionModel, setFunctionModel] = useState(props.functionModel || null); const payloadContext = { - protocol: Protocol.FTP, + protocol: model.moduleName === 'smb' ? Protocol.SMB : Protocol.FTP, filterType: functionModel?.name?.metadata?.label || "", } as GeneralPayloadContext; diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/index.tsx index 02ac3b6109e..f910cc02f34 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/index.tsx @@ -360,7 +360,7 @@ export function ServiceDesigner(props: ServiceDesignerProps) { } setReadonlyProperties(readonlyProps); - setIsFtpService(service.moduleName === "ftp"); + setIsFtpService(service.moduleName === "ftp" || service.moduleName === "smb"); setIsHttpService(service.moduleName === "http"); setIsMcpService(service.moduleName === "mcp"); setIsCdcService(service.moduleName === "mssql" || service.moduleName === "postgresql"); diff --git a/workspaces/ballerina/ballerina-visualizer/src/views/BI/TypeEditor/utils.tsx b/workspaces/ballerina/ballerina-visualizer/src/views/BI/TypeEditor/utils.tsx index 4c9ebf86462..a9d47849f89 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/views/BI/TypeEditor/utils.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/views/BI/TypeEditor/utils.tsx @@ -80,7 +80,7 @@ export const getTypes = (types: VisibleTypeItem[], filterDMTypes?: boolean, payl items })); - if (payloadContext?.protocol === Protocol.FTP || payloadContext?.protocol === Protocol.CDC) { + if (payloadContext?.protocol === Protocol.FTP || payloadContext?.protocol === Protocol.SMB || payloadContext?.protocol === Protocol.CDC) { categories = categories .filter((category) => category.category === "User-Defined") diff --git a/workspaces/ballerina/component-diagram/src/components/nodes/EntryNode/components/GeneralWidget.tsx b/workspaces/ballerina/component-diagram/src/components/nodes/EntryNode/components/GeneralWidget.tsx index 18b82470b4c..2b4da68bfbb 100644 --- a/workspaces/ballerina/component-diagram/src/components/nodes/EntryNode/components/GeneralWidget.tsx +++ b/workspaces/ballerina/component-diagram/src/components/nodes/EntryNode/components/GeneralWidget.tsx @@ -122,6 +122,8 @@ function getCustomEntryNodeIcon(type: string) { return ; case "ftp": return ; + case "smb": + return ; case "file": return ; case "mssql": diff --git a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx index 0adc8b2db51..75a452aac72 100644 --- a/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx +++ b/workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/GenericImportTab.tsx @@ -579,12 +579,14 @@ export function GenericImportTab(props: GenericImportTabProps) { Generate Sample JSON - - or - + {payloadContext?.protocol !== Protocol.FTP && payloadContext?.protocol !== Protocol.SMB && ( + + or + + )} )} {payloadContext?.protocol!==Protocol.FTP && ( + + + + + + + + + + + + \ No newline at end of file