diff --git a/react/src/components/ModelCardModal.tsx b/react/src/components/ModelCardModal.tsx index 9e22386ab..2204697f7 100644 --- a/react/src/components/ModelCardModal.tsx +++ b/react/src/components/ModelCardModal.tsx @@ -215,14 +215,6 @@ const ModelCardModal: React.FC = ({ size="small" disabled={!model_card?.vfolder?.cloneable} onClick={() => { - // const event = new CustomEvent('backend-ai-vfolder-cloning', { - // detail: { - // // TODO: change this to vfolder name - // name: mode_card?.name, - // }, - // }); - // onRequestClose(); - // document.dispatchEvent(event); setVisibleCloneModal(true); }} > diff --git a/react/src/components/ModelCloneModal.tsx b/react/src/components/ModelCloneModal.tsx index 7c9d6a4e8..5b9b6094d 100644 --- a/react/src/components/ModelCloneModal.tsx +++ b/react/src/components/ModelCloneModal.tsx @@ -94,11 +94,11 @@ const ModelCloneModal: React.FC = ({ formRef.current ?.validateFields() .then((values) => { - if (vfolder?.name && vfolder.host) { + if (vfolder?.id && vfolder.host) { mutationToClone.mutate( { input: values, - name: vfolder.name, + name: vfolder.id, }, { onSuccess(data) { diff --git a/src/components/backend-ai-data-view.ts b/src/components/backend-ai-data-view.ts index 8c247922f..de6288b4e 100644 --- a/src/components/backend-ai-data-view.ts +++ b/src/components/backend-ai-data-view.ts @@ -93,6 +93,7 @@ export default class BackendAIData extends BackendAIPage { @property({ type: Object }) options; @property({ type: Number }) capacity; @property({ type: String }) cloneFolderName = ''; + @property({ type: String }) cloneFolder = ''; @property({ type: Object }) storageProxyInfo = Object(); @property({ type: String }) folderType = 'user'; @property({ type: Number }) currentGroupIdx = 0; @@ -820,6 +821,11 @@ export default class BackendAIData extends BackendAIPage { if (e.detail) { const selectedItems = e.detail; this.cloneFolderName = selectedItems.name; + this.cloneFolder = globalThis.backendaiclient.supports( + 'vfolder-id-based', + ) + ? selectedItems.id + : selectedItems.name; this._cloneFolderDialog(); } }); @@ -1257,7 +1263,7 @@ export default class BackendAIData extends BackendAIPage { } cloneable = cloneableEl ? cloneableEl.selected : false; this.cloneFolderNameInput.reportValidity(); - if (this.cloneFolderNameInput.checkValidity()) { + if (this.cloneFolderNameInput.checkValidity() && this.cloneFolder) { const input = { cloneable: cloneable, permission: permission, @@ -1267,7 +1273,7 @@ export default class BackendAIData extends BackendAIPage { }; const job = globalThis.backendaiclient.vfolder.clone( input, - this.cloneFolderName, + this.cloneFolder, ); job .then(() => { diff --git a/src/components/backend-ai-folder-explorer.ts b/src/components/backend-ai-folder-explorer.ts index 691408d6d..177de23de 100644 --- a/src/components/backend-ai-folder-explorer.ts +++ b/src/components/backend-ai-folder-explorer.ts @@ -53,6 +53,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { // [target vfolder information] @property({ type: String }) vfolderID = ''; @property({ type: String }) vfolderName = ''; + @property({ type: String }) vfolder = ''; @property({ type: Array }) vfolderFiles = []; @property({ type: String }) vhost = ''; @property({ type: Boolean }) isWritable = false; @@ -496,7 +497,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { const path = this.breadcrumb.concat(fn).join('/'); const job = globalThis.backendaiclient.vfolder.request_download_token( path, - this.vfolderName, + this.vfolder, archive, ); job @@ -777,6 +778,9 @@ export default class BackendAIFolderExplorer extends BackendAIPage { return vfolder.id === this.vfolderID; }); this.vfolderName = vfolder.name; + this.vfolder = globalThis.backendaiclient.supports('vfolder-id-based') + ? vfolder.id + : vfolder.name; this.vhost = vfolder.host; this.isWritable = vfolder.permission.includes('w'); @@ -791,7 +795,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { this.fileListGrid.selectedItems = []; const filesInfo = await globalThis.backendaiclient.vfolder.list_files( this.breadcrumb.join('/'), - this.vfolderName, + this.vfolder, ); const details = filesInfo.items; @@ -968,7 +972,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { const job = globalThis.backendaiclient.vfolder.rename_file( path, newName, - this.vfolderName, + this.vfolder, this.is_dir, ); job @@ -1034,7 +1038,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { const job = globalThis.backendaiclient.vfolder.delete_files( filenames, true, - this.vfolderName, + this.vfolder, ); job.then((res) => { this.notification.text = @@ -1054,7 +1058,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { const job = globalThis.backendaiclient.vfolder.delete_files( [path], true, - this.vfolderName, + this.vfolder, ); job .then((res) => { @@ -1121,7 +1125,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { this.mkdirNameInput.reportValidity(); if (this.mkdirNameInput.checkValidity()) { const job = globalThis.backendaiclient.vfolder - .mkdir([...this.breadcrumb, newfolder].join('/'), this.vfolderName) + .mkdir([...this.breadcrumb, newfolder].join('/'), this.vfolder) .catch((err) => { if (err & err.message) { this.notification.text = PainKiller.relieve(err.title); @@ -1290,7 +1294,7 @@ export default class BackendAIFolderExplorer extends BackendAIPage { const job = globalThis.backendaiclient.vfolder.create_upload_session( path, fileObj, - this.vfolderName, + this.vfolder, ); job.then((url) => { const start_date = new Date().getTime(); diff --git a/src/components/backend-ai-storage-list.ts b/src/components/backend-ai-storage-list.ts index 04d334b56..837ae669e 100644 --- a/src/components/backend-ai-storage-list.ts +++ b/src/components/backend-ai-storage-list.ts @@ -105,6 +105,7 @@ export default class BackendAiStorageList extends BackendAIPage { @property({ type: Boolean }) enableVfolderTrashBin = false; @property({ type: Boolean }) authenticated = false; @property({ type: String }) renameFolderName = ''; + @property({ type: String }) renameFolderID = ''; @property({ type: String }) deleteFolderName = ''; @property({ type: String }) deleteFolderID = ''; @property({ type: String }) leaveFolderName = ''; @@ -1932,8 +1933,10 @@ export default class BackendAiStorageList extends BackendAIPage { * @param {Event} e - click the info icon button * */ _infoFolder(e) { - const folderName = this._getControlName(e); - const job = globalThis.backendaiclient.vfolder.info(folderName); + const folder = globalThis.backendaiclient.supports('vfolder-id-based') + ? this._getControlID(e) + : this._getControlName(e); + const job = globalThis.backendaiclient.vfolder.info(folder); job .then((value) => { this.folderInfo = value; @@ -1955,7 +1958,10 @@ export default class BackendAiStorageList extends BackendAIPage { * @param {Event} e - click the settings icon button * */ _modifyFolderOptionDialog(e) { - globalThis.backendaiclient.vfolder.name = this._getControlName(e); + globalThis.backendaiclient.vfolder.name = + globalThis.backendaiclient.supports('vfolder-id-based') + ? this._getControlID(e) + : this._getControlName(e); const job = globalThis.backendaiclient.vfolder.info( globalThis.backendaiclient.vfolder.name, ); @@ -2075,6 +2081,7 @@ export default class BackendAiStorageList extends BackendAIPage { */ async _updateFolderName() { globalThis.backendaiclient.vfolder.name = this.renameFolderName; + globalThis.backendaiclient.vfolder.id = this.renameFolderID; const newName = this.newFolderNameInput.value; this.newFolderNameInput.reportValidity(); if (newName) { @@ -2101,6 +2108,11 @@ export default class BackendAiStorageList extends BackendAIPage { * @param {Event} e - click the */ _renameFolderDialog(e) { + this.renameFolderID = globalThis.backendaiclient.supports( + 'vfolder-id-based', + ) + ? this._getControlID(e) + : this._getControlName(e); this.renameFolderName = this._getControlName(e); this.newFolderNameInput.value = ''; this.openDialog('modify-folder-name-dialog'); @@ -2427,7 +2439,11 @@ export default class BackendAiStorageList extends BackendAIPage { * @param {Event} e - click the share button * */ _shareFolderDialog(e) { - this.selectedFolder = this._getControlName(e); + this.selectedFolder = globalThis.backendaiclient.supports( + 'vfolder-id-based', + ) + ? this._getControlID(e) + : this._getControlName(e); this.selectedFolderType = this._getControlType(e); this._initializeSharingFolderDialogLayout(); this.openDialog('share-folder-dialog'); diff --git a/src/components/backend-ai-summary-view.ts b/src/components/backend-ai-summary-view.ts index 01ff36706..9a3402b13 100644 --- a/src/components/backend-ai-summary-view.ts +++ b/src/components/backend-ai-summary-view.ts @@ -429,9 +429,10 @@ export default class BackendAISummary extends BackendAIPage { } try { await globalThis.backendaiclient.vfolder.accept_invitation(invitation.id); - const vfolderInfo = await globalThis.backendaiclient.vfolder.info( - invitation.vfolder_name, - ); + const folder = globalThis.backendaiclient.supports('vfolder-id-based') + ? invitation.vfolder_id + : invitation.vfolder_name; + const vfolderInfo = await globalThis.backendaiclient.vfolder.info(folder); const tabName = BackendAISummary.getVFolderTabByVFolderInfo(vfolderInfo); this.notification.url = `/data?tab=${tabName}&folder=${invitation.vfolder_id.replace('-', '')}`; this.notification.text = diff --git a/src/lib/backend.ai-client-esm.ts b/src/lib/backend.ai-client-esm.ts index cc862b4de..0418f6fce 100644 --- a/src/lib/backend.ai-client-esm.ts +++ b/src/lib/backend.ai-client-esm.ts @@ -734,6 +734,9 @@ class Client { this._features['max_network_count'] = true; this._features['replicas'] = true; } + if (this.isManagerVersionCompatibleWith(['25.1.0', '24.09.6', '24.03.12'])) { + this._features['vfolder-id-based'] = true; + } } /** @@ -2079,6 +2082,7 @@ class ResourcePreset { class VFolder { public client: any; public name: any; + public id: any; public urlPrefix: any; /** @@ -2087,9 +2091,10 @@ class VFolder { * @param {Client} client - the Client API wrapper object to bind * @param {string} name - Virtual folder name. */ - constructor(client, name = null) { + constructor(client, name = null, id = null) { this.client = client; this.name = name; + this.id = id; this.urlPrefix = '/folders'; } @@ -2259,9 +2264,10 @@ class VFolder { */ async rename(new_name = null): Promise { const body = { new_name }; + const vfolder = this.client.supports('vfolder-id-based') ? this.id : this.name; let rqst = this.client.newSignedRequest( 'POST', - `${this.urlPrefix}/${this.name}/rename`, + `${this.urlPrefix}/${vfolder}/rename`, body, ); return this.client._wrapWithPromise(rqst); diff --git a/src/lib/pep440.test.ts b/src/lib/pep440.test.ts index cbf9774d1..f64b5971a 100644 --- a/src/lib/pep440.test.ts +++ b/src/lib/pep440.test.ts @@ -80,5 +80,10 @@ describe('isCompatibleMultipleConditions', ()=>{ expect(isCompatibleMultipleConditions('23.03.3', ['24.3.1', '23.03.4'])).toBe(false) expect(isCompatibleMultipleConditions('25.03.3', ['24.3.1', '23.03.4'])).toBe(true) expect(isCompatibleMultipleConditions('22.03.3', ['24.3.1', '23.03.4'])).toBe(false) + expect(isCompatibleMultipleConditions('24.12.1', ['25.1.0', '24.09.6', '24.03.12'])).toBe(false) + expect(isCompatibleMultipleConditions('25.1.1', ['25.1.0', '24.09.6', '24.03.12'])).toBe(true) + expect(isCompatibleMultipleConditions('25.0.0', ['25.1.0', '24.09.6', '24.03.12'])).toBe(false) + expect(isCompatibleMultipleConditions('24.09.5', ['25.1.0', '24.09.6', '24.03.12'])).toBe(false) + expect(isCompatibleMultipleConditions('24.09.7', ['25.1.0', '24.09.6', '24.03.12'])).toBe(true) }); }) \ No newline at end of file