Skip to content

Commit 4733707

Browse files
authored
Add self-hosted url info in Studio setup section (#5089)
1 parent 735743b commit 4733707

File tree

22 files changed

+349
-116
lines changed

22 files changed

+349
-116
lines changed

extension/src/setup/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ export class Setup
437437
pythonBinPath: getBinDisplayText(pythonBinPath),
438438
remoteList,
439439
sectionCollapsed: collectSectionCollapsed(this.focusedSection),
440+
selfHostedStudioUrl: this.studio.getSelfHostedStudioUrl(),
440441
shareLiveToStudio: !!this.studio.getShareLiveToStudio()
441442
})
442443
this.focusedSection = undefined

extension/src/setup/studio.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export class Studio extends Disposable {
5858
return this.studioUrl
5959
}
6060

61+
public getSelfHostedStudioUrl() {
62+
const url = this.getStudioUrl()
63+
return url === DEFAULT_STUDIO_URL ? null : url
64+
}
65+
6166
public removeStudioAccessToken(dvcRoots: string[]) {
6267
return this.removeKeyFromConfig(dvcRoots, ConfigKey.STUDIO_TOKEN)
6368
}

extension/src/setup/webview/contract.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type SetupData = {
2222
pythonBinPath: string | undefined
2323
remoteList: RemoteList
2424
sectionCollapsed: typeof DEFAULT_SECTION_COLLAPSED | undefined
25+
selfHostedStudioUrl: string | null
2526
shareLiveToStudio: boolean
2627
isAboveLatestTestedVersion: boolean | undefined
2728
}

extension/src/setup/webview/messages.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,14 @@ export class WebviewMessages {
7070
return commands.executeCommand(
7171
RegisteredCommands.ADD_STUDIO_ACCESS_TOKEN
7272
)
73+
case MessageFromWebviewType.SAVE_STUDIO_URL:
74+
return commands.executeCommand(RegisteredCommands.UPDATE_STUDIO_URL)
7375
case MessageFromWebviewType.REMOVE_STUDIO_TOKEN:
7476
return commands.executeCommand(
7577
RegisteredCommands.REMOVE_STUDIO_ACCESS_TOKEN
7678
)
79+
case MessageFromWebviewType.REMOVE_STUDIO_URL:
80+
return commands.executeCommand(RegisteredCommands.REMOVE_STUDIO_URL)
7781
case MessageFromWebviewType.SET_STUDIO_SHARE_EXPERIMENTS_LIVE:
7882
return this.updateStudioOffline(message.payload)
7983
case MessageFromWebviewType.REQUEST_STUDIO_TOKEN:

extension/src/test/suite/setup/index.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ suite('Setup Test Suite', () => {
395395
pythonBinPath: undefined,
396396
remoteList: undefined,
397397
sectionCollapsed: undefined,
398+
selfHostedStudioUrl: null,
398399
shareLiveToStudio: false
399400
})
400401
}).timeout(WEBVIEW_TEST_TIMEOUT)
@@ -441,6 +442,7 @@ suite('Setup Test Suite', () => {
441442
pythonBinPath: undefined,
442443
remoteList: undefined,
443444
sectionCollapsed: undefined,
445+
selfHostedStudioUrl: null,
444446
shareLiveToStudio: true
445447
})
446448
}).timeout(WEBVIEW_TEST_TIMEOUT)
@@ -495,6 +497,7 @@ suite('Setup Test Suite', () => {
495497
pythonBinPath: undefined,
496498
remoteList: undefined,
497499
sectionCollapsed: undefined,
500+
selfHostedStudioUrl: null,
498501
shareLiveToStudio: true
499502
})
500503
}).timeout(WEBVIEW_TEST_TIMEOUT)
@@ -549,6 +552,7 @@ suite('Setup Test Suite', () => {
549552
pythonBinPath: undefined,
550553
remoteList: { [dvcDemoPath]: undefined },
551554
sectionCollapsed: undefined,
555+
selfHostedStudioUrl: null,
552556
shareLiveToStudio: true
553557
})
554558
}).timeout(WEBVIEW_TEST_TIMEOUT)
@@ -1332,6 +1336,68 @@ suite('Setup Test Suite', () => {
13321336
)
13331337
})
13341338

1339+
it("should handle a message from the webview to update the user's self hosted url", async () => {
1340+
const { setup, mockExecuteCommand } = buildSetup({
1341+
disposer: disposable
1342+
})
1343+
1344+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1345+
stub(Setup.prototype as any, 'getCliCompatible').returns(true)
1346+
1347+
const webview = await setup.showWebview()
1348+
await webview.isReady()
1349+
1350+
const mockMessageReceived = getMessageReceivedEmitter(webview)
1351+
1352+
const commandCalled = new Promise(resolve =>
1353+
mockExecuteCommand.callsFake(() => {
1354+
resolve(undefined)
1355+
return Promise.resolve(undefined)
1356+
})
1357+
)
1358+
1359+
mockMessageReceived.fire({
1360+
type: MessageFromWebviewType.SAVE_STUDIO_URL
1361+
})
1362+
1363+
await commandCalled
1364+
1365+
expect(mockExecuteCommand).to.be.calledWithExactly(
1366+
RegisteredCommands.UPDATE_STUDIO_URL
1367+
)
1368+
}).timeout(WEBVIEW_TEST_TIMEOUT)
1369+
1370+
it("should handle a message from the webview to remove the user's self hosted url", async () => {
1371+
const { setup, mockExecuteCommand } = buildSetup({
1372+
disposer: disposable
1373+
})
1374+
1375+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1376+
stub(Setup.prototype as any, 'getCliCompatible').returns(true)
1377+
1378+
const webview = await setup.showWebview()
1379+
await webview.isReady()
1380+
1381+
const commandCalled = new Promise(resolve =>
1382+
mockExecuteCommand.callsFake(() => {
1383+
resolve(undefined)
1384+
return Promise.resolve(undefined)
1385+
})
1386+
)
1387+
1388+
const mockMessageReceived = getMessageReceivedEmitter(webview)
1389+
1390+
mockMessageReceived.fire({
1391+
type: MessageFromWebviewType.SAVE_STUDIO_URL
1392+
})
1393+
1394+
await commandCalled
1395+
1396+
expect(mockExecuteCommand).to.be.calledWithExactly(
1397+
RegisteredCommands.UPDATE_STUDIO_URL
1398+
)
1399+
}).timeout(WEBVIEW_TEST_TIMEOUT)
1400+
13351401
it('should check if experiments and dvc are setup', async () => {
13361402
const { setup } = buildSetup({
13371403
disposer: disposable,

extension/src/webview/contract.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export enum MessageFromWebviewType {
4747
RESIZE_PLOTS = 'resize-plots',
4848
REQUEST_STUDIO_TOKEN = 'request-studio-token',
4949
SAVE_STUDIO_TOKEN = 'save-studio-token',
50+
SAVE_STUDIO_URL = 'save-studio-url',
5051
SET_COMPARISON_MULTI_PLOT_VALUE = 'update-comparison-multi-plot-value',
5152
SET_SMOOTH_PLOT_VALUE = 'update-smooth-plot-value',
5253
SHOW_EXPERIMENT_LOGS = 'show-experiment-logs',
@@ -71,6 +72,7 @@ export enum MessageFromWebviewType {
7172
REMOTE_REMOVE = 'remote-remove',
7273
REMOVE_CUSTOM_PLOTS = 'remove-custom-plots',
7374
REMOVE_STUDIO_TOKEN = 'remove-studio-token',
75+
REMOVE_STUDIO_URL = 'remove-studio-url',
7476
MODIFY_WORKSPACE_PARAMS_AND_QUEUE = 'modify-workspace-params-and-queue',
7577
MODIFY_WORKSPACE_PARAMS_AND_RUN = 'modify-workspace-params-and-run',
7678
MODIFY_WORKSPACE_PARAMS_RESET_AND_RUN = 'modify-workspace-params-reset-and-run',
@@ -224,6 +226,7 @@ export type MessageFromWebview =
224226
type: MessageFromWebviewType.REMOVE_CUSTOM_PLOTS
225227
}
226228
| { type: MessageFromWebviewType.REMOVE_STUDIO_TOKEN }
229+
| { type: MessageFromWebviewType.REMOVE_STUDIO_URL }
227230
| {
228231
type: MessageFromWebviewType.REORDER_PLOTS_COMPARISON
229232
payload: string[]
@@ -291,6 +294,7 @@ export type MessageFromWebview =
291294
| { type: MessageFromWebviewType.UPGRADE_DVC }
292295
| { type: MessageFromWebviewType.SETUP_WORKSPACE }
293296
| { type: MessageFromWebviewType.SAVE_STUDIO_TOKEN }
297+
| { type: MessageFromWebviewType.SAVE_STUDIO_URL }
294298
| { type: MessageFromWebviewType.REQUEST_STUDIO_TOKEN }
295299
| { type: MessageFromWebviewType.ADD_CONFIGURATION }
296300
| { type: MessageFromWebviewType.ZOOM_PLOT; payload?: string }

webview/src/setup/components/App.test.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const DEFAULT_DATA = {
4141
pythonBinPath: undefined,
4242
remoteList: undefined,
4343
sectionCollapsed: undefined,
44+
selfHostedStudioUrl: null,
4445
shareLiveToStudio: false
4546
}
4647

@@ -675,6 +676,65 @@ describe('App', () => {
675676
within(iconWrapper).getByTestId(TooltipIconType.WARNING)
676677
).toBeInTheDocument()
677678
})
679+
680+
it('should show the self hosted url with actions to change it if the user has set one', () => {
681+
const selfHostedUrl = 'https://studio.example.com'
682+
renderApp({ selfHostedStudioUrl: selfHostedUrl })
683+
684+
const urlDetails = screen.getByTestId('studio-url-details')
685+
686+
expect(
687+
within(urlDetails).getByText('Self-Hosted Url:')
688+
).toBeInTheDocument()
689+
expect(within(urlDetails).getByText(selfHostedUrl)).toBeInTheDocument()
690+
691+
const updateUrlBtn = within(urlDetails).getByText('Update')
692+
const removeUrlBtn = within(urlDetails).getByText('Remove')
693+
694+
expect(updateUrlBtn).toBeInTheDocument()
695+
expect(removeUrlBtn).toBeInTheDocument()
696+
697+
mockPostMessage.mockClear()
698+
fireEvent.click(updateUrlBtn)
699+
700+
expect(mockPostMessage).toHaveBeenCalledTimes(1)
701+
expect(mockPostMessage).toHaveBeenCalledWith({
702+
type: MessageFromWebviewType.SAVE_STUDIO_URL
703+
})
704+
705+
mockPostMessage.mockClear()
706+
fireEvent.click(removeUrlBtn)
707+
708+
expect(mockPostMessage).toHaveBeenCalledTimes(1)
709+
expect(mockPostMessage).toHaveBeenCalledWith({
710+
type: MessageFromWebviewType.REMOVE_STUDIO_URL
711+
})
712+
})
713+
714+
it('should show the self hosted url with "Not found" with an action to add one if the user has not set one', () => {
715+
renderApp()
716+
717+
const urlDetails = screen.getByTestId('studio-url-details')
718+
719+
expect(
720+
within(urlDetails).getByText('Self-Hosted Url:')
721+
).toBeInTheDocument()
722+
expect(within(urlDetails).getByText('Not found')).toBeInTheDocument()
723+
724+
const addUrlBtn = within(urlDetails).getByText('Add Url')
725+
726+
expect(addUrlBtn).toBeInTheDocument()
727+
728+
mockPostMessage.mockClear()
729+
fireEvent.click(addUrlBtn)
730+
731+
expect(mockPostMessage).toHaveBeenCalledTimes(1)
732+
expect(mockPostMessage).toHaveBeenCalledWith({
733+
type: MessageFromWebviewType.SAVE_STUDIO_URL
734+
})
735+
736+
mockPostMessage.mockClear()
737+
})
678738
})
679739

680740
describe('Studio connected', () => {
@@ -718,6 +778,24 @@ describe('App', () => {
718778
within(iconWrapper).getByTestId(TooltipIconType.PASSED)
719779
).toBeInTheDocument()
720780
})
781+
782+
it('should show the self hosted url with actions to change it if the user has set one', () => {
783+
const selfHostedUrl = 'https://studio.example.com'
784+
renderApp({ isStudioConnected: true, selfHostedStudioUrl: selfHostedUrl })
785+
786+
const urlDetails = screen.getByTestId('studio-url-details')
787+
788+
expect(
789+
within(urlDetails).getByText('Self-Hosted Url:')
790+
).toBeInTheDocument()
791+
expect(within(urlDetails).getByText(selfHostedUrl)).toBeInTheDocument()
792+
793+
const updateUrlBtn = within(urlDetails).getByText('Update')
794+
const removeUrlBtn = within(urlDetails).getByText('Remove')
795+
796+
expect(updateUrlBtn).toBeInTheDocument()
797+
expect(removeUrlBtn).toBeInTheDocument()
798+
})
721799
})
722800

723801
describe('focused section', () => {

webview/src/setup/components/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
import { updateRemoteList } from '../state/remoteSlice'
3434
import {
3535
updateIsStudioConnected,
36+
updateSelfHostedStudioUrl,
3637
updateShareLiveToStudio
3738
} from '../state/studioSlice'
3839
import { setStudioShareExperimentsLive } from '../util/messages'
@@ -119,6 +120,9 @@ export const feedStore = (
119120
case 'shareLiveToStudio':
120121
dispatch(updateShareLiveToStudio(data.data.shareLiveToStudio))
121122
continue
123+
case 'selfHostedStudioUrl':
124+
dispatch(updateSelfHostedStudioUrl(data.data.selfHostedStudioUrl))
125+
continue
122126
case 'remoteList':
123127
dispatch(updateRemoteList(data.data.remoteList))
124128
continue

webview/src/setup/components/dvc/DvcEnvCommandRow.tsx

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)