diff --git a/client/src/components/ContentNavigator/ContentModel.ts b/client/src/components/ContentNavigator/ContentModel.ts index 8a939cac6..288d05c58 100644 --- a/client/src/components/ContentNavigator/ContentModel.ts +++ b/client/src/components/ContentNavigator/ContentModel.ts @@ -1,6 +1,8 @@ // Copyright © 2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Uri } from "vscode"; +import { Uri, l10n } from "vscode"; + +import { extname } from "path"; import { ALL_ROOT_FOLDERS, Messages } from "./const"; import { ContentAdapter, ContentItem } from "./types"; @@ -92,6 +94,43 @@ export class ContentModel { ); } + public async createUniqueFileOfPrefix( + parentItem: ContentItem, + fileName: string, + buffer?: ArrayBufferLike, + ) { + const itemsInFolder = await this.getChildren(parentItem); + const uniqueFileName = getUniqueFileName(); + + return await this.createFile(parentItem, uniqueFileName, buffer); + + function getUniqueFileName(): string { + const ext = extname(fileName); + const basename = fileName.replace(ext, ""); + const usedFlowNames = itemsInFolder.reduce((carry, item) => { + if (item.name.endsWith(ext)) { + return { ...carry, [item.name]: true }; + } + return carry; + }, {}); + + if (!usedFlowNames[fileName]) { + return fileName; + } + + let number = 1; + let newFileName; + do { + newFileName = l10n.t("{basename}_Copy{number}.flw", { + basename, + number: number++, + }); + } while (usedFlowNames[newFileName]); + + return newFileName || fileName; + } + } + public async createFolder( item: ContentItem, name: string, diff --git a/client/src/components/ContentNavigator/convert.ts b/client/src/components/ContentNavigator/convert.ts index be54ca318..4a8783e2a 100644 --- a/client/src/components/ContentNavigator/convert.ts +++ b/client/src/components/ContentNavigator/convert.ts @@ -411,7 +411,7 @@ export class NotebookToFlowConverter { } const parentItem = await this.parent(); - const newItem = await this.contentModel.createFile( + const newItem = await this.contentModel.createUniqueFileOfPrefix( parentItem, outputName, flowDataUint8Array, diff --git a/client/src/connection/rest/RestSASServerAdapter.ts b/client/src/connection/rest/RestSASServerAdapter.ts index 0466e8a0a..b5e6e31c0 100644 --- a/client/src/connection/rest/RestSASServerAdapter.ts +++ b/client/src/connection/rest/RestSASServerAdapter.ts @@ -146,22 +146,26 @@ class RestSASServerAdapter implements ContentAdapter { fileName: string, buffer?: ArrayBufferLike, ): Promise { - const response = await this.fileSystemApi.createFileOrDirectory({ - sessionId: this.sessionId, - fileOrDirectoryPath: this.trimComputePrefix(parentItem.uri), - fileProperties: { name: fileName, isDirectory: false }, - }); + try { + const response = await this.fileSystemApi.createFileOrDirectory({ + sessionId: this.sessionId, + fileOrDirectoryPath: this.trimComputePrefix(parentItem.uri), + fileProperties: { name: fileName, isDirectory: false }, + }); - const contentItem = this.filePropertiesToContentItem(response.data); + const contentItem = this.filePropertiesToContentItem(response.data); - if (buffer) { - await this.updateContentOfItemAtPath( - this.trimComputePrefix(contentItem.uri), - new TextDecoder().decode(buffer), - ); - } + if (buffer) { + await this.updateContentOfItemAtPath( + this.trimComputePrefix(contentItem.uri), + new TextDecoder().decode(buffer), + ); + } - return contentItem; + return contentItem; + } catch (error) { + return; + } } public async deleteItem(item: ContentItem): Promise {